@rotorsoft/act 1.32.26 → 1.32.27
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 +2 -3
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/act.d.ts +5 -151
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/adapters/in-memory-store.d.ts +0 -21
- package/dist/@types/adapters/in-memory-store.d.ts.map +1 -1
- package/dist/@types/builders/act-builder.d.ts +0 -25
- package/dist/@types/builders/act-builder.d.ts.map +1 -1
- package/dist/@types/builders/build-classify.d.ts +4 -4
- package/dist/@types/builders/build-classify.d.ts.map +1 -1
- package/dist/@types/builders/projection-builder.d.ts.map +1 -1
- package/dist/@types/builders/slice-builder.d.ts +1 -12
- package/dist/@types/builders/slice-builder.d.ts.map +1 -1
- package/dist/@types/builders/state-builder.d.ts +0 -189
- package/dist/@types/builders/state-builder.d.ts.map +1 -1
- package/dist/@types/config.d.ts +0 -27
- package/dist/@types/config.d.ts.map +1 -1
- package/dist/@types/internal/close-cycle.d.ts +2 -1
- package/dist/@types/internal/close-cycle.d.ts.map +1 -1
- package/dist/@types/ports.d.ts +0 -7
- package/dist/@types/ports.d.ts.map +1 -1
- package/dist/@types/test/sandbox.d.ts +0 -14
- package/dist/@types/test/sandbox.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +0 -27
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/@types/types/errors.d.ts +1 -92
- package/dist/@types/types/errors.d.ts.map +1 -1
- package/dist/@types/types/ports.d.ts +0 -70
- package/dist/@types/types/ports.d.ts.map +1 -1
- package/dist/@types/types/reaction.d.ts +0 -33
- package/dist/@types/types/reaction.d.ts.map +1 -1
- package/dist/{chunk-QXW3JV2H.js → chunk-5FXT63KF.js} +2 -2
- package/dist/chunk-5FXT63KF.js.map +1 -0
- package/dist/{chunk-XSG2NW4J.js → chunk-F7K7SIG6.js} +1 -1
- package/dist/chunk-F7K7SIG6.js.map +1 -0
- package/dist/index.cjs +22 -159
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +24 -161
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js +2 -2
- package/dist/test/index.js.map +1 -1
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.js +1 -1
- package/package.json +1 -4
- package/dist/chunk-QXW3JV2H.js.map +0 -1
- package/dist/chunk-XSG2NW4J.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1406,10 +1406,8 @@ var ALL_LANES = /* @__PURE__ */ Symbol("act-1103/all-lanes");
|
|
|
1406
1406
|
function classify_registry(registry, states) {
|
|
1407
1407
|
const statics = /* @__PURE__ */ new Map();
|
|
1408
1408
|
const target_lanes = /* @__PURE__ */ new Map();
|
|
1409
|
-
const reactive_events = /* @__PURE__ */ new Set();
|
|
1410
1409
|
const event_to_lanes = /* @__PURE__ */ new Map();
|
|
1411
1410
|
for (const [name, register] of Object.entries(registry.events)) {
|
|
1412
|
-
if (register.reactions.size > 0) reactive_events.add(name);
|
|
1413
1411
|
for (const reaction of register.reactions.values()) {
|
|
1414
1412
|
if (typeof reaction.resolver === "function") {
|
|
1415
1413
|
event_to_lanes.set(name, ALL_LANES);
|
|
@@ -1447,7 +1445,6 @@ function classify_registry(registry, states) {
|
|
|
1447
1445
|
}
|
|
1448
1446
|
return {
|
|
1449
1447
|
static_targets: [...statics.values()],
|
|
1450
|
-
reactive_events,
|
|
1451
1448
|
event_to_state,
|
|
1452
1449
|
event_to_lanes
|
|
1453
1450
|
};
|
|
@@ -2604,7 +2601,7 @@ async function run_full_closes(full, target_map, deps, skipped) {
|
|
|
2604
2601
|
}
|
|
2605
2602
|
const safe = await partition_by_safety(
|
|
2606
2603
|
stream_info,
|
|
2607
|
-
deps.
|
|
2604
|
+
deps.reactive,
|
|
2608
2605
|
skipped,
|
|
2609
2606
|
deps.probe_page_size ?? SAFETY_PROBE_PAGE_SIZE,
|
|
2610
2607
|
deps.catch_up_correlation
|
|
@@ -2641,8 +2638,8 @@ async function run_full_closes(full, target_map, deps, skipped) {
|
|
|
2641
2638
|
);
|
|
2642
2639
|
}
|
|
2643
2640
|
async function run_windowed_closes(windowed, deps, skipped) {
|
|
2644
|
-
const checkpoint = deps.
|
|
2645
|
-
const min_at = deps.
|
|
2641
|
+
const checkpoint = deps.reactive ? await deps.catch_up_correlation(Number.MAX_SAFE_INTEGER) : -1;
|
|
2642
|
+
const min_at = deps.reactive ? await probe_min_watermarks(
|
|
2646
2643
|
windowed.map((t) => t.stream),
|
|
2647
2644
|
deps.probe_page_size ?? SAFETY_PROBE_PAGE_SIZE,
|
|
2648
2645
|
checkpoint
|
|
@@ -2730,8 +2727,8 @@ async function scan_stream_heads(streams) {
|
|
|
2730
2727
|
}
|
|
2731
2728
|
return out;
|
|
2732
2729
|
}
|
|
2733
|
-
async function partition_by_safety(stream_info,
|
|
2734
|
-
if (
|
|
2730
|
+
async function partition_by_safety(stream_info, reactive, skipped, page_size, catch_up_correlation2) {
|
|
2731
|
+
if (!reactive) return [...stream_info.keys()];
|
|
2735
2732
|
let needed = -1;
|
|
2736
2733
|
for (const info of stream_info.values())
|
|
2737
2734
|
needed = Math.max(needed, info.max_id);
|
|
@@ -4589,7 +4586,8 @@ var Act = class {
|
|
|
4589
4586
|
_breaker;
|
|
4590
4587
|
_listen;
|
|
4591
4588
|
_drain;
|
|
4592
|
-
|
|
4589
|
+
/** Whether any event has a reaction; nothing arms without one. */
|
|
4590
|
+
_reactive;
|
|
4593
4591
|
_drain_controllers;
|
|
4594
4592
|
_correlate;
|
|
4595
4593
|
_settle;
|
|
@@ -4719,7 +4717,7 @@ var Act = class {
|
|
|
4719
4717
|
});
|
|
4720
4718
|
this._handle_batch = build_handle_batch(this._logger);
|
|
4721
4719
|
const classification = classify_registry(this.registry, this._states);
|
|
4722
|
-
this.
|
|
4720
|
+
this._reactive = classification.event_to_lanes.size > 0;
|
|
4723
4721
|
this._event_to_state = classification.event_to_state;
|
|
4724
4722
|
this._event_to_lanes = classification.event_to_lanes;
|
|
4725
4723
|
this._listen = options.listen !== false;
|
|
@@ -4826,9 +4824,8 @@ var Act = class {
|
|
|
4826
4824
|
declared_lanes: new Set(this._declared_lanes),
|
|
4827
4825
|
// Built after the controllers, so their keys are the active lanes.
|
|
4828
4826
|
shard: this._drain_controllers.size < this._declared_lanes.length ? [...this._drain_controllers.keys()] : void 0,
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
},
|
|
4827
|
+
// Only reached on a draining app: writer-only ones never correlate.
|
|
4828
|
+
on_init: () => this._arm_all(),
|
|
4832
4829
|
on_init_async: this._drain ? () => this._seed_persisted_defers() : void 0
|
|
4833
4830
|
});
|
|
4834
4831
|
}
|
|
@@ -4925,7 +4922,7 @@ var Act = class {
|
|
|
4925
4922
|
}
|
|
4926
4923
|
/** Failures are logged, never thrown: `notify` is a hint. */
|
|
4927
4924
|
async _wire_notify(s) {
|
|
4928
|
-
if (this.
|
|
4925
|
+
if (!this._reactive) return void 0;
|
|
4929
4926
|
if (!s.notify) return void 0;
|
|
4930
4927
|
if (!this._listen) return void 0;
|
|
4931
4928
|
try {
|
|
@@ -4985,46 +4982,6 @@ var Act = class {
|
|
|
4985
4982
|
* console.log(snapshots[0].state.count); // Current count after increment
|
|
4986
4983
|
* ```
|
|
4987
4984
|
*
|
|
4988
|
-
* @example With error handling
|
|
4989
|
-
* ```typescript
|
|
4990
|
-
* try {
|
|
4991
|
-
* await app.do(
|
|
4992
|
-
* "withdraw",
|
|
4993
|
-
* { stream: "account-123", actor: { id: "user1", name: "Alice" } },
|
|
4994
|
-
* { amount: 1000 }
|
|
4995
|
-
* );
|
|
4996
|
-
* } catch (error) {
|
|
4997
|
-
* if (error instanceof InvariantError) {
|
|
4998
|
-
* console.error("Business rule violated:", error.description);
|
|
4999
|
-
* } else if (error instanceof ConcurrencyError) {
|
|
5000
|
-
* console.error("Concurrent modification detected, retry...");
|
|
5001
|
-
* } else if (error instanceof ValidationError) {
|
|
5002
|
-
* console.error("Invalid payload:", error.details);
|
|
5003
|
-
* }
|
|
5004
|
-
* }
|
|
5005
|
-
* ```
|
|
5006
|
-
*
|
|
5007
|
-
* @example Reaction triggering another action (reactingTo auto-injected)
|
|
5008
|
-
* ```typescript
|
|
5009
|
-
* const app = act()
|
|
5010
|
-
* .withState(Order)
|
|
5011
|
-
* .withState(Inventory)
|
|
5012
|
-
* .on("OrderPlaced")
|
|
5013
|
-
* .do(async function reduceInventory(event, _stream, app) {
|
|
5014
|
-
* // Inside reaction handlers, reactingTo is auto-injected when omitted.
|
|
5015
|
-
* // The triggering event is used by default, maintaining the correlation chain.
|
|
5016
|
-
* await app.do(
|
|
5017
|
-
* "reduceStock",
|
|
5018
|
-
* { stream: "inventory-1", actor: { id: "sys", name: "system" } },
|
|
5019
|
-
* { amount: event.data.items.length }
|
|
5020
|
-
* );
|
|
5021
|
-
* // To use a different correlation, pass reactingTo explicitly:
|
|
5022
|
-
* // await app.do("reduceStock", target, payload, { reactingTo: customEvent });
|
|
5023
|
-
* })
|
|
5024
|
-
* .to("inventory-1")
|
|
5025
|
-
* .build();
|
|
5026
|
-
* ```
|
|
5027
|
-
*
|
|
5028
4985
|
* @see {@link Target} for target structure
|
|
5029
4986
|
* @see {@link Snapshot} for return value structure
|
|
5030
4987
|
* @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
|
|
@@ -5040,10 +4997,9 @@ var Act = class {
|
|
|
5040
4997
|
payload,
|
|
5041
4998
|
do_options
|
|
5042
4999
|
);
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
);
|
|
5000
|
+
this._arm_for_event_names(
|
|
5001
|
+
snapshots.map((s) => s.event.name)
|
|
5002
|
+
);
|
|
5047
5003
|
this.emit("committed", snapshots);
|
|
5048
5004
|
return snapshots;
|
|
5049
5005
|
});
|
|
@@ -5090,26 +5046,6 @@ var Act = class {
|
|
|
5090
5046
|
* console.log(`Found ${count} events from ${first?.id} to ${last?.id}`);
|
|
5091
5047
|
* ```
|
|
5092
5048
|
*
|
|
5093
|
-
* @example Query specific event types
|
|
5094
|
-
* ```typescript
|
|
5095
|
-
* const { count } = await app.query(
|
|
5096
|
-
* { name: "UserCreated", limit: 100 },
|
|
5097
|
-
* (event) => {
|
|
5098
|
-
* console.log("User created:", event.data.email);
|
|
5099
|
-
* }
|
|
5100
|
-
* );
|
|
5101
|
-
* ```
|
|
5102
|
-
*
|
|
5103
|
-
* @example Query events in time range
|
|
5104
|
-
* ```typescript
|
|
5105
|
-
* const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
|
5106
|
-
* const { count } = await app.query({
|
|
5107
|
-
* created_after: yesterday,
|
|
5108
|
-
* stream: "user-123"
|
|
5109
|
-
* });
|
|
5110
|
-
* console.log(`User had ${count} events in last 24 hours`);
|
|
5111
|
-
* ```
|
|
5112
|
-
*
|
|
5113
5049
|
* @see {@link query_array} for loading events into memory
|
|
5114
5050
|
*/
|
|
5115
5051
|
async query(query, callback) {
|
|
@@ -5141,14 +5077,6 @@ var Act = class {
|
|
|
5141
5077
|
* events.forEach(event => console.log(event.name, event.data));
|
|
5142
5078
|
* ```
|
|
5143
5079
|
*
|
|
5144
|
-
* @example Get recent events
|
|
5145
|
-
* ```typescript
|
|
5146
|
-
* const recent = await app.query_array({
|
|
5147
|
-
* stream: "user-123",
|
|
5148
|
-
* limit: 10
|
|
5149
|
-
* });
|
|
5150
|
-
* ```
|
|
5151
|
-
*
|
|
5152
5080
|
* @see {@link query} for large result sets
|
|
5153
5081
|
*/
|
|
5154
5082
|
async query_array(query) {
|
|
@@ -5219,12 +5147,6 @@ var Act = class {
|
|
|
5219
5147
|
* await app.drain();
|
|
5220
5148
|
* ```
|
|
5221
5149
|
*
|
|
5222
|
-
* @example In production, prefer settle()
|
|
5223
|
-
* ```typescript
|
|
5224
|
-
* await app.do("CreateItem", target, input);
|
|
5225
|
-
* app.settle(); // debounced correlate→drain, emits "settled"
|
|
5226
|
-
* ```
|
|
5227
|
-
*
|
|
5228
5150
|
* @see {@link settle} for debounced correlate→drain with lifecycle events
|
|
5229
5151
|
* @see {@link correlate} for dynamic stream discovery
|
|
5230
5152
|
* @see {@link start_correlations} for automatic correlation
|
|
@@ -5236,6 +5158,7 @@ var Act = class {
|
|
|
5236
5158
|
return this._scoped(() => this._drain_all(options));
|
|
5237
5159
|
}
|
|
5238
5160
|
_arm_all() {
|
|
5161
|
+
if (!this._reactive) return;
|
|
5239
5162
|
this._correlate.arm();
|
|
5240
5163
|
for (const c of this._drain_controllers.values()) c.arm();
|
|
5241
5164
|
}
|
|
@@ -5291,37 +5214,20 @@ var Act = class {
|
|
|
5291
5214
|
* on reaction resolvers. It then registers these streams so they'll be picked up by
|
|
5292
5215
|
* the next drain cycle.
|
|
5293
5216
|
*
|
|
5294
|
-
* @param query - Query filter to scan for new correlations
|
|
5295
5217
|
* @param query - Scan filter — see {@link Query} for fields (typically
|
|
5296
5218
|
* `{ after: <event-id>, limit: <count> }`)
|
|
5297
|
-
* @returns
|
|
5219
|
+
* @returns The number of newly subscribed streams and the last scanned event id
|
|
5298
5220
|
*
|
|
5299
5221
|
* @example Manual correlation
|
|
5300
5222
|
* ```typescript
|
|
5301
5223
|
* // Scan for new streams
|
|
5302
|
-
* const {
|
|
5303
|
-
* console.log(`
|
|
5224
|
+
* const { subscribed, last_id } = await app.correlate({ after: 0, limit: 100 });
|
|
5225
|
+
* console.log(`Subscribed ${subscribed} new streams`);
|
|
5304
5226
|
*
|
|
5305
5227
|
* // Save last_id for next scan
|
|
5306
5228
|
* await saveCheckpoint(last_id);
|
|
5307
5229
|
* ```
|
|
5308
5230
|
*
|
|
5309
|
-
* @example Dynamic stream creation
|
|
5310
|
-
* ```typescript
|
|
5311
|
-
* const app = act()
|
|
5312
|
-
* .withState(User)
|
|
5313
|
-
* .withState(UserStats)
|
|
5314
|
-
* .on("UserLoggedIn")
|
|
5315
|
-
* .do(async (event) => ["incrementLoginCount", {}])
|
|
5316
|
-
* .to((event) => ({
|
|
5317
|
-
* target: `stats-${event.stream}` // Dynamic target per user
|
|
5318
|
-
* }))
|
|
5319
|
-
* .build();
|
|
5320
|
-
*
|
|
5321
|
-
* // Discover stats streams as users log in
|
|
5322
|
-
* await app.correlate();
|
|
5323
|
-
* ```
|
|
5324
|
-
*
|
|
5325
5231
|
* @see {@link start_correlations} for automatic periodic correlation
|
|
5326
5232
|
* @see {@link stop_correlations} to stop automatic correlation
|
|
5327
5233
|
*/
|
|
@@ -5334,8 +5240,7 @@ var Act = class {
|
|
|
5334
5240
|
if (!this._drain) return { subscribed: 0, last_id: -1, scanned: false };
|
|
5335
5241
|
return this._scoped(async () => {
|
|
5336
5242
|
const { subscribed, last_id, marked, scanned } = await this._correlate.correlate(query, lease);
|
|
5337
|
-
if (
|
|
5338
|
-
this._arm_all();
|
|
5243
|
+
if (subscribed > 0 || marked > 0) this._arm_all();
|
|
5339
5244
|
if (subscribed > 0) this._on_discovered?.(subscribed);
|
|
5340
5245
|
return { subscribed, last_id, scanned };
|
|
5341
5246
|
});
|
|
@@ -5427,12 +5332,6 @@ var Act = class {
|
|
|
5427
5332
|
* app.settle({ eventLimit: 1000 }); // emits "settled" when fully replayed
|
|
5428
5333
|
* ```
|
|
5429
5334
|
*
|
|
5430
|
-
* @example Rebuild a projection (tests / scripts)
|
|
5431
|
-
* ```typescript
|
|
5432
|
-
* await app.reset(["my-projection"]);
|
|
5433
|
-
* await app.drain({ eventLimit: 1000 }); // small streams: one pass is enough
|
|
5434
|
-
* ```
|
|
5435
|
-
*
|
|
5436
5335
|
* @see {@link Store.reset} for the underlying store primitive
|
|
5437
5336
|
* @see {@link settle} for the debounced full-catch-up loop
|
|
5438
5337
|
*/
|
|
@@ -5441,7 +5340,7 @@ var Act = class {
|
|
|
5441
5340
|
const count = await store2().reset(input);
|
|
5442
5341
|
for (const handler of this._batch_handlers.values())
|
|
5443
5342
|
handler[FOLD_RESET]?.();
|
|
5444
|
-
if (count > 0
|
|
5343
|
+
if (count > 0) this._arm_all();
|
|
5445
5344
|
return count;
|
|
5446
5345
|
});
|
|
5447
5346
|
}
|
|
@@ -5475,7 +5374,7 @@ var Act = class {
|
|
|
5475
5374
|
async unblock(input) {
|
|
5476
5375
|
return this._scoped(async () => {
|
|
5477
5376
|
const count = await store2().unblock(input);
|
|
5478
|
-
if (count > 0
|
|
5377
|
+
if (count > 0) this._arm_all();
|
|
5479
5378
|
return count;
|
|
5480
5379
|
});
|
|
5481
5380
|
}
|
|
@@ -5511,11 +5410,6 @@ var Act = class {
|
|
|
5511
5410
|
* await cache().clear(); // operator's responsibility
|
|
5512
5411
|
* ```
|
|
5513
5412
|
*
|
|
5514
|
-
* @example Copy one store into another
|
|
5515
|
-
* ```typescript
|
|
5516
|
-
* await app.restore(pg, {}, sqlite);
|
|
5517
|
-
* ```
|
|
5518
|
-
*
|
|
5519
5413
|
* @see {@link Store.restore} for the underlying driver-pattern primitive.
|
|
5520
5414
|
*/
|
|
5521
5415
|
async restore(source, opts = {}, sink) {
|
|
@@ -5654,16 +5548,6 @@ var Act = class {
|
|
|
5654
5548
|
* await app.prioritize({ stream: "^proj-orders$", stream_exact: false }, 10);
|
|
5655
5549
|
* ```
|
|
5656
5550
|
*
|
|
5657
|
-
* @example Drop all audit projections to background
|
|
5658
|
-
* ```typescript
|
|
5659
|
-
* await app.prioritize({ source: "^audit-" }, -5);
|
|
5660
|
-
* ```
|
|
5661
|
-
*
|
|
5662
|
-
* @example Reset everyone to default
|
|
5663
|
-
* ```typescript
|
|
5664
|
-
* await app.prioritize({}, 0);
|
|
5665
|
-
* ```
|
|
5666
|
-
*
|
|
5667
5551
|
* @see {@link Store.prioritize} for the underlying primitive
|
|
5668
5552
|
* @see {@link claim} for how priority biases scheduling
|
|
5669
5553
|
*/
|
|
@@ -5674,7 +5558,7 @@ var Act = class {
|
|
|
5674
5558
|
async _run_close(targets) {
|
|
5675
5559
|
const close_actor = { id: "$close", name: "close" };
|
|
5676
5560
|
const result = await run_close_cycle(targets, {
|
|
5677
|
-
|
|
5561
|
+
reactive: this._reactive,
|
|
5678
5562
|
catch_up_correlation: (until) => catch_up_correlation(
|
|
5679
5563
|
this._correlate,
|
|
5680
5564
|
(limit) => this.correlate({ limit }),
|
|
@@ -5727,27 +5611,6 @@ var Act = class {
|
|
|
5727
5611
|
* { stream: "order-456" },
|
|
5728
5612
|
* ]);
|
|
5729
5613
|
* ```
|
|
5730
|
-
*
|
|
5731
|
-
* @example Close with restart (state loaded automatically after guard)
|
|
5732
|
-
* ```typescript
|
|
5733
|
-
* await app.close([
|
|
5734
|
-
* { stream: "counter-1", restart: true },
|
|
5735
|
-
* { stream: "counter-2" }, // tombstoned
|
|
5736
|
-
* ]);
|
|
5737
|
-
* ```
|
|
5738
|
-
*
|
|
5739
|
-
* @example Windowed close — keep the last 180 days of real events
|
|
5740
|
-
* ```typescript
|
|
5741
|
-
* const cutoff = new Date();
|
|
5742
|
-
* cutoff.setDate(cutoff.getDate() - 180);
|
|
5743
|
-
* await app.close([
|
|
5744
|
-
* {
|
|
5745
|
-
* stream: "ledger-acme",
|
|
5746
|
-
* before: cutoff,
|
|
5747
|
-
* archive: async () => { await archiveToS3("ledger-acme", cutoff); },
|
|
5748
|
-
* },
|
|
5749
|
-
* ]);
|
|
5750
|
-
* ```
|
|
5751
5614
|
*/
|
|
5752
5615
|
async close(targets) {
|
|
5753
5616
|
if (!targets.length) return { truncated: /* @__PURE__ */ new Map(), skipped: [] };
|