@rotorsoft/act 1.19.0 → 1.20.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/act-builder.d.ts.map +1 -1
- package/dist/@types/builders/projection-builder.d.ts +41 -12
- package/dist/@types/builders/projection-builder.d.ts.map +1 -1
- package/dist/@types/internal/event-sourcing.d.ts +1 -1
- package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
- package/dist/index.cjs +74 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +74 -33
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1556,7 +1556,7 @@ var DEFAULT_BATCH = 500;
|
|
|
1556
1556
|
async function snap(snapshot) {
|
|
1557
1557
|
const { id, stream, name, meta, version } = snapshot.event;
|
|
1558
1558
|
try {
|
|
1559
|
-
await store().commit(
|
|
1559
|
+
const [committed] = await store().commit(
|
|
1560
1560
|
stream,
|
|
1561
1561
|
[{ name: SNAP_EVENT, data: snapshot.state }],
|
|
1562
1562
|
{
|
|
@@ -1566,6 +1566,7 @@ async function snap(snapshot) {
|
|
|
1566
1566
|
version
|
|
1567
1567
|
// IMPORTANT! - state events are committed right after the snapshot event
|
|
1568
1568
|
);
|
|
1569
|
+
return committed;
|
|
1569
1570
|
} catch (error) {
|
|
1570
1571
|
const reason = error instanceof Error ? error.message : String(error);
|
|
1571
1572
|
log().warn(
|
|
@@ -1778,7 +1779,7 @@ async function action(me, action2, target, payload, options) {
|
|
|
1778
1779
|
const snapshot = await load(me, { stream, actor: target.actor });
|
|
1779
1780
|
if (snapshot.event?.name === TOMBSTONE_EVENT)
|
|
1780
1781
|
throw new StreamClosedError(stream);
|
|
1781
|
-
const expected = expectedVersion ?? snapshot.
|
|
1782
|
+
const expected = expectedVersion ?? (snapshot.version >= 0 ? snapshot.version : void 0);
|
|
1782
1783
|
if (me.given) {
|
|
1783
1784
|
const invariants = me.given[action2] || [];
|
|
1784
1785
|
invariants.forEach(({ valid: valid2, description }) => {
|
|
@@ -1860,18 +1861,22 @@ async function action(me, action2, target, payload, options) {
|
|
|
1860
1861
|
};
|
|
1861
1862
|
});
|
|
1862
1863
|
const last = snapshots.at(-1);
|
|
1863
|
-
const
|
|
1864
|
+
const contiguous = committed[0].version === snapshot.version + 1;
|
|
1865
|
+
const snapped = contiguous && me.snap?.(last);
|
|
1866
|
+
const snap_event = snapped ? await snap(last) : void 0;
|
|
1864
1867
|
if (!me.pii_aware) {
|
|
1865
|
-
|
|
1866
|
-
stream,
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1868
|
+
if (contiguous)
|
|
1869
|
+
cache().set(stream, {
|
|
1870
|
+
stream,
|
|
1871
|
+
state: last.state,
|
|
1872
|
+
version: snap_event?.version ?? last.event.version,
|
|
1873
|
+
event_id: snap_event?.id ?? last.event.id,
|
|
1874
|
+
patches: snap_event ? 0 : last.patches,
|
|
1875
|
+
snaps: snap_event ? last.snaps + 1 : last.snaps
|
|
1876
|
+
}).catch((err) => log().error(err));
|
|
1877
|
+
else
|
|
1878
|
+
cache().invalidate(stream).catch((err) => log().error(err));
|
|
1873
1879
|
}
|
|
1874
|
-
if (snapped) void snap(last);
|
|
1875
1880
|
return snapshots;
|
|
1876
1881
|
} catch (error) {
|
|
1877
1882
|
if (!(error instanceof ConcurrencyError)) throw error;
|
|
@@ -3740,6 +3745,7 @@ function act() {
|
|
|
3740
3745
|
autoclose_archiver: (state_name) => _aa.get(state_name) ?? null
|
|
3741
3746
|
};
|
|
3742
3747
|
const pending_projections = [];
|
|
3748
|
+
const fold_projections = [];
|
|
3743
3749
|
const batch_handlers = /* @__PURE__ */ new Map();
|
|
3744
3750
|
const lanes = [];
|
|
3745
3751
|
let _built = false;
|
|
@@ -3805,6 +3811,8 @@ function act() {
|
|
|
3805
3811
|
withProjection: (proj) => {
|
|
3806
3812
|
merge_projection(proj, registry.events);
|
|
3807
3813
|
register_batch_handler(proj, batch_handlers);
|
|
3814
|
+
if (proj.fold)
|
|
3815
|
+
fold_projections.push(proj);
|
|
3808
3816
|
return builder;
|
|
3809
3817
|
},
|
|
3810
3818
|
withActor: () => builder,
|
|
@@ -3818,6 +3826,26 @@ function act() {
|
|
|
3818
3826
|
for (const proj of pending_projections) {
|
|
3819
3827
|
merge_projection(proj, registry.events);
|
|
3820
3828
|
register_batch_handler(proj, batch_handlers);
|
|
3829
|
+
if (proj.fold) fold_projections.push(proj);
|
|
3830
|
+
}
|
|
3831
|
+
for (const proj of fold_projections) {
|
|
3832
|
+
const fold = proj.fold;
|
|
3833
|
+
const merged = states.get(fold.name);
|
|
3834
|
+
if (!merged)
|
|
3835
|
+
throw new Error(
|
|
3836
|
+
`State projection "${proj.target}" folds "${fold.name}", which is not registered \u2014 add it via withState/withSlice before build()`
|
|
3837
|
+
);
|
|
3838
|
+
const missing = Object.keys(merged.events).filter(
|
|
3839
|
+
(event_name) => !(event_name in proj.events)
|
|
3840
|
+
);
|
|
3841
|
+
if (missing.length > 0)
|
|
3842
|
+
throw new Error(
|
|
3843
|
+
`State projection "${proj.target}" of "${fold.name}" is missing events ${missing.join(", ")} \u2014 pass every partial of the state to .of()`
|
|
3844
|
+
);
|
|
3845
|
+
batch_handlers.set(
|
|
3846
|
+
proj.target,
|
|
3847
|
+
make_fold_handler(merged, fold.flush, fold.config)
|
|
3848
|
+
);
|
|
3821
3849
|
}
|
|
3822
3850
|
finalize_deprecations();
|
|
3823
3851
|
validate_lane_references(registry, lanes);
|
|
@@ -3960,39 +3988,52 @@ function _projection(target) {
|
|
|
3960
3988
|
batchHandler: handler
|
|
3961
3989
|
})
|
|
3962
3990
|
}),
|
|
3963
|
-
of: (
|
|
3991
|
+
of: (...args) => {
|
|
3964
3992
|
if (Object.keys(events).length > 0)
|
|
3965
3993
|
throw new Error(
|
|
3966
3994
|
`Projection "${target}" mixes .of() with .on() handlers \u2014 a projection either folds a state or declares handlers, never both`
|
|
3967
3995
|
);
|
|
3996
|
+
const is_state = (a) => !!a && typeof a.init === "function";
|
|
3997
|
+
const partials = args.filter(is_state);
|
|
3998
|
+
const options = args.find((a) => !is_state(a)) ?? {};
|
|
3999
|
+
if (partials.length === 0)
|
|
4000
|
+
throw new Error(`Projection "${target}" .of() requires a state`);
|
|
4001
|
+
const name = partials[0].name;
|
|
4002
|
+
for (const partial of partials)
|
|
4003
|
+
if (partial.name !== name)
|
|
4004
|
+
throw new Error(
|
|
4005
|
+
`Projection "${target}" .of() partials must share one state name \u2014 got "${name}" and "${partial.name}"`
|
|
4006
|
+
);
|
|
3968
4007
|
const config2 = resolveFoldConfig(options);
|
|
3969
4008
|
const fold_events = {};
|
|
3970
|
-
for (const
|
|
3971
|
-
const
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
4009
|
+
for (const partial of partials)
|
|
4010
|
+
for (const [event_name, schema] of Object.entries(partial.events)) {
|
|
4011
|
+
if (event_name in fold_events) continue;
|
|
4012
|
+
const noop = {
|
|
4013
|
+
[`${target}_fold`]: async () => {
|
|
4014
|
+
}
|
|
4015
|
+
}[`${target}_fold`];
|
|
4016
|
+
fold_events[event_name] = {
|
|
4017
|
+
schema,
|
|
4018
|
+
reactions: /* @__PURE__ */ new Map([
|
|
4019
|
+
[
|
|
4020
|
+
`${target}_fold`,
|
|
4021
|
+
{
|
|
4022
|
+
handler: noop,
|
|
4023
|
+
resolver: { target },
|
|
4024
|
+
options: { blockOnError: true, maxRetries: 3 }
|
|
4025
|
+
}
|
|
4026
|
+
]
|
|
4027
|
+
])
|
|
4028
|
+
};
|
|
4029
|
+
}
|
|
3989
4030
|
return {
|
|
3990
4031
|
flush: (handler) => ({
|
|
3991
4032
|
build: () => ({
|
|
3992
4033
|
_tag: "Projection",
|
|
3993
4034
|
events: fold_events,
|
|
3994
4035
|
target,
|
|
3995
|
-
|
|
4036
|
+
fold: { name, flush: handler, config: config2 }
|
|
3996
4037
|
})
|
|
3997
4038
|
})
|
|
3998
4039
|
};
|