@rotorsoft/act 0.35.2 → 0.37.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 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/builders/act-builder.d.ts.map +1 -1
- package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
- package/dist/@types/internal/event-versions.d.ts +44 -0
- package/dist/@types/internal/event-versions.d.ts.map +1 -0
- package/dist/@types/internal/index.d.ts +1 -0
- package/dist/@types/internal/index.d.ts.map +1 -1
- package/dist/@types/internal/merge.d.ts.map +1 -1
- package/dist/index.cjs +92 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +92 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1317,6 +1317,43 @@ var DrainController = class {
|
|
|
1317
1317
|
}
|
|
1318
1318
|
};
|
|
1319
1319
|
|
|
1320
|
+
// src/internal/event-versions.ts
|
|
1321
|
+
var VERSION_SUFFIX = /^(.+?)_v(\d+)$/;
|
|
1322
|
+
function parse(name) {
|
|
1323
|
+
const m = name.match(VERSION_SUFFIX);
|
|
1324
|
+
if (m) {
|
|
1325
|
+
const v = Number.parseInt(m[2], 10);
|
|
1326
|
+
if (v >= 2) return { base: m[1], version: v };
|
|
1327
|
+
}
|
|
1328
|
+
return { base: name, version: 1 };
|
|
1329
|
+
}
|
|
1330
|
+
function deprecatedEventNames(names) {
|
|
1331
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1332
|
+
for (const name of names) {
|
|
1333
|
+
const { base, version } = parse(name);
|
|
1334
|
+
const list = groups.get(base);
|
|
1335
|
+
if (list) list.push({ version, name });
|
|
1336
|
+
else groups.set(base, [{ version, name }]);
|
|
1337
|
+
}
|
|
1338
|
+
const deprecated = /* @__PURE__ */ new Set();
|
|
1339
|
+
for (const list of groups.values()) {
|
|
1340
|
+
if (list.length < 2) continue;
|
|
1341
|
+
list.sort((a, b) => b.version - a.version);
|
|
1342
|
+
for (let i = 1; i < list.length; i++) deprecated.add(list[i].name);
|
|
1343
|
+
}
|
|
1344
|
+
return deprecated;
|
|
1345
|
+
}
|
|
1346
|
+
function currentVersionOf(deprecatedName, allNames) {
|
|
1347
|
+
const target = parse(deprecatedName);
|
|
1348
|
+
let highest;
|
|
1349
|
+
for (const name of allNames) {
|
|
1350
|
+
const { base, version } = parse(name);
|
|
1351
|
+
if (base !== target.base) continue;
|
|
1352
|
+
if (!highest || version > highest.version) highest = { version, name };
|
|
1353
|
+
}
|
|
1354
|
+
return highest && highest.version > target.version ? highest.name : void 0;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1320
1357
|
// src/internal/merge.ts
|
|
1321
1358
|
import { ZodObject } from "zod";
|
|
1322
1359
|
function baseTypeName(zodType) {
|
|
@@ -1374,7 +1411,11 @@ function mergeIntoExisting(state2, existing, states, actions, events) {
|
|
|
1374
1411
|
}
|
|
1375
1412
|
for (const name of Object.keys(state2.events)) {
|
|
1376
1413
|
if (existing.events[name] === state2.events[name]) continue;
|
|
1377
|
-
if (existing.events[name])
|
|
1414
|
+
if (existing.events[name]) {
|
|
1415
|
+
throw new Error(
|
|
1416
|
+
`Event "${name}" in state "${state2.name}" is declared with different Zod schemas across slices. Cross-slice event schemas must reference the same instance \u2014 extract a shared schema (e.g. \`export const ${name} = z.object({ ... })\` in a shared module) and import it in every slice that declares it.`
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1378
1419
|
if (events[name]) throw new Error(`Duplicate event "${name}"`);
|
|
1379
1420
|
}
|
|
1380
1421
|
const mergedPatch = mergePatches(existing.patch, state2.patch, state2.name);
|
|
@@ -1725,6 +1766,20 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
1725
1766
|
return [snapshot];
|
|
1726
1767
|
}
|
|
1727
1768
|
const tuples = Array.isArray(result[0]) ? result : [result];
|
|
1769
|
+
const deprecated = me._deprecated;
|
|
1770
|
+
if (deprecated && deprecated.size > 0) {
|
|
1771
|
+
const me_ = me;
|
|
1772
|
+
const warned = me_._warned ?? (me_._warned = /* @__PURE__ */ new Set());
|
|
1773
|
+
for (const [name] of tuples) {
|
|
1774
|
+
const evt = name;
|
|
1775
|
+
if (deprecated.has(evt) && !warned.has(evt)) {
|
|
1776
|
+
warned.add(evt);
|
|
1777
|
+
log().warn(
|
|
1778
|
+
`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)`
|
|
1779
|
+
);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1728
1783
|
const emitted = tuples.map(([name, data]) => ({
|
|
1729
1784
|
name,
|
|
1730
1785
|
data: skipValidation ? data : validate(name, data, me.events[name])
|
|
@@ -2725,6 +2780,38 @@ function act() {
|
|
|
2725
2780
|
mergeProjection(proj, registry.events);
|
|
2726
2781
|
registerBatchHandler(proj, batchHandlers);
|
|
2727
2782
|
}
|
|
2783
|
+
const deprecationSummary = [];
|
|
2784
|
+
for (const state2 of states.values()) {
|
|
2785
|
+
const eventNames = Object.keys(state2.events);
|
|
2786
|
+
const deprecated = deprecatedEventNames(eventNames);
|
|
2787
|
+
if (deprecated.size === 0) continue;
|
|
2788
|
+
state2._deprecated = deprecated;
|
|
2789
|
+
for (const name of deprecated) {
|
|
2790
|
+
const current = currentVersionOf(name, eventNames);
|
|
2791
|
+
deprecationSummary.push({
|
|
2792
|
+
stateName: state2.name,
|
|
2793
|
+
deprecated: name,
|
|
2794
|
+
current
|
|
2795
|
+
});
|
|
2796
|
+
}
|
|
2797
|
+
for (const [actionName, handler] of Object.entries(state2.on)) {
|
|
2798
|
+
const staticTarget = handler?._staticEmit;
|
|
2799
|
+
if (staticTarget && deprecated.has(staticTarget)) {
|
|
2800
|
+
const current = currentVersionOf(staticTarget, eventNames);
|
|
2801
|
+
throw new Error(
|
|
2802
|
+
`Action "${actionName}" in state "${state2.name}" emits deprecated event "${staticTarget}". A newer version exists: "${current}". Update the .emit() call to target the current version. The reducer (.patch) for "${staticTarget}" stays as-is \u2014 historical events still need it.`
|
|
2803
|
+
);
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
if (deprecationSummary.length > 0) {
|
|
2808
|
+
const list = deprecationSummary.map(
|
|
2809
|
+
(d) => `"${d.deprecated}" (current: "${d.current}", state: "${d.stateName}")`
|
|
2810
|
+
).join(", ");
|
|
2811
|
+
log().info(
|
|
2812
|
+
`Act registered ${deprecationSummary.length} deprecated event(s): ${list}. These are legacy versions kept for the read path. Consider truncating closed streams via app.close() when feasible to reduce historical event load. See docs/docs/architecture/event-schema-evolution.md.`
|
|
2813
|
+
);
|
|
2814
|
+
}
|
|
2728
2815
|
return new Act(
|
|
2729
2816
|
registry,
|
|
2730
2817
|
states,
|
|
@@ -2911,10 +2998,10 @@ function action_builder(state2) {
|
|
|
2911
2998
|
function emit(handler) {
|
|
2912
2999
|
if (typeof handler === "string") {
|
|
2913
3000
|
const eventName = handler;
|
|
2914
|
-
|
|
2915
|
-
eventName
|
|
2916
|
-
|
|
2917
|
-
];
|
|
3001
|
+
const emitFn = Object.assign((payload) => [eventName, payload], {
|
|
3002
|
+
_staticEmit: eventName
|
|
3003
|
+
});
|
|
3004
|
+
internal.on[action2] = emitFn;
|
|
2918
3005
|
} else {
|
|
2919
3006
|
internal.on[action2] = handler;
|
|
2920
3007
|
}
|