@rotorsoft/act 1.10.1 → 1.10.2
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/act.d.ts +10 -7
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/builders/act-builder.d.ts.map +1 -1
- package/dist/@types/builders/slice-builder.d.ts +1 -1
- package/dist/@types/builders/state-builder.d.ts.map +1 -1
- package/dist/@types/internal/event-sourcing.d.ts +7 -7
- package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
- package/dist/@types/internal/index.d.ts +1 -1
- package/dist/@types/internal/index.d.ts.map +1 -1
- package/dist/@types/internal/reactions.d.ts.map +1 -1
- package/dist/@types/internal/sensitive.d.ts +6 -23
- package/dist/@types/internal/sensitive.d.ts.map +1 -1
- package/dist/@types/internal/tracing.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +78 -16
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/@types/types/registry.d.ts +12 -0
- package/dist/@types/types/registry.d.ts.map +1 -1
- package/dist/{chunk-3Z2HU726.js → chunk-5PWLGL5E.js} +2 -2
- package/dist/{chunk-BY5JPOZR.js → chunk-NGVWJXEF.js} +9 -24
- package/dist/{chunk-BY5JPOZR.js.map → chunk-NGVWJXEF.js.map} +1 -1
- package/dist/index.cjs +91 -117
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +85 -98
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +2 -2
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js +4 -4
- 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 +2 -2
- /package/dist/{chunk-3Z2HU726.js.map → chunk-5PWLGL5E.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
sleep,
|
|
20
20
|
store,
|
|
21
21
|
validate
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-5PWLGL5E.js";
|
|
23
23
|
import {
|
|
24
24
|
ActorSchema,
|
|
25
25
|
CausationEventSchema,
|
|
@@ -40,11 +40,10 @@ import {
|
|
|
40
40
|
ZodEmpty,
|
|
41
41
|
pii_fields,
|
|
42
42
|
pii_gate,
|
|
43
|
-
pii_merge,
|
|
44
43
|
pii_split,
|
|
45
44
|
pii_strip,
|
|
46
45
|
sensitive
|
|
47
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-NGVWJXEF.js";
|
|
48
47
|
import "./chunk-5WRI5ZAA.js";
|
|
49
48
|
|
|
50
49
|
// src/signals.ts
|
|
@@ -639,7 +638,7 @@ async function load_restart_seeds(guarded, target_map, stream_info, event_to_sta
|
|
|
639
638
|
);
|
|
640
639
|
return;
|
|
641
640
|
}
|
|
642
|
-
const snap2 = await load2(owner_state, stream);
|
|
641
|
+
const snap2 = await load2(owner_state, { stream });
|
|
643
642
|
seed_states.set(stream, snap2.state);
|
|
644
643
|
})
|
|
645
644
|
);
|
|
@@ -1003,7 +1002,6 @@ async function scan(source, opts = {}, callback) {
|
|
|
1003
1002
|
migrated = {
|
|
1004
1003
|
...event,
|
|
1005
1004
|
name: migration.to,
|
|
1006
|
-
// biome-ignore lint/suspicious/noExplicitAny: migration target shape is caller-defined
|
|
1007
1005
|
data: new_data
|
|
1008
1006
|
};
|
|
1009
1007
|
migrated_count++;
|
|
@@ -1052,39 +1050,37 @@ async function scan(source, opts = {}, callback) {
|
|
|
1052
1050
|
}
|
|
1053
1051
|
};
|
|
1054
1052
|
}
|
|
1055
|
-
async function load(me,
|
|
1053
|
+
async function load(me, target, callback) {
|
|
1054
|
+
const { stream, actor, asOf } = target;
|
|
1056
1055
|
const time_travel = !!asOf && Object.values(asOf).some((v) => v !== void 0);
|
|
1057
1056
|
const cached = time_travel ? void 0 : await cache().get(stream);
|
|
1058
1057
|
const cache_hit = !!cached;
|
|
1059
1058
|
let state2 = cached?.state ?? (me.init ? me.init() : {});
|
|
1060
1059
|
let patches = cached?.patches ?? 0;
|
|
1061
1060
|
let snaps = cached?.snaps ?? 0;
|
|
1062
|
-
let version = cached?.version ?? -1;
|
|
1063
1061
|
let replayed = 0;
|
|
1064
1062
|
let event;
|
|
1065
1063
|
await store().query(
|
|
1066
|
-
(
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
if (e.name === SNAP_EVENT) {
|
|
1071
|
-
state2 = e.data;
|
|
1064
|
+
(raw) => {
|
|
1065
|
+
event = me.view(raw, actor);
|
|
1066
|
+
if (event.name === SNAP_EVENT) {
|
|
1067
|
+
state2 = event.data;
|
|
1072
1068
|
snaps++;
|
|
1073
1069
|
patches = 0;
|
|
1074
1070
|
replayed++;
|
|
1075
|
-
} else if (me.patch[
|
|
1076
|
-
state2 = patch(state2, me.patch[
|
|
1071
|
+
} else if (me.patch[event.name]) {
|
|
1072
|
+
state2 = patch(state2, me.patch[event.name](event, state2));
|
|
1077
1073
|
patches++;
|
|
1078
1074
|
replayed++;
|
|
1079
|
-
} else if (
|
|
1075
|
+
} else if (event.name !== TOMBSTONE_EVENT) {
|
|
1080
1076
|
log().warn(
|
|
1081
|
-
`Skipping unknown event "${String(
|
|
1077
|
+
`Skipping unknown event "${String(event.name)}" on stream "${stream}" (id=${event.id}) \u2014 no reducer in state "${me.name}"`
|
|
1082
1078
|
);
|
|
1083
1079
|
}
|
|
1084
1080
|
callback?.({
|
|
1085
1081
|
event,
|
|
1086
1082
|
state: state2,
|
|
1087
|
-
version,
|
|
1083
|
+
version: event.version,
|
|
1088
1084
|
patches,
|
|
1089
1085
|
snaps,
|
|
1090
1086
|
cache_hit,
|
|
@@ -1097,39 +1093,43 @@ async function load(me, stream, callback, asOf, actor) {
|
|
|
1097
1093
|
...cached ? { after: cached.event_id } : { with_snaps: true, ...asOf }
|
|
1098
1094
|
}
|
|
1099
1095
|
);
|
|
1100
|
-
if (replayed > 0 && !time_travel && event) {
|
|
1096
|
+
if (replayed > 0 && !time_travel && event && !me.pii_aware) {
|
|
1101
1097
|
await cache().set(stream, {
|
|
1102
1098
|
state: state2,
|
|
1103
|
-
version,
|
|
1099
|
+
version: event.version,
|
|
1104
1100
|
event_id: event.id,
|
|
1105
1101
|
patches,
|
|
1106
1102
|
snaps
|
|
1107
1103
|
});
|
|
1108
1104
|
}
|
|
1109
|
-
return {
|
|
1105
|
+
return {
|
|
1106
|
+
event,
|
|
1107
|
+
state: state2,
|
|
1108
|
+
version: event?.version ?? cached?.version ?? -1,
|
|
1109
|
+
patches,
|
|
1110
|
+
snaps,
|
|
1111
|
+
cache_hit,
|
|
1112
|
+
replayed
|
|
1113
|
+
};
|
|
1110
1114
|
}
|
|
1111
|
-
async function action(me, action2, target, payload,
|
|
1115
|
+
async function action(me, action2, target, payload, options) {
|
|
1112
1116
|
const { stream, expectedVersion, actor } = target;
|
|
1113
1117
|
if (!stream) throw new Error("Missing target stream");
|
|
1114
|
-
const
|
|
1118
|
+
const reactingTo = options?.reactingTo;
|
|
1119
|
+
const correlator = options?.correlator ?? default_correlator;
|
|
1120
|
+
const validated = validate(action2, payload, me.actions[action2]);
|
|
1115
1121
|
const opts = me.options?.[action2];
|
|
1116
1122
|
const max_retries = opts?.maxRetries ?? 0;
|
|
1117
1123
|
for (let attempt = 0; ; attempt++) {
|
|
1118
1124
|
try {
|
|
1119
|
-
const snapshot = await load(
|
|
1120
|
-
me,
|
|
1121
|
-
stream,
|
|
1122
|
-
void 0,
|
|
1123
|
-
void 0,
|
|
1124
|
-
target.actor
|
|
1125
|
-
);
|
|
1125
|
+
const snapshot = await load(me, { stream, actor: target.actor });
|
|
1126
1126
|
if (snapshot.event?.name === TOMBSTONE_EVENT)
|
|
1127
1127
|
throw new StreamClosedError(stream);
|
|
1128
1128
|
const expected = expectedVersion ?? snapshot.event?.version;
|
|
1129
1129
|
if (me.given) {
|
|
1130
1130
|
const invariants = me.given[action2] || [];
|
|
1131
|
-
invariants.forEach(({ valid, description }) => {
|
|
1132
|
-
if (!
|
|
1131
|
+
invariants.forEach(({ valid: valid2, description }) => {
|
|
1132
|
+
if (!valid2(snapshot.state, actor))
|
|
1133
1133
|
throw new InvariantError(
|
|
1134
1134
|
action2,
|
|
1135
1135
|
validated,
|
|
@@ -1145,24 +1145,11 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
1145
1145
|
return [snapshot];
|
|
1146
1146
|
}
|
|
1147
1147
|
const tuples = Array.isArray(result[0]) ? result : [result];
|
|
1148
|
-
const
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
const evt = name;
|
|
1154
|
-
if (deprecated.has(evt) && !warned.has(evt)) {
|
|
1155
|
-
warned.add(evt);
|
|
1156
|
-
log().warn(
|
|
1157
|
-
`Action "${String(action2)}" emitted deprecated event "${evt}". A newer version exists in the registry \u2014 update the action's .emit() to target the current version. (warned once per process)`
|
|
1158
|
-
);
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
const emitted = tuples.map(([name, data]) => {
|
|
1163
|
-
const validated2 = skipValidation ? data : validate(name, data, me.events[name]);
|
|
1164
|
-
return me.message({ name, data: validated2 });
|
|
1165
|
-
});
|
|
1148
|
+
const valid = tuples.map(([name, data]) => ({
|
|
1149
|
+
name,
|
|
1150
|
+
data: validate(name, data, me.events[name])
|
|
1151
|
+
}));
|
|
1152
|
+
const emitted = valid.map((e) => me.message(e));
|
|
1166
1153
|
const meta = {
|
|
1167
1154
|
correlation: reactingTo?.meta.correlation || correlator({
|
|
1168
1155
|
action: action2,
|
|
@@ -1203,12 +1190,13 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
1203
1190
|
throw error;
|
|
1204
1191
|
}
|
|
1205
1192
|
let { state: state2, patches } = snapshot;
|
|
1206
|
-
const snapshots = committed.map((
|
|
1193
|
+
const snapshots = committed.map((row, i) => {
|
|
1194
|
+
const event = { ...row, data: valid[i].data };
|
|
1207
1195
|
const p = me.patch[event.name](event, state2);
|
|
1208
1196
|
state2 = patch(state2, p);
|
|
1209
1197
|
patches++;
|
|
1210
1198
|
return {
|
|
1211
|
-
event
|
|
1199
|
+
event,
|
|
1212
1200
|
state: state2,
|
|
1213
1201
|
version: event.version,
|
|
1214
1202
|
patches,
|
|
@@ -1220,13 +1208,15 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
1220
1208
|
});
|
|
1221
1209
|
const last = snapshots.at(-1);
|
|
1222
1210
|
const snapped = me.snap?.(last);
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1211
|
+
if (!me.pii_aware) {
|
|
1212
|
+
cache().set(stream, {
|
|
1213
|
+
state: last.state,
|
|
1214
|
+
version: last.event.version,
|
|
1215
|
+
event_id: last.event.id,
|
|
1216
|
+
patches: snapped ? 0 : last.patches,
|
|
1217
|
+
snaps: snapped ? last.snaps + 1 : last.snaps
|
|
1218
|
+
}).catch((err) => log().error(err));
|
|
1219
|
+
}
|
|
1230
1220
|
if (snapped) void snap(last);
|
|
1231
1221
|
return snapshots;
|
|
1232
1222
|
} catch (error) {
|
|
@@ -1293,15 +1283,7 @@ var traced = (inner, exit, entry) => (async (...args) => {
|
|
|
1293
1283
|
return result;
|
|
1294
1284
|
});
|
|
1295
1285
|
function build_es(logger, correlator = default_correlator) {
|
|
1296
|
-
const bound_action = (me, action_name, target, payload,
|
|
1297
|
-
me,
|
|
1298
|
-
action_name,
|
|
1299
|
-
target,
|
|
1300
|
-
payload,
|
|
1301
|
-
reactingTo,
|
|
1302
|
-
skipValidation,
|
|
1303
|
-
correlator
|
|
1304
|
-
);
|
|
1286
|
+
const bound_action = (me, action_name, target, payload, options) => action(me, action_name, target, payload, { correlator, ...options });
|
|
1305
1287
|
if (logger.level !== "trace") {
|
|
1306
1288
|
return {
|
|
1307
1289
|
snap,
|
|
@@ -1320,7 +1302,7 @@ function build_es(logger, correlator = default_correlator) {
|
|
|
1320
1302
|
)
|
|
1321
1303
|
);
|
|
1322
1304
|
}),
|
|
1323
|
-
load: traced(load, (result, _me,
|
|
1305
|
+
load: traced(load, (result, _me, target) => {
|
|
1324
1306
|
const stats = stats_marker(
|
|
1325
1307
|
result.version,
|
|
1326
1308
|
result.replayed,
|
|
@@ -1331,7 +1313,7 @@ function build_es(logger, correlator = default_correlator) {
|
|
|
1331
1313
|
es_caption(
|
|
1332
1314
|
"load",
|
|
1333
1315
|
C_GREEN,
|
|
1334
|
-
`${stream}${as_of_marker(asOf)} ${cache_marker(result.cache_hit)} ${stats}`
|
|
1316
|
+
`${target.stream}${as_of_marker(target.asOf)} ${cache_marker(result.cache_hit)} ${stats}`
|
|
1335
1317
|
)
|
|
1336
1318
|
);
|
|
1337
1319
|
}),
|
|
@@ -1819,13 +1801,10 @@ function build_handle(deps) {
|
|
|
1819
1801
|
};
|
|
1820
1802
|
for (const payload of payloads) {
|
|
1821
1803
|
const { event, handler } = payload;
|
|
1822
|
-
scoped_app.do = (action2, target, action_payload,
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
reactingTo ?? event,
|
|
1827
|
-
skipValidation
|
|
1828
|
-
);
|
|
1804
|
+
scoped_app.do = (action2, target, action_payload, options) => bound_do(action2, target, action_payload, {
|
|
1805
|
+
...options,
|
|
1806
|
+
reactingTo: options?.reactingTo ?? event
|
|
1807
|
+
});
|
|
1829
1808
|
try {
|
|
1830
1809
|
await handler(event, stream, scoped_app);
|
|
1831
1810
|
at = event.id;
|
|
@@ -2231,8 +2210,9 @@ var Act = class {
|
|
|
2231
2210
|
* @param action - The name of the action to execute
|
|
2232
2211
|
* @param target - Target specification with stream ID and actor context
|
|
2233
2212
|
* @param payload - Action payload matching the action's schema
|
|
2234
|
-
* @param
|
|
2235
|
-
*
|
|
2213
|
+
* @param options - Per-call dispatch options ({@link DoOptions}) —
|
|
2214
|
+
* `reactingTo` to thread correlation, `correlator` to override the
|
|
2215
|
+
* framework or orchestrator-level correlator for this call only.
|
|
2236
2216
|
* @returns Array of snapshots for all affected states (usually one)
|
|
2237
2217
|
*
|
|
2238
2218
|
* @throws {ValidationError} If payload doesn't match action schema
|
|
@@ -2287,7 +2267,7 @@ var Act = class {
|
|
|
2287
2267
|
* { amount: event.data.items.length }
|
|
2288
2268
|
* );
|
|
2289
2269
|
* // To use a different correlation, pass reactingTo explicitly:
|
|
2290
|
-
* // await app.do("reduceStock", target, payload, customEvent);
|
|
2270
|
+
* // await app.do("reduceStock", target, payload, { reactingTo: customEvent });
|
|
2291
2271
|
* })
|
|
2292
2272
|
* .to("inventory-1")
|
|
2293
2273
|
* .build();
|
|
@@ -2297,15 +2277,14 @@ var Act = class {
|
|
|
2297
2277
|
* @see {@link Snapshot} for return value structure
|
|
2298
2278
|
* @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
|
|
2299
2279
|
*/
|
|
2300
|
-
async do(action2, target, payload,
|
|
2280
|
+
async do(action2, target, payload, options) {
|
|
2301
2281
|
return this._scoped(async () => {
|
|
2302
2282
|
const snapshots = await this._es.action(
|
|
2303
2283
|
this.registry.actions[action2],
|
|
2304
2284
|
action2,
|
|
2305
2285
|
target,
|
|
2306
2286
|
payload,
|
|
2307
|
-
|
|
2308
|
-
skipValidation
|
|
2287
|
+
options
|
|
2309
2288
|
);
|
|
2310
2289
|
if (this._reactive_events.size > 0)
|
|
2311
2290
|
this._arm_for_event_names(
|
|
@@ -2315,7 +2294,7 @@ var Act = class {
|
|
|
2315
2294
|
return snapshots;
|
|
2316
2295
|
});
|
|
2317
2296
|
}
|
|
2318
|
-
async load(stateOrName,
|
|
2297
|
+
async load(stateOrName, streamOrTarget, callback, asOf) {
|
|
2319
2298
|
return this._scoped(async () => {
|
|
2320
2299
|
let merged;
|
|
2321
2300
|
if (typeof stateOrName === "string") {
|
|
@@ -2325,7 +2304,12 @@ var Act = class {
|
|
|
2325
2304
|
} else {
|
|
2326
2305
|
merged = this._states.get(stateOrName.name) || stateOrName;
|
|
2327
2306
|
}
|
|
2328
|
-
|
|
2307
|
+
const target = typeof streamOrTarget === "string" ? {
|
|
2308
|
+
stream: streamOrTarget,
|
|
2309
|
+
actor: void 0,
|
|
2310
|
+
asOf
|
|
2311
|
+
} : streamOrTarget;
|
|
2312
|
+
return await this._es.load(merged, target, callback);
|
|
2329
2313
|
});
|
|
2330
2314
|
}
|
|
2331
2315
|
/**
|
|
@@ -3049,11 +3033,14 @@ function act() {
|
|
|
3049
3033
|
const states = /* @__PURE__ */ new Map();
|
|
3050
3034
|
const _sf = /* @__PURE__ */ new Map();
|
|
3051
3035
|
const _dp = /* @__PURE__ */ new Map();
|
|
3036
|
+
const _de = /* @__PURE__ */ new Map();
|
|
3037
|
+
const EMPTY_DEPRECATED = /* @__PURE__ */ new Set();
|
|
3052
3038
|
const registry = {
|
|
3053
3039
|
actions: {},
|
|
3054
3040
|
events: {},
|
|
3055
3041
|
sensitive_fields: (event_name) => _sf.get(event_name) ?? [],
|
|
3056
|
-
disclosure_predicate: (state_name) => _dp.get(state_name) ?? null
|
|
3042
|
+
disclosure_predicate: (state_name) => _dp.get(state_name) ?? null,
|
|
3043
|
+
deprecated_events: (state_name) => _de.get(state_name) ?? EMPTY_DEPRECATED
|
|
3057
3044
|
};
|
|
3058
3045
|
const pending_projections = [];
|
|
3059
3046
|
const batch_handlers = /* @__PURE__ */ new Map();
|
|
@@ -3065,7 +3052,7 @@ function act() {
|
|
|
3065
3052
|
const event_names = Object.keys(state2.events);
|
|
3066
3053
|
const deprecated = deprecated_event_names(event_names);
|
|
3067
3054
|
if (deprecated.size === 0) continue;
|
|
3068
|
-
state2.
|
|
3055
|
+
_de.set(state2.name, deprecated);
|
|
3069
3056
|
for (const name of deprecated) {
|
|
3070
3057
|
const current = current_version_of(name, event_names);
|
|
3071
3058
|
deprecation_summary.push({
|
|
@@ -3193,18 +3180,17 @@ function act() {
|
|
|
3193
3180
|
);
|
|
3194
3181
|
}
|
|
3195
3182
|
const disclose = state2.disclose ?? null;
|
|
3196
|
-
state2.
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3183
|
+
state2.pii_aware = true;
|
|
3184
|
+
state2.view = (event, actor) => pii_gate(
|
|
3185
|
+
event,
|
|
3186
|
+
fields_by_event.get(event.name) ?? [],
|
|
3187
|
+
disclose,
|
|
3188
|
+
actor
|
|
3189
|
+
);
|
|
3200
3190
|
state2.message = (validated) => {
|
|
3201
3191
|
const fields = fields_by_event.get(validated.name);
|
|
3202
3192
|
return fields ? pii_split(validated, fields) : validated;
|
|
3203
3193
|
};
|
|
3204
|
-
for (const [event_name, fields] of fields_by_event) {
|
|
3205
|
-
const original = state2.patch[event_name];
|
|
3206
|
-
state2.patch[event_name] = (event, s) => original(pii_merge(event, fields), s);
|
|
3207
|
-
}
|
|
3208
3194
|
}
|
|
3209
3195
|
for (const [target, original] of batch_handlers) {
|
|
3210
3196
|
const wrapped = async (events, stream) => {
|
|
@@ -3384,9 +3370,10 @@ function state(entry) {
|
|
|
3384
3370
|
init,
|
|
3385
3371
|
patch: default_patch,
|
|
3386
3372
|
on: {},
|
|
3387
|
-
// Step delegates initialized as identity
|
|
3388
|
-
//
|
|
3389
|
-
// the gate / split.
|
|
3373
|
+
// Step delegates initialized as identity, `pii_aware` false.
|
|
3374
|
+
// `act().build()` flips both on states with `sensitive(...)`
|
|
3375
|
+
// events to bake in the gate / split and gate the cache write.
|
|
3376
|
+
pii_aware: false,
|
|
3390
3377
|
view: (event) => event,
|
|
3391
3378
|
message: (validated) => validated
|
|
3392
3379
|
};
|