@rotorsoft/act 1.18.7 → 1.19.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/.tsbuildinfo +1 -1
- package/dist/@types/builders/projection-builder.d.ts +35 -5
- package/dist/@types/builders/projection-builder.d.ts.map +1 -1
- package/dist/@types/internal/event-sourcing.d.ts +3 -2
- package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
- package/dist/@types/internal/index.d.ts +1 -0
- package/dist/@types/internal/index.d.ts.map +1 -1
- package/dist/@types/internal/state-fold.d.ts +18 -0
- package/dist/@types/internal/state-fold.d.ts.map +1 -0
- package/dist/@types/types/ports.d.ts +5 -0
- package/dist/@types/types/ports.d.ts.map +1 -1
- package/dist/@types/types/reaction.d.ts +12 -0
- package/dist/@types/types/reaction.d.ts.map +1 -1
- package/dist/index.cjs +124 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +124 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1242,6 +1242,7 @@ async function truncate_and_warm_cache(guarded, seed_states, guard_events, corre
|
|
|
1242
1242
|
const state2 = seed_states.get(stream);
|
|
1243
1243
|
if (state2 && entry) {
|
|
1244
1244
|
await cache().set(stream, {
|
|
1245
|
+
stream,
|
|
1245
1246
|
state: state2,
|
|
1246
1247
|
version: entry.committed.version,
|
|
1247
1248
|
event_id: entry.committed.id,
|
|
@@ -1746,6 +1747,7 @@ async function load(me, target, callback) {
|
|
|
1746
1747
|
);
|
|
1747
1748
|
if (replayed > 0 && !time_travel && event && !me.pii_aware) {
|
|
1748
1749
|
await cache().set(stream, {
|
|
1750
|
+
stream,
|
|
1749
1751
|
state: state2,
|
|
1750
1752
|
version: event.version,
|
|
1751
1753
|
event_id: event.id,
|
|
@@ -1861,6 +1863,7 @@ async function action(me, action2, target, payload, options) {
|
|
|
1861
1863
|
const snapped = me.snap?.(last);
|
|
1862
1864
|
if (!me.pii_aware) {
|
|
1863
1865
|
cache().set(stream, {
|
|
1866
|
+
stream,
|
|
1864
1867
|
state: last.state,
|
|
1865
1868
|
version: last.event.version,
|
|
1866
1869
|
event_id: last.event.id,
|
|
@@ -2420,6 +2423,89 @@ var SettleLoop = class {
|
|
|
2420
2423
|
}
|
|
2421
2424
|
};
|
|
2422
2425
|
|
|
2426
|
+
// src/internal/state-fold.ts
|
|
2427
|
+
import { patch as patch2 } from "@rotorsoft/act-patch";
|
|
2428
|
+
import { z as z5 } from "zod";
|
|
2429
|
+
var DEFAULT_FOLD_FLUSH_EVERY = 1e3;
|
|
2430
|
+
var DEFAULT_MAX_CACHED_STATES = 1e4;
|
|
2431
|
+
var FoldOptionsSchema = z5.object({
|
|
2432
|
+
flushEvery: z5.number().int().min(1).default(DEFAULT_FOLD_FLUSH_EVERY),
|
|
2433
|
+
maxCachedStates: z5.number().int().min(1).default(DEFAULT_MAX_CACHED_STATES)
|
|
2434
|
+
});
|
|
2435
|
+
function resolveFoldConfig(options) {
|
|
2436
|
+
return FoldOptionsSchema.parse(options);
|
|
2437
|
+
}
|
|
2438
|
+
function make_fold_handler(me, flush, config2) {
|
|
2439
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
2440
|
+
const row = ({ dirty: _, ...row2 }) => row2;
|
|
2441
|
+
const flush_dirty = async () => {
|
|
2442
|
+
const rows = [];
|
|
2443
|
+
const flushed = [];
|
|
2444
|
+
for (const f of cache2.values())
|
|
2445
|
+
if (f.dirty) {
|
|
2446
|
+
rows.push(row(f));
|
|
2447
|
+
flushed.push(f);
|
|
2448
|
+
}
|
|
2449
|
+
if (rows.length === 0) return;
|
|
2450
|
+
await flush(rows);
|
|
2451
|
+
for (const f of flushed) f.dirty = false;
|
|
2452
|
+
};
|
|
2453
|
+
return async (events) => {
|
|
2454
|
+
let folded_since_flush = 0;
|
|
2455
|
+
for (const event of events) {
|
|
2456
|
+
const stream = event.stream;
|
|
2457
|
+
let fold = cache2.get(stream);
|
|
2458
|
+
if (fold) {
|
|
2459
|
+
cache2.delete(stream);
|
|
2460
|
+
cache2.set(stream, fold);
|
|
2461
|
+
} else {
|
|
2462
|
+
if (cache2.size >= config2.maxCachedStates) {
|
|
2463
|
+
const oldest = cache2.keys().next().value;
|
|
2464
|
+
const evictee = cache2.get(oldest);
|
|
2465
|
+
if (evictee.dirty) await flush([row(evictee)]);
|
|
2466
|
+
cache2.delete(oldest);
|
|
2467
|
+
}
|
|
2468
|
+
const snapshot = await load(me, { stream });
|
|
2469
|
+
const entry = await cache().get(stream);
|
|
2470
|
+
const frontier = entry ?? {
|
|
2471
|
+
version: snapshot.event.version,
|
|
2472
|
+
event_id: snapshot.event.id,
|
|
2473
|
+
patches: snapshot.patches,
|
|
2474
|
+
snaps: snapshot.snaps
|
|
2475
|
+
};
|
|
2476
|
+
fold = {
|
|
2477
|
+
stream,
|
|
2478
|
+
state: snapshot.state,
|
|
2479
|
+
version: frontier.version,
|
|
2480
|
+
event_id: frontier.event_id,
|
|
2481
|
+
patches: frontier.patches,
|
|
2482
|
+
snaps: frontier.snaps,
|
|
2483
|
+
dirty: true
|
|
2484
|
+
};
|
|
2485
|
+
cache2.set(stream, fold);
|
|
2486
|
+
}
|
|
2487
|
+
if (event.id > fold.event_id) {
|
|
2488
|
+
const reducer = me.patch[event.name];
|
|
2489
|
+
fold.state = patch2(
|
|
2490
|
+
fold.state,
|
|
2491
|
+
reducer(event, fold.state)
|
|
2492
|
+
);
|
|
2493
|
+
fold.version = event.version;
|
|
2494
|
+
fold.event_id = event.id;
|
|
2495
|
+
fold.patches++;
|
|
2496
|
+
fold.dirty = true;
|
|
2497
|
+
} else {
|
|
2498
|
+
fold.dirty = true;
|
|
2499
|
+
}
|
|
2500
|
+
if (++folded_since_flush >= config2.flushEvery) {
|
|
2501
|
+
await flush_dirty();
|
|
2502
|
+
folded_since_flush = 0;
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
await flush_dirty();
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2423
2509
|
// src/act.ts
|
|
2424
2510
|
var DEFAULT_MAX_SUBSCRIBED_STREAMS = 1e3;
|
|
2425
2511
|
var DEFAULT_SETTLE_DEBOUNCE_MS = 10;
|
|
@@ -3873,7 +3959,44 @@ function _projection(target) {
|
|
|
3873
3959
|
target,
|
|
3874
3960
|
batchHandler: handler
|
|
3875
3961
|
})
|
|
3876
|
-
})
|
|
3962
|
+
}),
|
|
3963
|
+
of: (state2, options = {}) => {
|
|
3964
|
+
if (Object.keys(events).length > 0)
|
|
3965
|
+
throw new Error(
|
|
3966
|
+
`Projection "${target}" mixes .of() with .on() handlers \u2014 a projection either folds a state or declares handlers, never both`
|
|
3967
|
+
);
|
|
3968
|
+
const config2 = resolveFoldConfig(options);
|
|
3969
|
+
const fold_events = {};
|
|
3970
|
+
for (const [name, schema] of Object.entries(state2.events)) {
|
|
3971
|
+
const noop = {
|
|
3972
|
+
[`${target}_fold`]: async () => {
|
|
3973
|
+
}
|
|
3974
|
+
}[`${target}_fold`];
|
|
3975
|
+
fold_events[name] = {
|
|
3976
|
+
schema,
|
|
3977
|
+
reactions: /* @__PURE__ */ new Map([
|
|
3978
|
+
[
|
|
3979
|
+
`${target}_fold`,
|
|
3980
|
+
{
|
|
3981
|
+
handler: noop,
|
|
3982
|
+
resolver: { target },
|
|
3983
|
+
options: { blockOnError: true, maxRetries: 3 }
|
|
3984
|
+
}
|
|
3985
|
+
]
|
|
3986
|
+
])
|
|
3987
|
+
};
|
|
3988
|
+
}
|
|
3989
|
+
return {
|
|
3990
|
+
flush: (handler) => ({
|
|
3991
|
+
build: () => ({
|
|
3992
|
+
_tag: "Projection",
|
|
3993
|
+
events: fold_events,
|
|
3994
|
+
target,
|
|
3995
|
+
batchHandler: make_fold_handler(state2, handler, config2)
|
|
3996
|
+
})
|
|
3997
|
+
})
|
|
3998
|
+
};
|
|
3999
|
+
}
|
|
3877
4000
|
});
|
|
3878
4001
|
}
|
|
3879
4002
|
return base;
|