@rotorsoft/act 1.34.4 → 1.35.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 +2 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/act.d.ts +8 -6
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/builders/act-builder.d.ts.map +1 -1
- package/dist/@types/builders/state-builder.d.ts +1 -1
- package/dist/@types/internal/correlate-cycle.d.ts +4 -2
- package/dist/@types/internal/correlate-cycle.d.ts.map +1 -1
- package/dist/@types/internal/event-sourcing.d.ts +1 -6
- package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
- package/dist/@types/internal/projection-fold.d.ts +1 -1
- package/dist/@types/internal/projection-fold.d.ts.map +1 -1
- package/dist/@types/internal/tracing.d.ts.map +1 -1
- package/dist/@types/types/act.d.ts +5 -21
- package/dist/@types/types/act.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +34 -16
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/@types/types/builders.d.ts +26 -30
- package/dist/@types/types/builders.d.ts.map +1 -1
- package/dist/@types/types/errors.d.ts +56 -29
- package/dist/@types/types/errors.d.ts.map +1 -1
- package/dist/{chunk-GBAMBUDN.cjs → chunk-BNTVE7RQ.cjs} +13 -13
- package/dist/{chunk-GBAMBUDN.cjs.map → chunk-BNTVE7RQ.cjs.map} +1 -1
- package/dist/{chunk-BKCNU6FF.js → chunk-DH2HW5XJ.js} +13 -4
- package/dist/chunk-DH2HW5XJ.js.map +1 -0
- package/dist/{chunk-RF5MV72K.cjs → chunk-L66OOP2F.cjs} +14 -5
- package/dist/chunk-L66OOP2F.cjs.map +1 -0
- package/dist/{chunk-IEM2NS6J.js → chunk-MHPUVTMX.js} +2 -2
- package/dist/index.cjs +230 -229
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +73 -72
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +4 -4
- package/dist/test/index.js +2 -2
- package/dist/types/index.cjs +4 -2
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.js +3 -1
- package/package.json +1 -1
- package/dist/chunk-BKCNU6FF.js.map +0 -1
- package/dist/chunk-RF5MV72K.cjs.map +0 -1
- /package/dist/{chunk-IEM2NS6J.js.map → chunk-MHPUVTMX.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
sleep,
|
|
41
41
|
store,
|
|
42
42
|
validate
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-MHPUVTMX.js";
|
|
44
44
|
import {
|
|
45
45
|
ActorSchema,
|
|
46
46
|
CausationEventSchema,
|
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
InvariantError,
|
|
58
58
|
LogLevels,
|
|
59
59
|
NonRetryableError,
|
|
60
|
+
PreconditionError,
|
|
60
61
|
QuerySchema,
|
|
61
62
|
REDACTED,
|
|
62
63
|
SHREDDED,
|
|
@@ -70,7 +71,7 @@ import {
|
|
|
70
71
|
pii_fields,
|
|
71
72
|
pii_schemas,
|
|
72
73
|
sensitive
|
|
73
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-DH2HW5XJ.js";
|
|
74
75
|
|
|
75
76
|
// src/signals.ts
|
|
76
77
|
process.once("SIGINT", async (arg) => {
|
|
@@ -1265,9 +1266,9 @@ var CorrelateCycle = class {
|
|
|
1265
1266
|
return pattern.test(stream);
|
|
1266
1267
|
}
|
|
1267
1268
|
/** Scan past the checkpoint, subscribe new targets and raise their work marks. */
|
|
1268
|
-
async correlate(
|
|
1269
|
+
async correlate(limit, lease = false) {
|
|
1269
1270
|
if (this._restoring) return { kind: "idle", last_id: this._checkpoint };
|
|
1270
|
-
const pass = this._scan(
|
|
1271
|
+
const pass = this._scan(limit, lease);
|
|
1271
1272
|
this._passes.add(pass);
|
|
1272
1273
|
try {
|
|
1273
1274
|
return await pass;
|
|
@@ -1275,7 +1276,7 @@ var CorrelateCycle = class {
|
|
|
1275
1276
|
this._passes.delete(pass);
|
|
1276
1277
|
}
|
|
1277
1278
|
}
|
|
1278
|
-
async _scan(
|
|
1279
|
+
async _scan(limit, lease) {
|
|
1279
1280
|
await this._init();
|
|
1280
1281
|
const epoch = this._epoch;
|
|
1281
1282
|
const idle = () => ({
|
|
@@ -1370,7 +1371,7 @@ var CorrelateCycle = class {
|
|
|
1370
1371
|
},
|
|
1371
1372
|
// Resolvers see `pii: null`: correlate runs actor-less, and one
|
|
1372
1373
|
// undecryptable row would otherwise stop every stream's reactions.
|
|
1373
|
-
{ after, limit
|
|
1374
|
+
{ after, limit, with_pii: false }
|
|
1374
1375
|
);
|
|
1375
1376
|
if (this._declared_lanes.size > 1) await this._recall(correlated);
|
|
1376
1377
|
const read = last_id > after;
|
|
@@ -1409,7 +1410,7 @@ var CorrelateCycle = class {
|
|
|
1409
1410
|
return { kind: "scanned", last_id, subscribed, marked, read };
|
|
1410
1411
|
}
|
|
1411
1412
|
this._checkpoint = last_id;
|
|
1412
|
-
if (!stopped && found !==
|
|
1413
|
+
if (!stopped && found !== limit) this._lowered = raised;
|
|
1413
1414
|
return { kind: "scanned", last_id, subscribed: 0, marked: 0, read };
|
|
1414
1415
|
}
|
|
1415
1416
|
/**
|
|
@@ -1647,14 +1648,12 @@ var subscribe = (streams, correlated_at, correlator) => store().subscribe(stream
|
|
|
1647
1648
|
|
|
1648
1649
|
// src/internal/event-sourcing.ts
|
|
1649
1650
|
import { patch } from "@rotorsoft/act-patch";
|
|
1651
|
+
import { prettifyError } from "zod";
|
|
1650
1652
|
var bare_patch = (_me, state2, partial) => patch(state2, partial);
|
|
1651
|
-
var
|
|
1652
|
-
const
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
next,
|
|
1656
|
-
me.state
|
|
1657
|
-
);
|
|
1653
|
+
var check_invariants = (target, state2, schema) => {
|
|
1654
|
+
const result = schema.safeParse(state2);
|
|
1655
|
+
if (!result.success)
|
|
1656
|
+
throw new ValidationError(target, state2, prettifyError(result.error));
|
|
1658
1657
|
};
|
|
1659
1658
|
var DEFAULT_BATCH = 500;
|
|
1660
1659
|
var cache_put = (stream, entry) => cache().set(stream, entry).catch(
|
|
@@ -1911,8 +1910,8 @@ async function action(me, action2, target, payload, options, patch_fn = bare_pat
|
|
|
1911
1910
|
if (snapshot.event?.name === TOMBSTONE_EVENT)
|
|
1912
1911
|
throw new StreamClosedError(stream);
|
|
1913
1912
|
const expected = expectedVersion ?? (snapshot.version >= 0 ? snapshot.version : void 0);
|
|
1914
|
-
for (const { valid
|
|
1915
|
-
if (!
|
|
1913
|
+
for (const { valid, description } of me.given?.[action2] ?? [])
|
|
1914
|
+
if (!valid(snapshot.state, actor))
|
|
1916
1915
|
throw new InvariantError(
|
|
1917
1916
|
action2,
|
|
1918
1917
|
validated,
|
|
@@ -1922,25 +1921,22 @@ async function action(me, action2, target, payload, options, patch_fn = bare_pat
|
|
|
1922
1921
|
);
|
|
1923
1922
|
for (const { holds, description } of me.requires?.[action2] ?? [])
|
|
1924
1923
|
if (!holds(validated, snapshot.state, actor))
|
|
1925
|
-
throw new
|
|
1924
|
+
throw new PreconditionError(
|
|
1926
1925
|
action2,
|
|
1927
1926
|
validated,
|
|
1928
1927
|
target,
|
|
1929
1928
|
snapshot,
|
|
1930
|
-
description
|
|
1931
|
-
"precondition"
|
|
1929
|
+
description
|
|
1932
1930
|
);
|
|
1933
1931
|
const result = me.on[action2](validated, snapshot, target);
|
|
1934
|
-
if (!result)
|
|
1935
|
-
if (Array.isArray(result) && result.length === 0) {
|
|
1932
|
+
if (!result || Array.isArray(result) && !result.length)
|
|
1936
1933
|
return [snapshot];
|
|
1937
|
-
}
|
|
1938
1934
|
const tuples = Array.isArray(result[0]) ? result : [result];
|
|
1939
|
-
const
|
|
1935
|
+
const events = tuples.map(([name, data]) => ({
|
|
1940
1936
|
name,
|
|
1941
1937
|
data: validate(name, data, me.events[name])
|
|
1942
1938
|
}));
|
|
1943
|
-
const emitted =
|
|
1939
|
+
const emitted = events.map((e) => me.message(e));
|
|
1944
1940
|
const meta = {
|
|
1945
1941
|
correlation: reactingTo?.meta.correlation || correlator({
|
|
1946
1942
|
action: action2,
|
|
@@ -1961,6 +1957,23 @@ async function action(me, action2, target, payload, options, patch_fn = bare_pat
|
|
|
1961
1957
|
} : void 0
|
|
1962
1958
|
}
|
|
1963
1959
|
};
|
|
1960
|
+
let { state: state2, patches } = snapshot;
|
|
1961
|
+
const created = /* @__PURE__ */ new Date();
|
|
1962
|
+
const folded = events.map((e, i) => {
|
|
1963
|
+
const event = {
|
|
1964
|
+
...e,
|
|
1965
|
+
id: -1,
|
|
1966
|
+
stream,
|
|
1967
|
+
version: snapshot.version + i + 1,
|
|
1968
|
+
created,
|
|
1969
|
+
meta
|
|
1970
|
+
};
|
|
1971
|
+
const patch2 = me.patch[event.name](event, state2);
|
|
1972
|
+
state2 = patch_fn(me, state2, patch2, event);
|
|
1973
|
+
patches++;
|
|
1974
|
+
return { state: state2, patch: patch2, patches };
|
|
1975
|
+
});
|
|
1976
|
+
check_invariants(`${me.name}.${action2}`, state2, me.state);
|
|
1964
1977
|
let committed;
|
|
1965
1978
|
try {
|
|
1966
1979
|
committed = await store().commit(
|
|
@@ -1975,24 +1988,17 @@ async function action(me, action2, target, payload, options, patch_fn = bare_pat
|
|
|
1975
1988
|
if (error instanceof ConcurrencyError) void cache_drop(stream);
|
|
1976
1989
|
throw error;
|
|
1977
1990
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
patches
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
patches,
|
|
1990
|
-
snaps: snapshot.snaps,
|
|
1991
|
-
patch: p,
|
|
1992
|
-
cache_hit: snapshot.cache_hit,
|
|
1993
|
-
replayed: snapshot.replayed
|
|
1994
|
-
};
|
|
1995
|
-
});
|
|
1991
|
+
const snapshots = committed.map((row, i) => ({
|
|
1992
|
+
event: { ...row, data: events[i].data },
|
|
1993
|
+
state: folded[i].state,
|
|
1994
|
+
version: row.version,
|
|
1995
|
+
id: row.id,
|
|
1996
|
+
patches: folded[i].patches,
|
|
1997
|
+
snaps: snapshot.snaps,
|
|
1998
|
+
patch: folded[i].patch,
|
|
1999
|
+
cache_hit: snapshot.cache_hit,
|
|
2000
|
+
replayed: snapshot.replayed
|
|
2001
|
+
}));
|
|
1996
2002
|
const last = snapshots.at(-1);
|
|
1997
2003
|
const contiguous = committed[0].version === snapshot.version + 1;
|
|
1998
2004
|
const snapped = contiguous && me.snap?.(last);
|
|
@@ -2569,7 +2575,7 @@ function pii_strip(event, fields) {
|
|
|
2569
2575
|
|
|
2570
2576
|
// src/internal/projection-fold.ts
|
|
2571
2577
|
var FOLD_RESET = /* @__PURE__ */ Symbol("act.fold.reset");
|
|
2572
|
-
function make_fold_handler(me, flush, config2, patch_fn
|
|
2578
|
+
function make_fold_handler(me, flush, config2, patch_fn, sensitive_fields) {
|
|
2573
2579
|
const load_me = {
|
|
2574
2580
|
...me,
|
|
2575
2581
|
view: (raw, actor) => {
|
|
@@ -2933,10 +2939,8 @@ var SettleLoop = class {
|
|
|
2933
2939
|
};
|
|
2934
2940
|
|
|
2935
2941
|
// src/act.ts
|
|
2936
|
-
function validate_only_lanes(
|
|
2937
|
-
|
|
2938
|
-
const declared = /* @__PURE__ */ new Set(["default", ...lanes.map((l) => l.name)]);
|
|
2939
|
-
const unknown = options.onlyLanes.filter((l) => !declared.has(l));
|
|
2942
|
+
function validate_only_lanes(only, declared) {
|
|
2943
|
+
const unknown = (only ?? []).filter((l) => !declared.includes(l));
|
|
2940
2944
|
if (unknown.length > 0)
|
|
2941
2945
|
throw new Error(
|
|
2942
2946
|
`ActOptions.onlyLanes references undeclared lane(s): ${unknown.map((l) => `"${l}"`).join(", ")}`
|
|
@@ -3040,7 +3044,7 @@ var Act = class {
|
|
|
3040
3044
|
this._batch_handlers = batch_handlers;
|
|
3041
3045
|
this._lanes = lanes;
|
|
3042
3046
|
this._declared_lanes = ["default", ...lanes.map((l) => l.name)];
|
|
3043
|
-
validate_only_lanes(options,
|
|
3047
|
+
validate_only_lanes(options.onlyLanes, this._declared_lanes);
|
|
3044
3048
|
this._ports = options.scoped ?? default_scope();
|
|
3045
3049
|
const ports = this._ports;
|
|
3046
3050
|
this._scoped = make_run_scoped({
|
|
@@ -3082,7 +3086,7 @@ var Act = class {
|
|
|
3082
3086
|
handle,
|
|
3083
3087
|
handle_batch
|
|
3084
3088
|
);
|
|
3085
|
-
this._advise_orphaned_lanes(
|
|
3089
|
+
this._advise_orphaned_lanes();
|
|
3086
3090
|
this._audit_deps = this._build_audit_deps();
|
|
3087
3091
|
this._correlate = this._build_correlate(options, classification);
|
|
3088
3092
|
this._settle = this._build_settle(options);
|
|
@@ -3107,7 +3111,7 @@ var Act = class {
|
|
|
3107
3111
|
/** Apps that declare no lanes pass no lane filter to `claim`. */
|
|
3108
3112
|
_build_drain_controllers(options, lanes, handle, handle_batch) {
|
|
3109
3113
|
const all_lanes = this._declared_lanes;
|
|
3110
|
-
const only_set = options.onlyLanes
|
|
3114
|
+
const only_set = options.onlyLanes?.length ? new Set(options.onlyLanes) : void 0;
|
|
3111
3115
|
const active_lanes = only_set ? all_lanes.filter((n) => only_set.has(n)) : all_lanes;
|
|
3112
3116
|
const single_default_lane = lanes.length === 0;
|
|
3113
3117
|
const controllers = /* @__PURE__ */ new Map();
|
|
@@ -3144,15 +3148,14 @@ var Act = class {
|
|
|
3144
3148
|
leaseMillis: cfg.leaseMillis
|
|
3145
3149
|
}
|
|
3146
3150
|
});
|
|
3147
|
-
if (cfg?.cycleMs !== void 0 &&
|
|
3151
|
+
if (cfg?.cycleMs !== void 0 && this._drain)
|
|
3148
3152
|
controller.start(cfg.cycleMs);
|
|
3149
3153
|
controllers.set(name, controller);
|
|
3150
3154
|
}
|
|
3151
3155
|
return controllers;
|
|
3152
3156
|
}
|
|
3153
3157
|
/** One process can't see the cluster, so it reports its own lane gaps. */
|
|
3154
|
-
_advise_orphaned_lanes(
|
|
3155
|
-
if (!options.onlyLanes || options.onlyLanes.length === 0) return;
|
|
3158
|
+
_advise_orphaned_lanes() {
|
|
3156
3159
|
const active = new Set(this._drain_controllers.keys());
|
|
3157
3160
|
const orphaned = this._declared_lanes.filter((name) => !active.has(name));
|
|
3158
3161
|
if (orphaned.length === 0) return;
|
|
@@ -3202,10 +3205,7 @@ var Act = class {
|
|
|
3202
3205
|
_build_settle(options) {
|
|
3203
3206
|
return new SettleLoop(
|
|
3204
3207
|
{
|
|
3205
|
-
correlate: (q) => this._correlate_pass(
|
|
3206
|
-
{ ...q, limit: q.limit ?? this._poll_limit ?? 100 },
|
|
3207
|
-
true
|
|
3208
|
-
),
|
|
3208
|
+
correlate: (q) => this._correlate_pass(q.limit ?? this._poll_limit ?? 100, true),
|
|
3209
3209
|
// `settle()` validated the options, and `_schedule` skips writer-only
|
|
3210
3210
|
// instances, so the loop drains through the internal path.
|
|
3211
3211
|
drain: (o) => this._scoped(() => this._drain_all(o)),
|
|
@@ -3300,9 +3300,10 @@ var Act = class {
|
|
|
3300
3300
|
}
|
|
3301
3301
|
/**
|
|
3302
3302
|
* Executes an action: validates the payload, loads the state, checks its
|
|
3303
|
-
*
|
|
3304
|
-
*
|
|
3305
|
-
*
|
|
3303
|
+
* preconditions, runs the handler, folds the events it emits, checks the
|
|
3304
|
+
* state's invariants (its schema) on the result, and commits them with
|
|
3305
|
+
* optimistic concurrency on the stream. The reactions to those events
|
|
3306
|
+
* settle on their own (unless `autoSettle: false`).
|
|
3306
3307
|
*
|
|
3307
3308
|
* @template TKey - Action name from registered actions
|
|
3308
3309
|
* @param action - The name of the action to execute
|
|
@@ -3313,8 +3314,9 @@ var Act = class {
|
|
|
3313
3314
|
* framework or orchestrator-level correlator for this call only.
|
|
3314
3315
|
* @returns Array of snapshots for all affected states (usually one)
|
|
3315
3316
|
*
|
|
3316
|
-
* @throws {ValidationError} If payload doesn't match action schema
|
|
3317
|
-
*
|
|
3317
|
+
* @throws {ValidationError} If the payload doesn't match the action schema,
|
|
3318
|
+
* or the state the action leaves breaks the state schema
|
|
3319
|
+
* @throws {PreconditionError} If business rules are violated
|
|
3318
3320
|
* @throws {ConcurrencyError} If another process modified the stream
|
|
3319
3321
|
*
|
|
3320
3322
|
* @example Basic action execution
|
|
@@ -3333,7 +3335,7 @@ var Act = class {
|
|
|
3333
3335
|
*
|
|
3334
3336
|
* @see {@link Target} for target structure
|
|
3335
3337
|
* @see {@link Snapshot} for return value structure
|
|
3336
|
-
* @see {@link ValidationError}, {@link
|
|
3338
|
+
* @see {@link ValidationError}, {@link PreconditionError}, {@link ConcurrencyError}
|
|
3337
3339
|
*/
|
|
3338
3340
|
async do(action2, target, payload, options) {
|
|
3339
3341
|
const reacting_to = options?.reactingTo ?? current_reacting();
|
|
@@ -3567,17 +3569,17 @@ var Act = class {
|
|
|
3567
3569
|
* @see {@link start_correlations} for automatic periodic correlation
|
|
3568
3570
|
*/
|
|
3569
3571
|
async correlate(query = {}) {
|
|
3570
|
-
const pass = await this._correlate_pass(
|
|
3572
|
+
const pass = await this._correlate_pass(query.limit ?? 10);
|
|
3571
3573
|
return {
|
|
3572
3574
|
subscribed: pass.kind === "scanned" ? pass.subscribed : 0,
|
|
3573
3575
|
last_id: pass.last_id
|
|
3574
3576
|
};
|
|
3575
3577
|
}
|
|
3576
3578
|
/** One correlate pass; arms every lane when it found work. */
|
|
3577
|
-
async _correlate_pass(
|
|
3579
|
+
async _correlate_pass(limit, lease = false) {
|
|
3578
3580
|
if (!this._drain) return { kind: "idle", last_id: -1 };
|
|
3579
3581
|
return this._scoped(async () => {
|
|
3580
|
-
const pass = await this._correlate.correlate(
|
|
3582
|
+
const pass = await this._correlate.correlate(limit, lease);
|
|
3581
3583
|
if (pass.kind === "scanned" && pass.marked > 0) this._arm_all();
|
|
3582
3584
|
return pass;
|
|
3583
3585
|
});
|
|
@@ -4640,7 +4642,7 @@ function act() {
|
|
|
4640
4642
|
if (reset) Object.defineProperty(wrapped, FOLD_RESET, { value: reset });
|
|
4641
4643
|
return wrapped;
|
|
4642
4644
|
};
|
|
4643
|
-
const make_batch_handlers = (
|
|
4645
|
+
const make_batch_handlers = () => {
|
|
4644
4646
|
const handlers = new Map(batch_handlers);
|
|
4645
4647
|
for (const spec of fold_specs) {
|
|
4646
4648
|
handlers.set(
|
|
@@ -4650,7 +4652,7 @@ function act() {
|
|
|
4650
4652
|
spec.merged,
|
|
4651
4653
|
spec.flush,
|
|
4652
4654
|
spec.config,
|
|
4653
|
-
|
|
4655
|
+
bare_patch,
|
|
4654
4656
|
// Head loads strip sensitive keys like the warm path.
|
|
4655
4657
|
registry.sensitive_fields
|
|
4656
4658
|
)
|
|
@@ -4731,7 +4733,6 @@ function act() {
|
|
|
4731
4733
|
},
|
|
4732
4734
|
build: (options) => {
|
|
4733
4735
|
resolveActConfig(options);
|
|
4734
|
-
const patch_fn = options?.validateFoldedState === true ? validating_patch : bare_patch;
|
|
4735
4736
|
if (!_built) {
|
|
4736
4737
|
for (const proj of pending_projections) add_projection(proj);
|
|
4737
4738
|
for (const proj of fold_projections) {
|
|
@@ -4792,10 +4793,9 @@ function act() {
|
|
|
4792
4793
|
return new Act(
|
|
4793
4794
|
registry,
|
|
4794
4795
|
states,
|
|
4795
|
-
make_batch_handlers(
|
|
4796
|
+
make_batch_handlers(),
|
|
4796
4797
|
options,
|
|
4797
|
-
lanes
|
|
4798
|
-
patch_fn
|
|
4798
|
+
lanes
|
|
4799
4799
|
);
|
|
4800
4800
|
},
|
|
4801
4801
|
events: registry.events
|
|
@@ -5270,6 +5270,7 @@ export {
|
|
|
5270
5270
|
LogLevels,
|
|
5271
5271
|
NonRetryableError,
|
|
5272
5272
|
PackageSchema,
|
|
5273
|
+
PreconditionError,
|
|
5273
5274
|
QuerySchema,
|
|
5274
5275
|
REDACTED,
|
|
5275
5276
|
SHREDDED,
|