@rotorsoft/act 1.31.28 → 1.32.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/adapters/in-memory-store.d.ts.map +1 -1
- package/dist/@types/internal/audit.d.ts.map +1 -1
- package/dist/@types/internal/correlate-cycle.d.ts.map +1 -1
- package/dist/@types/internal/drain-cycle.d.ts.map +1 -1
- package/dist/@types/internal/drain.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +13 -0
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/@types/types/audit.d.ts +19 -1
- package/dist/@types/types/audit.d.ts.map +1 -1
- package/dist/@types/types/reaction.d.ts +14 -0
- package/dist/@types/types/reaction.d.ts.map +1 -1
- package/dist/@types/types/schemas.d.ts +1 -0
- package/dist/@types/types/schemas.d.ts.map +1 -1
- package/dist/{chunk-34WL4DPW.js → chunk-5NFBMYTY.js} +16 -5
- package/dist/chunk-5NFBMYTY.js.map +1 -0
- package/dist/{chunk-7IBF4FTQ.js → chunk-BI3UNNVA.js} +2 -1
- package/dist/chunk-BI3UNNVA.js.map +1 -0
- package/dist/index.cjs +124 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +111 -15
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +15 -3
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js +2 -2
- package/dist/types/index.cjs +1 -0
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-34WL4DPW.js.map +0 -1
- package/dist/chunk-7IBF4FTQ.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -524,6 +524,7 @@ var QuerySchema = import_zod2.z.object({
|
|
|
524
524
|
backward: import_zod2.z.boolean().optional(),
|
|
525
525
|
correlation: import_zod2.z.string().optional(),
|
|
526
526
|
with_snaps: import_zod2.z.boolean().optional(),
|
|
527
|
+
with_pii: import_zod2.z.boolean().optional(),
|
|
527
528
|
stream_exact: import_zod2.z.boolean().optional()
|
|
528
529
|
}).readonly();
|
|
529
530
|
|
|
@@ -881,7 +882,8 @@ var InMemoryStore = class {
|
|
|
881
882
|
}
|
|
882
883
|
// Attach the isolated PII payload (or null) to an event before handing it to
|
|
883
884
|
// a caller. Allocation-free for events without PII — by far the common case.
|
|
884
|
-
_with_pii(e) {
|
|
885
|
+
_with_pii(e, wanted = true) {
|
|
886
|
+
if (!wanted) return e;
|
|
885
887
|
const pii = this._pii.get(e.stream)?.get(e.id);
|
|
886
888
|
return pii ? { ...e, pii } : e;
|
|
887
889
|
}
|
|
@@ -952,7 +954,12 @@ var InMemoryStore = class {
|
|
|
952
954
|
if (floor_id >= 0 && e.id < floor_id) break;
|
|
953
955
|
if (query.created_after && e.created <= query.created_after) continue;
|
|
954
956
|
await Promise.resolve(
|
|
955
|
-
callback(
|
|
957
|
+
callback(
|
|
958
|
+
this._with_pii(
|
|
959
|
+
e,
|
|
960
|
+
query?.with_pii !== false
|
|
961
|
+
)
|
|
962
|
+
)
|
|
956
963
|
);
|
|
957
964
|
count++;
|
|
958
965
|
if (query?.limit && count >= query.limit) break;
|
|
@@ -967,7 +974,12 @@ var InMemoryStore = class {
|
|
|
967
974
|
if (query?.created_before && e.created >= query.created_before)
|
|
968
975
|
continue;
|
|
969
976
|
await Promise.resolve(
|
|
970
|
-
callback(
|
|
977
|
+
callback(
|
|
978
|
+
this._with_pii(
|
|
979
|
+
e,
|
|
980
|
+
query?.with_pii !== false
|
|
981
|
+
)
|
|
982
|
+
)
|
|
971
983
|
);
|
|
972
984
|
count++;
|
|
973
985
|
if (query?.limit && count >= query.limit) break;
|
|
@@ -1858,7 +1870,8 @@ var ALL_CATEGORIES = [
|
|
|
1858
1870
|
"snapshot-drift",
|
|
1859
1871
|
"routing-health",
|
|
1860
1872
|
"correlation-gaps",
|
|
1861
|
-
"clock-anomalies"
|
|
1873
|
+
"clock-anomalies",
|
|
1874
|
+
"unreadable-events"
|
|
1862
1875
|
];
|
|
1863
1876
|
async function* audit(deps, categories, options = {}) {
|
|
1864
1877
|
const requested = new Set(categories ?? [...ALL_CATEGORIES]);
|
|
@@ -1868,7 +1881,9 @@ async function* audit(deps, categories, options = {}) {
|
|
|
1868
1881
|
);
|
|
1869
1882
|
const need_stats = passes.some((p) => p.on_stat !== void 0);
|
|
1870
1883
|
const need_streams = passes.some((p) => p.on_stream !== void 0);
|
|
1871
|
-
const need_events = passes.some(
|
|
1884
|
+
const need_events = passes.some(
|
|
1885
|
+
(p) => p.on_event !== void 0 || p.on_unreadable !== void 0
|
|
1886
|
+
);
|
|
1872
1887
|
if (need_stats) {
|
|
1873
1888
|
const stats = await deps.store().query_stats(
|
|
1874
1889
|
{},
|
|
@@ -1884,15 +1899,57 @@ async function* audit(deps, categories, options = {}) {
|
|
|
1884
1899
|
});
|
|
1885
1900
|
}
|
|
1886
1901
|
if (need_events) {
|
|
1887
|
-
await
|
|
1888
|
-
|
|
1889
|
-
|
|
1902
|
+
await scan_events_resiliently(
|
|
1903
|
+
deps,
|
|
1904
|
+
options.query,
|
|
1905
|
+
(event) => {
|
|
1906
|
+
for (const p of passes) p.on_event?.(event);
|
|
1907
|
+
},
|
|
1908
|
+
(row) => {
|
|
1909
|
+
for (const p of passes) p.on_unreadable?.(row);
|
|
1910
|
+
}
|
|
1911
|
+
);
|
|
1890
1912
|
}
|
|
1891
1913
|
for (const p of passes) await p.finalize?.(deps);
|
|
1892
1914
|
for (const p of passes) {
|
|
1893
1915
|
for (const f of p.drain()) yield f;
|
|
1894
1916
|
}
|
|
1895
1917
|
}
|
|
1918
|
+
async function scan_events_resiliently(deps, query, on_event, on_unreadable) {
|
|
1919
|
+
let cursor = query?.after;
|
|
1920
|
+
let remaining = query?.limit;
|
|
1921
|
+
for (; ; ) {
|
|
1922
|
+
let seen = 0;
|
|
1923
|
+
try {
|
|
1924
|
+
await deps.store().query(
|
|
1925
|
+
(event) => {
|
|
1926
|
+
seen++;
|
|
1927
|
+
cursor = event.id;
|
|
1928
|
+
on_event(event);
|
|
1929
|
+
},
|
|
1930
|
+
{
|
|
1931
|
+
...query,
|
|
1932
|
+
after: cursor,
|
|
1933
|
+
...remaining !== void 0 && { limit: remaining }
|
|
1934
|
+
}
|
|
1935
|
+
);
|
|
1936
|
+
return;
|
|
1937
|
+
} catch (error) {
|
|
1938
|
+
if (query?.backward) throw error;
|
|
1939
|
+
let row;
|
|
1940
|
+
await deps.store().query(
|
|
1941
|
+
(e) => {
|
|
1942
|
+
row = { stream: e.stream, event_id: e.id, error };
|
|
1943
|
+
},
|
|
1944
|
+
{ ...query, after: cursor, limit: 1, with_pii: false }
|
|
1945
|
+
);
|
|
1946
|
+
if (!row) throw error;
|
|
1947
|
+
on_unreadable(row);
|
|
1948
|
+
if (remaining !== void 0) remaining -= seen;
|
|
1949
|
+
cursor = row.event_id;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1896
1953
|
var make_schema_pass = (deps) => {
|
|
1897
1954
|
const findings = [];
|
|
1898
1955
|
return {
|
|
@@ -2257,6 +2314,22 @@ function restart_is_supported(deps, head_event_name) {
|
|
|
2257
2314
|
const state2 = deps.event_to_state.get(head_event_name);
|
|
2258
2315
|
return state2?.snap !== void 0;
|
|
2259
2316
|
}
|
|
2317
|
+
function make_unreadable_events_pass() {
|
|
2318
|
+
const findings = [];
|
|
2319
|
+
return {
|
|
2320
|
+
category: "unreadable-events",
|
|
2321
|
+
on_unreadable({ stream, event_id, error }) {
|
|
2322
|
+
findings.push({
|
|
2323
|
+
category: "unreadable-events",
|
|
2324
|
+
stream,
|
|
2325
|
+
event_id,
|
|
2326
|
+
reason: "pii_decrypt_failed",
|
|
2327
|
+
error
|
|
2328
|
+
});
|
|
2329
|
+
},
|
|
2330
|
+
drain: () => findings
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2260
2333
|
var PASS_FACTORIES = {
|
|
2261
2334
|
schema: make_schema_pass,
|
|
2262
2335
|
"deprecated-load": make_deprecated_load_pass,
|
|
@@ -2266,7 +2339,8 @@ var PASS_FACTORIES = {
|
|
|
2266
2339
|
"snapshot-drift": make_snapshot_drift_pass,
|
|
2267
2340
|
"routing-health": make_routing_health_pass,
|
|
2268
2341
|
"correlation-gaps": make_correlation_gaps_pass,
|
|
2269
|
-
"clock-anomalies": make_clock_anomalies_pass
|
|
2342
|
+
"clock-anomalies": make_clock_anomalies_pass,
|
|
2343
|
+
"unreadable-events": make_unreadable_events_pass
|
|
2270
2344
|
};
|
|
2271
2345
|
|
|
2272
2346
|
// src/internal/autoclose-policy.ts
|
|
@@ -3397,7 +3471,19 @@ var CorrelateCycle = class {
|
|
|
3397
3471
|
}
|
|
3398
3472
|
}
|
|
3399
3473
|
},
|
|
3400
|
-
|
|
3474
|
+
// Decline the sensitive payload: this scan reads `name`/`stream`/`id`
|
|
3475
|
+
// to resolve reaction targets and throws the rest away. Asking for a
|
|
3476
|
+
// payload it discards made one unreadable row stop EVERY stream's
|
|
3477
|
+
// reactions — correlate is the sole producer of the work mark, so a
|
|
3478
|
+
// decrypt failure here strands the whole app with nothing blocked and
|
|
3479
|
+
// nothing naming the row (#1675).
|
|
3480
|
+
//
|
|
3481
|
+
// A dynamic `.to(event => …)` resolver therefore sees `pii: null`.
|
|
3482
|
+
// That is deliberate and is itself a fix: correlate runs actor-less,
|
|
3483
|
+
// so handing a resolver decrypted plaintext is un-gated disclosure —
|
|
3484
|
+
// the defect #1673 corrected on the drain return and #1277 before it.
|
|
3485
|
+
// `data` already carries no sensitive keys; nothing merges them here.
|
|
3486
|
+
{ ...query, after, with_pii: false }
|
|
3401
3487
|
);
|
|
3402
3488
|
const streams = [];
|
|
3403
3489
|
for (const [stream, entry] of correlated) {
|
|
@@ -3739,12 +3825,23 @@ async function fetch(leased, eventLimit) {
|
|
|
3739
3825
|
leased.map(async ({ stream, source, at, lagging }) => {
|
|
3740
3826
|
const events = [];
|
|
3741
3827
|
const stream_exact = source !== void 0 && is_literal_source(source) ? true : void 0;
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3828
|
+
try {
|
|
3829
|
+
await store2().query((e) => events.push(e), {
|
|
3830
|
+
stream: source,
|
|
3831
|
+
stream_exact,
|
|
3832
|
+
after: at,
|
|
3833
|
+
limit: eventLimit
|
|
3834
|
+
});
|
|
3835
|
+
} catch (error) {
|
|
3836
|
+
return {
|
|
3837
|
+
stream,
|
|
3838
|
+
source,
|
|
3839
|
+
at,
|
|
3840
|
+
lagging,
|
|
3841
|
+
events: [],
|
|
3842
|
+
error: error?.message ?? String(error)
|
|
3843
|
+
};
|
|
3844
|
+
}
|
|
3748
3845
|
return { stream, source, at, lagging, events };
|
|
3749
3846
|
})
|
|
3750
3847
|
);
|
|
@@ -4399,6 +4496,17 @@ async function run_drain_cycle(ops, registry, batch_handlers, misrouted, store_f
|
|
|
4399
4496
|
const handled = await Promise.all(
|
|
4400
4497
|
leased.map((lease) => {
|
|
4401
4498
|
const entry = fetch_map.get(lease.stream);
|
|
4499
|
+
if (entry.fetch.error !== void 0) {
|
|
4500
|
+
const error = `Fetch failed for ${lease.stream}: ${entry.fetch.error}`;
|
|
4501
|
+
log().error(error);
|
|
4502
|
+
const failed = {
|
|
4503
|
+
lease,
|
|
4504
|
+
handled: 0,
|
|
4505
|
+
acked_at: lease.at,
|
|
4506
|
+
error
|
|
4507
|
+
};
|
|
4508
|
+
return Promise.resolve(failed);
|
|
4509
|
+
}
|
|
4402
4510
|
const at = entry.fetch.events.at(-1)?.id || fetch_window_at;
|
|
4403
4511
|
const { payloads } = entry;
|
|
4404
4512
|
const exhausted = budget_exhausted(
|