@rotorsoft/act 1.9.0 → 1.10.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/act.d.ts +34 -2
- 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 +31 -1
- package/dist/@types/builders/state-builder.d.ts.map +1 -1
- package/dist/@types/internal/event-sourcing.d.ts +7 -2
- package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
- package/dist/@types/internal/index.d.ts +1 -0
- package/dist/@types/internal/index.d.ts.map +1 -1
- package/dist/@types/internal/reactions.d.ts +5 -4
- package/dist/@types/internal/reactions.d.ts.map +1 -1
- package/dist/@types/internal/sensitive.d.ts +147 -0
- package/dist/@types/internal/sensitive.d.ts.map +1 -0
- package/dist/@types/internal/tracing.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +57 -0
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/@types/types/registry.d.ts +9 -1
- package/dist/@types/types/registry.d.ts.map +1 -1
- package/dist/@types/types/schemas.d.ts +36 -0
- package/dist/@types/types/schemas.d.ts.map +1 -1
- package/dist/{chunk-F4S2JOPN.js → chunk-3ZTFNAY7.js} +2 -2
- package/dist/chunk-XSBT63QX.js +267 -0
- package/dist/chunk-XSBT63QX.js.map +1 -0
- package/dist/index.cjs +291 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +155 -32
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +62 -56
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js +11 -11
- package/dist/test/index.js.map +1 -1
- package/dist/types/index.cjs +52 -34
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.js +9 -3
- package/package.json +1 -1
- package/dist/chunk-PMAZTOSO.js +0 -164
- package/dist/chunk-PMAZTOSO.js.map +0 -1
- /package/dist/{chunk-F4S2JOPN.js.map → chunk-3ZTFNAY7.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -51,6 +51,8 @@ __export(index_exports, {
|
|
|
51
51
|
NonRetryableError: () => NonRetryableError,
|
|
52
52
|
PackageSchema: () => PackageSchema,
|
|
53
53
|
QuerySchema: () => QuerySchema,
|
|
54
|
+
REDACTED: () => REDACTED,
|
|
55
|
+
SHREDDED: () => SHREDDED,
|
|
54
56
|
SNAP_EVENT: () => SNAP_EVENT,
|
|
55
57
|
StreamClosedError: () => StreamClosedError,
|
|
56
58
|
TOMBSTONE_EVENT: () => TOMBSTONE_EVENT,
|
|
@@ -67,6 +69,7 @@ __export(index_exports, {
|
|
|
67
69
|
port: () => port,
|
|
68
70
|
projection: () => projection,
|
|
69
71
|
scoped: () => scoped,
|
|
72
|
+
sensitive: () => sensitive,
|
|
70
73
|
sleep: () => sleep,
|
|
71
74
|
slice: () => slice,
|
|
72
75
|
state: () => state,
|
|
@@ -380,55 +383,150 @@ var NonRetryableError = class extends Error {
|
|
|
380
383
|
};
|
|
381
384
|
|
|
382
385
|
// src/utils.ts
|
|
383
|
-
var
|
|
386
|
+
var import_zod4 = require("zod");
|
|
384
387
|
|
|
385
388
|
// src/config.ts
|
|
386
389
|
var fs = __toESM(require("fs"), 1);
|
|
387
|
-
var
|
|
390
|
+
var import_zod3 = require("zod");
|
|
388
391
|
|
|
389
392
|
// src/types/schemas.ts
|
|
393
|
+
var import_zod2 = require("zod");
|
|
394
|
+
|
|
395
|
+
// src/internal/sensitive.ts
|
|
390
396
|
var import_zod = require("zod");
|
|
391
|
-
var
|
|
392
|
-
var
|
|
393
|
-
|
|
394
|
-
|
|
397
|
+
var REDACTED = "[REDACTED]";
|
|
398
|
+
var SHREDDED = "[SHREDDED]";
|
|
399
|
+
var _registry = import_zod.z.registry();
|
|
400
|
+
function is_pii(schema) {
|
|
401
|
+
let cur = schema;
|
|
402
|
+
while (true) {
|
|
403
|
+
if (_registry.has(cur)) return true;
|
|
404
|
+
const inner = cur._def?.innerType;
|
|
405
|
+
if (!inner || inner === cur) return false;
|
|
406
|
+
cur = inner;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function pii_fields(schema) {
|
|
410
|
+
const shape = schema.shape;
|
|
411
|
+
if (!shape || typeof shape !== "object") return [];
|
|
412
|
+
const fields = [];
|
|
413
|
+
for (const key of Object.keys(shape)) {
|
|
414
|
+
if (is_pii(shape[key])) fields.push(key);
|
|
415
|
+
}
|
|
416
|
+
return fields;
|
|
417
|
+
}
|
|
418
|
+
function pii_split(emitted, fields) {
|
|
419
|
+
const rec = emitted.data;
|
|
420
|
+
const clean = {};
|
|
421
|
+
const pii = {};
|
|
422
|
+
for (const k of Object.keys(rec)) {
|
|
423
|
+
if (fields.includes(k)) pii[k] = rec[k];
|
|
424
|
+
else clean[k] = rec[k];
|
|
425
|
+
}
|
|
426
|
+
return { name: emitted.name, data: clean, pii };
|
|
427
|
+
}
|
|
428
|
+
function pii_merge(event, fields) {
|
|
429
|
+
const data = event.data;
|
|
430
|
+
const pii = event.pii;
|
|
431
|
+
if (pii != null) {
|
|
432
|
+
return {
|
|
433
|
+
...event,
|
|
434
|
+
data: { ...data, ...pii }
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
const shredded = { ...data };
|
|
438
|
+
for (const f of fields) shredded[f] = SHREDDED;
|
|
439
|
+
return {
|
|
440
|
+
...event,
|
|
441
|
+
data: shredded
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function pii_gate(event, fields, predicate, actor) {
|
|
445
|
+
const data = event.data;
|
|
446
|
+
if (event.pii == null) {
|
|
447
|
+
const shredded = { ...data };
|
|
448
|
+
for (const f of fields) shredded[f] = SHREDDED;
|
|
449
|
+
return {
|
|
450
|
+
...event,
|
|
451
|
+
data: shredded
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
const allowed = !!actor && !!predicate && predicate(event, actor);
|
|
455
|
+
if (allowed) {
|
|
456
|
+
return {
|
|
457
|
+
...event,
|
|
458
|
+
data: {
|
|
459
|
+
...data,
|
|
460
|
+
...event.pii
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
const redacted = { ...data };
|
|
465
|
+
for (const f of fields) redacted[f] = REDACTED;
|
|
466
|
+
return {
|
|
467
|
+
...event,
|
|
468
|
+
data: redacted
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
function pii_strip(event, fields) {
|
|
472
|
+
const data = event.data;
|
|
473
|
+
const stripped = {};
|
|
474
|
+
for (const k of Object.keys(data)) {
|
|
475
|
+
if (!fields.includes(k)) stripped[k] = data[k];
|
|
476
|
+
}
|
|
477
|
+
const { pii: _drop_pii, ...rest } = event;
|
|
478
|
+
return {
|
|
479
|
+
...rest,
|
|
480
|
+
data: stripped
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// src/types/schemas.ts
|
|
485
|
+
var ZodEmpty = import_zod2.z.record(import_zod2.z.string(), import_zod2.z.never());
|
|
486
|
+
function sensitive(schema) {
|
|
487
|
+
_registry.add(schema, { sensitive: true });
|
|
488
|
+
return schema;
|
|
489
|
+
}
|
|
490
|
+
var ActorSchema = import_zod2.z.object({
|
|
491
|
+
id: import_zod2.z.string(),
|
|
492
|
+
name: import_zod2.z.string()
|
|
395
493
|
}).loose().readonly();
|
|
396
|
-
var TargetSchema =
|
|
397
|
-
stream:
|
|
494
|
+
var TargetSchema = import_zod2.z.object({
|
|
495
|
+
stream: import_zod2.z.string(),
|
|
398
496
|
actor: ActorSchema,
|
|
399
|
-
expectedVersion:
|
|
497
|
+
expectedVersion: import_zod2.z.number().optional()
|
|
400
498
|
}).loose().readonly();
|
|
401
|
-
var CausationEventSchema =
|
|
402
|
-
id:
|
|
403
|
-
name:
|
|
404
|
-
stream:
|
|
499
|
+
var CausationEventSchema = import_zod2.z.object({
|
|
500
|
+
id: import_zod2.z.number(),
|
|
501
|
+
name: import_zod2.z.string(),
|
|
502
|
+
stream: import_zod2.z.string()
|
|
405
503
|
});
|
|
406
|
-
var EventMetaSchema =
|
|
407
|
-
correlation:
|
|
408
|
-
causation:
|
|
409
|
-
action: TargetSchema.and(
|
|
504
|
+
var EventMetaSchema = import_zod2.z.object({
|
|
505
|
+
correlation: import_zod2.z.string(),
|
|
506
|
+
causation: import_zod2.z.object({
|
|
507
|
+
action: TargetSchema.and(import_zod2.z.object({ name: import_zod2.z.string() })).optional(),
|
|
410
508
|
event: CausationEventSchema.optional()
|
|
411
509
|
})
|
|
412
510
|
}).readonly();
|
|
413
|
-
var CommittedMetaSchema =
|
|
414
|
-
id:
|
|
415
|
-
stream:
|
|
416
|
-
version:
|
|
417
|
-
created:
|
|
511
|
+
var CommittedMetaSchema = import_zod2.z.object({
|
|
512
|
+
id: import_zod2.z.number(),
|
|
513
|
+
stream: import_zod2.z.string(),
|
|
514
|
+
version: import_zod2.z.number(),
|
|
515
|
+
created: import_zod2.z.date(),
|
|
418
516
|
meta: EventMetaSchema
|
|
419
517
|
}).readonly();
|
|
420
|
-
var QuerySchema =
|
|
421
|
-
stream:
|
|
422
|
-
names:
|
|
423
|
-
before:
|
|
424
|
-
after:
|
|
425
|
-
limit:
|
|
426
|
-
created_before:
|
|
427
|
-
created_after:
|
|
428
|
-
backward:
|
|
429
|
-
correlation:
|
|
430
|
-
with_snaps:
|
|
431
|
-
stream_exact:
|
|
518
|
+
var QuerySchema = import_zod2.z.object({
|
|
519
|
+
stream: import_zod2.z.string().optional(),
|
|
520
|
+
names: import_zod2.z.string().array().optional(),
|
|
521
|
+
before: import_zod2.z.number().optional(),
|
|
522
|
+
after: import_zod2.z.number().optional(),
|
|
523
|
+
limit: import_zod2.z.number().optional(),
|
|
524
|
+
created_before: import_zod2.z.date().optional(),
|
|
525
|
+
created_after: import_zod2.z.date().optional(),
|
|
526
|
+
backward: import_zod2.z.boolean().optional(),
|
|
527
|
+
correlation: import_zod2.z.string().optional(),
|
|
528
|
+
with_snaps: import_zod2.z.boolean().optional(),
|
|
529
|
+
stream_exact: import_zod2.z.boolean().optional()
|
|
432
530
|
}).readonly();
|
|
433
531
|
|
|
434
532
|
// src/types/index.ts
|
|
@@ -448,13 +546,13 @@ var LogLevels = [
|
|
|
448
546
|
];
|
|
449
547
|
|
|
450
548
|
// src/config.ts
|
|
451
|
-
var PackageSchema =
|
|
452
|
-
name:
|
|
453
|
-
version:
|
|
454
|
-
description:
|
|
455
|
-
author:
|
|
456
|
-
license:
|
|
457
|
-
dependencies:
|
|
549
|
+
var PackageSchema = import_zod3.z.object({
|
|
550
|
+
name: import_zod3.z.string().min(1),
|
|
551
|
+
version: import_zod3.z.string().min(1),
|
|
552
|
+
description: import_zod3.z.string().min(1).optional(),
|
|
553
|
+
author: import_zod3.z.object({ name: import_zod3.z.string().min(1), email: import_zod3.z.string().optional() }).optional().or(import_zod3.z.string().min(1)).optional(),
|
|
554
|
+
license: import_zod3.z.string().min(1).optional(),
|
|
555
|
+
dependencies: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.string()).optional()
|
|
458
556
|
});
|
|
459
557
|
var FALLBACK_PACKAGE = {
|
|
460
558
|
name: "act-fallback",
|
|
@@ -472,10 +570,10 @@ var getPackage = () => {
|
|
|
472
570
|
};
|
|
473
571
|
var pkgLoadError;
|
|
474
572
|
var BaseSchema = PackageSchema.extend({
|
|
475
|
-
env:
|
|
476
|
-
logLevel:
|
|
477
|
-
logSingleLine:
|
|
478
|
-
sleepMs:
|
|
573
|
+
env: import_zod3.z.enum(Environments),
|
|
574
|
+
logLevel: import_zod3.z.enum(LogLevels),
|
|
575
|
+
logSingleLine: import_zod3.z.boolean(),
|
|
576
|
+
sleepMs: import_zod3.z.number().int().min(0).max(5e3)
|
|
479
577
|
});
|
|
480
578
|
var { NODE_ENV, LOG_LEVEL, LOG_SINGLE_LINE, SLEEP_MS } = process.env;
|
|
481
579
|
var env = NODE_ENV || "development";
|
|
@@ -506,8 +604,8 @@ var validate = (target, payload, schema) => {
|
|
|
506
604
|
try {
|
|
507
605
|
return schema ? schema.parse(payload) : payload;
|
|
508
606
|
} catch (error) {
|
|
509
|
-
if (error instanceof
|
|
510
|
-
throw new ValidationError(target, payload, (0,
|
|
607
|
+
if (error instanceof import_zod4.ZodError) {
|
|
608
|
+
throw new ValidationError(target, payload, (0, import_zod4.prettifyError)(error));
|
|
511
609
|
}
|
|
512
610
|
throw new ValidationError(target, payload, error);
|
|
513
611
|
}
|
|
@@ -2334,7 +2432,7 @@ async function scan(source, opts = {}, callback) {
|
|
|
2334
2432
|
}
|
|
2335
2433
|
};
|
|
2336
2434
|
}
|
|
2337
|
-
async function load(me, stream, callback, asOf) {
|
|
2435
|
+
async function load(me, stream, callback, asOf, actor) {
|
|
2338
2436
|
const timeTravel = !!asOf && Object.values(asOf).some((v) => v !== void 0);
|
|
2339
2437
|
const cached = timeTravel ? void 0 : await cache2().get(stream);
|
|
2340
2438
|
const cache_hit = !!cached;
|
|
@@ -2346,15 +2444,16 @@ async function load(me, stream, callback, asOf) {
|
|
|
2346
2444
|
let event;
|
|
2347
2445
|
await store2().query(
|
|
2348
2446
|
(e) => {
|
|
2349
|
-
event = e;
|
|
2350
2447
|
version = e.version;
|
|
2448
|
+
const typed = e;
|
|
2449
|
+
event = me.view(typed, actor);
|
|
2351
2450
|
if (e.name === SNAP_EVENT) {
|
|
2352
2451
|
state2 = e.data;
|
|
2353
2452
|
snaps++;
|
|
2354
2453
|
patches = 0;
|
|
2355
2454
|
replayed++;
|
|
2356
2455
|
} else if (me.patch[e.name]) {
|
|
2357
|
-
state2 = (0, import_act_patch.patch)(state2, me.patch[e.name](
|
|
2456
|
+
state2 = (0, import_act_patch.patch)(state2, me.patch[e.name](typed, state2));
|
|
2358
2457
|
patches++;
|
|
2359
2458
|
replayed++;
|
|
2360
2459
|
} else if (e.name !== TOMBSTONE_EVENT) {
|
|
@@ -2397,7 +2496,13 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
2397
2496
|
const maxRetries = opts?.maxRetries ?? 0;
|
|
2398
2497
|
for (let attempt = 0; ; attempt++) {
|
|
2399
2498
|
try {
|
|
2400
|
-
const snapshot = await load(
|
|
2499
|
+
const snapshot = await load(
|
|
2500
|
+
me,
|
|
2501
|
+
stream,
|
|
2502
|
+
void 0,
|
|
2503
|
+
void 0,
|
|
2504
|
+
target.actor
|
|
2505
|
+
);
|
|
2401
2506
|
if (snapshot.event?.name === TOMBSTONE_EVENT)
|
|
2402
2507
|
throw new StreamClosedError(stream);
|
|
2403
2508
|
const expected = expectedVersion ?? snapshot.event?.version;
|
|
@@ -2434,10 +2539,10 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
2434
2539
|
}
|
|
2435
2540
|
}
|
|
2436
2541
|
}
|
|
2437
|
-
const emitted = tuples.map(([name, data]) =>
|
|
2438
|
-
name,
|
|
2439
|
-
|
|
2440
|
-
})
|
|
2542
|
+
const emitted = tuples.map(([name, data]) => {
|
|
2543
|
+
const validated2 = skipValidation ? data : validate(name, data, me.events[name]);
|
|
2544
|
+
return me.message({ name, data: validated2 });
|
|
2545
|
+
});
|
|
2441
2546
|
const meta = {
|
|
2442
2547
|
correlation: reactingTo?.meta.correlation || correlator({
|
|
2443
2548
|
action: action2,
|
|
@@ -2449,8 +2554,8 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
2449
2554
|
action: {
|
|
2450
2555
|
name: action2,
|
|
2451
2556
|
...target
|
|
2452
|
-
// payload intentionally omitted
|
|
2453
|
-
//
|
|
2557
|
+
// payload intentionally omitted from causation metadata —
|
|
2558
|
+
// callers correlate via the correlation id when they need it.
|
|
2454
2559
|
},
|
|
2455
2560
|
event: reactingTo ? {
|
|
2456
2561
|
id: reactingTo.id,
|
|
@@ -2483,7 +2588,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
2483
2588
|
state2 = (0, import_act_patch.patch)(state2, p);
|
|
2484
2589
|
patches++;
|
|
2485
2590
|
return {
|
|
2486
|
-
event,
|
|
2591
|
+
event: me.view(event, target.actor),
|
|
2487
2592
|
state: state2,
|
|
2488
2593
|
version: event.version,
|
|
2489
2594
|
patches,
|
|
@@ -2568,7 +2673,7 @@ var traced = (inner, exit, entry) => (async (...args) => {
|
|
|
2568
2673
|
return result;
|
|
2569
2674
|
});
|
|
2570
2675
|
function buildEs(logger, correlator = defaultCorrelator) {
|
|
2571
|
-
const
|
|
2676
|
+
const bound_action = (me, actionName, target, payload, reactingTo, skipValidation = false) => action(
|
|
2572
2677
|
me,
|
|
2573
2678
|
actionName,
|
|
2574
2679
|
target,
|
|
@@ -2581,7 +2686,7 @@ function buildEs(logger, correlator = defaultCorrelator) {
|
|
|
2581
2686
|
return {
|
|
2582
2687
|
snap,
|
|
2583
2688
|
load,
|
|
2584
|
-
action:
|
|
2689
|
+
action: bound_action,
|
|
2585
2690
|
tombstone
|
|
2586
2691
|
};
|
|
2587
2692
|
}
|
|
@@ -2611,7 +2716,7 @@ function buildEs(logger, correlator = defaultCorrelator) {
|
|
|
2611
2716
|
);
|
|
2612
2717
|
}),
|
|
2613
2718
|
action: traced(
|
|
2614
|
-
|
|
2719
|
+
bound_action,
|
|
2615
2720
|
(snapshots, _me, _action, target) => {
|
|
2616
2721
|
const committed = snapshots.filter((s) => s.event);
|
|
2617
2722
|
if (committed.length) {
|
|
@@ -2904,7 +3009,7 @@ var DrainController = class {
|
|
|
2904
3009
|
};
|
|
2905
3010
|
|
|
2906
3011
|
// src/internal/merge.ts
|
|
2907
|
-
var
|
|
3012
|
+
var import_zod5 = require("zod");
|
|
2908
3013
|
function baseTypeName(zodType) {
|
|
2909
3014
|
let t = zodType;
|
|
2910
3015
|
while (typeof t.unwrap === "function") {
|
|
@@ -2913,7 +3018,7 @@ function baseTypeName(zodType) {
|
|
|
2913
3018
|
return t.constructor.name;
|
|
2914
3019
|
}
|
|
2915
3020
|
function mergeSchemas(existing, incoming, stateName) {
|
|
2916
|
-
if (existing instanceof
|
|
3021
|
+
if (existing instanceof import_zod5.ZodObject && incoming instanceof import_zod5.ZodObject) {
|
|
2917
3022
|
const existingShape = existing.shape;
|
|
2918
3023
|
const incomingShape = incoming.shape;
|
|
2919
3024
|
for (const key of Object.keys(incomingShape)) {
|
|
@@ -3068,7 +3173,14 @@ function finalize(lease, handled, at, error, options, logger, failed_at) {
|
|
|
3068
3173
|
};
|
|
3069
3174
|
}
|
|
3070
3175
|
function buildHandle(deps) {
|
|
3071
|
-
const {
|
|
3176
|
+
const {
|
|
3177
|
+
logger,
|
|
3178
|
+
bound_do,
|
|
3179
|
+
bound_load,
|
|
3180
|
+
bound_query,
|
|
3181
|
+
bound_query_array,
|
|
3182
|
+
bound_forget
|
|
3183
|
+
} = deps;
|
|
3072
3184
|
return async (lease, payloads) => {
|
|
3073
3185
|
if (payloads.length === 0) return { lease, handled: 0, acked_at: lease.at };
|
|
3074
3186
|
const stream = lease.stream;
|
|
@@ -3077,14 +3189,15 @@ function buildHandle(deps) {
|
|
|
3077
3189
|
if (lease.retry > 0)
|
|
3078
3190
|
logger.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
|
|
3079
3191
|
const scopedApp = {
|
|
3080
|
-
do:
|
|
3081
|
-
load:
|
|
3082
|
-
query:
|
|
3083
|
-
query_array:
|
|
3192
|
+
do: bound_do,
|
|
3193
|
+
load: bound_load,
|
|
3194
|
+
query: bound_query,
|
|
3195
|
+
query_array: bound_query_array,
|
|
3196
|
+
forget: bound_forget
|
|
3084
3197
|
};
|
|
3085
3198
|
for (const payload of payloads) {
|
|
3086
3199
|
const { event, handler } = payload;
|
|
3087
|
-
scopedApp.do = (action2, target, actionPayload, reactingTo, skipValidation) =>
|
|
3200
|
+
scopedApp.do = (action2, target, actionPayload, reactingTo, skipValidation) => bound_do(
|
|
3088
3201
|
action2,
|
|
3089
3202
|
target,
|
|
3090
3203
|
actionPayload,
|
|
@@ -3113,7 +3226,9 @@ function buildHandle(deps) {
|
|
|
3113
3226
|
function buildHandleBatch(logger) {
|
|
3114
3227
|
return async (lease, payloads, batchHandler) => {
|
|
3115
3228
|
const stream = lease.stream;
|
|
3116
|
-
const events = payloads.map(
|
|
3229
|
+
const events = payloads.map(
|
|
3230
|
+
(p) => p.event
|
|
3231
|
+
);
|
|
3117
3232
|
const options = payloads[0].options;
|
|
3118
3233
|
if (lease.retry > 0)
|
|
3119
3234
|
logger.warn(`Retrying batch ${stream}@${events[0].id} (${lease.retry}).`);
|
|
@@ -3296,6 +3411,7 @@ var Act = class {
|
|
|
3296
3411
|
_bound_load = this.load.bind(this);
|
|
3297
3412
|
_bound_query = this.query.bind(this);
|
|
3298
3413
|
_bound_query_array = this.query_array.bind(this);
|
|
3414
|
+
_bound_forget = this.forget.bind(this);
|
|
3299
3415
|
/** Reaction dispatchers built once and handed to runDrainCycle each cycle. */
|
|
3300
3416
|
_handle;
|
|
3301
3417
|
_handle_batch;
|
|
@@ -3341,10 +3457,11 @@ var Act = class {
|
|
|
3341
3457
|
this._cd = buildDrain(this._logger);
|
|
3342
3458
|
this._handle = buildHandle({
|
|
3343
3459
|
logger: this._logger,
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3460
|
+
bound_do: this._bound_do,
|
|
3461
|
+
bound_load: this._bound_load,
|
|
3462
|
+
bound_query: this._bound_query,
|
|
3463
|
+
bound_query_array: this._bound_query_array,
|
|
3464
|
+
bound_forget: this._bound_forget
|
|
3348
3465
|
});
|
|
3349
3466
|
this._handle_batch = buildHandleBatch(this._logger);
|
|
3350
3467
|
const {
|
|
@@ -3576,7 +3693,7 @@ var Act = class {
|
|
|
3576
3693
|
return snapshots;
|
|
3577
3694
|
});
|
|
3578
3695
|
}
|
|
3579
|
-
async load(stateOrName, stream, callback, asOf) {
|
|
3696
|
+
async load(stateOrName, stream, callback, asOf, actor) {
|
|
3580
3697
|
return this._scoped(async () => {
|
|
3581
3698
|
let merged;
|
|
3582
3699
|
if (typeof stateOrName === "string") {
|
|
@@ -3586,7 +3703,7 @@ var Act = class {
|
|
|
3586
3703
|
} else {
|
|
3587
3704
|
merged = this._states.get(stateOrName.name) || stateOrName;
|
|
3588
3705
|
}
|
|
3589
|
-
return await this._es.load(merged, stream, callback, asOf);
|
|
3706
|
+
return await this._es.load(merged, stream, callback, asOf, actor);
|
|
3590
3707
|
});
|
|
3591
3708
|
}
|
|
3592
3709
|
/**
|
|
@@ -3680,6 +3797,34 @@ var Act = class {
|
|
|
3680
3797
|
return events;
|
|
3681
3798
|
});
|
|
3682
3799
|
}
|
|
3800
|
+
/**
|
|
3801
|
+
* Wipe the sensitive-data payload for every event on the stream — see
|
|
3802
|
+
* {@link IAct.forget}. Application-level half of #566.
|
|
3803
|
+
*
|
|
3804
|
+
* Throws on adapters without `Store.forget_pii`, invalidates the cache
|
|
3805
|
+
* entry for the stream, emits the `forgotten` lifecycle event with the
|
|
3806
|
+
* row count. Idempotent: a second call returns `{eventCount: 0}` and
|
|
3807
|
+
* does NOT re-emit.
|
|
3808
|
+
*
|
|
3809
|
+
* @param stream - Target stream.
|
|
3810
|
+
* @returns `{eventCount}` — number of events whose PII column was wiped.
|
|
3811
|
+
*/
|
|
3812
|
+
async forget(stream) {
|
|
3813
|
+
return this._scoped(async () => {
|
|
3814
|
+
const s = store2();
|
|
3815
|
+
if (!s.forget_pii) {
|
|
3816
|
+
throw new Error(
|
|
3817
|
+
`Store does not implement forget_pii \u2014 adapter cannot comply with sensitive-data erasure. Use an adapter that declares pii_isolation: true (e.g. @rotorsoft/act on the in-memory store).`
|
|
3818
|
+
);
|
|
3819
|
+
}
|
|
3820
|
+
const eventCount = await s.forget_pii(stream);
|
|
3821
|
+
await cache2().invalidate(stream);
|
|
3822
|
+
if (eventCount > 0) {
|
|
3823
|
+
this.emit("forgotten", { stream, at: /* @__PURE__ */ new Date(), eventCount });
|
|
3824
|
+
}
|
|
3825
|
+
return { eventCount };
|
|
3826
|
+
});
|
|
3827
|
+
}
|
|
3683
3828
|
/**
|
|
3684
3829
|
* Processes pending reactions by draining uncommitted events from the event store.
|
|
3685
3830
|
*
|
|
@@ -4280,9 +4425,13 @@ function validateLaneReferences(registry, lanes) {
|
|
|
4280
4425
|
}
|
|
4281
4426
|
function act() {
|
|
4282
4427
|
const states = /* @__PURE__ */ new Map();
|
|
4428
|
+
const _sf = /* @__PURE__ */ new Map();
|
|
4429
|
+
const _dp = /* @__PURE__ */ new Map();
|
|
4283
4430
|
const registry = {
|
|
4284
4431
|
actions: {},
|
|
4285
|
-
events: {}
|
|
4432
|
+
events: {},
|
|
4433
|
+
sensitive_fields: (eventName) => _sf.get(eventName) ?? [],
|
|
4434
|
+
disclosure_predicate: (stateName) => _dp.get(stateName) ?? null
|
|
4286
4435
|
};
|
|
4287
4436
|
const pendingProjections = [];
|
|
4288
4437
|
const batchHandlers = /* @__PURE__ */ new Map();
|
|
@@ -4393,6 +4542,58 @@ function act() {
|
|
|
4393
4542
|
}
|
|
4394
4543
|
finalizeDeprecations();
|
|
4395
4544
|
validateLaneReferences(registry, lanes);
|
|
4545
|
+
for (const [eventName, reg] of Object.entries(
|
|
4546
|
+
registry.events
|
|
4547
|
+
)) {
|
|
4548
|
+
const fields = pii_fields(reg.schema);
|
|
4549
|
+
if (fields.length === 0) continue;
|
|
4550
|
+
_sf.set(eventName, fields);
|
|
4551
|
+
for (const [name, reaction] of reg.reactions) {
|
|
4552
|
+
const inner = reaction.handler;
|
|
4553
|
+
const wrapped = (event, stream, app) => inner(pii_strip(event, fields), stream, app);
|
|
4554
|
+
Object.defineProperty(wrapped, "name", { value: inner.name });
|
|
4555
|
+
reaction.handler = wrapped;
|
|
4556
|
+
reg.reactions.set(name, reaction);
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
for (const state2 of states.values()) {
|
|
4560
|
+
if (state2.disclose) _dp.set(state2.name, state2.disclose);
|
|
4561
|
+
const fields_by_event = /* @__PURE__ */ new Map();
|
|
4562
|
+
for (const eventName of Object.keys(state2.events)) {
|
|
4563
|
+
const fields = _sf.get(eventName);
|
|
4564
|
+
if (fields) fields_by_event.set(eventName, fields);
|
|
4565
|
+
}
|
|
4566
|
+
if (fields_by_event.size === 0) continue;
|
|
4567
|
+
if (state2.snap) {
|
|
4568
|
+
const offending = [...fields_by_event.keys()];
|
|
4569
|
+
throw new Error(
|
|
4570
|
+
`State "${state2.name}" cannot snapshot \u2014 events {${offending.join(", ")}} carry sensitive fields. Snapshots write derived state into __snapshot__.data, which forget_pii cannot reach. Remove .snap() or remove sensitive(...) markers.`
|
|
4571
|
+
);
|
|
4572
|
+
}
|
|
4573
|
+
const disclose = state2.disclose ?? null;
|
|
4574
|
+
state2.view = (event, actor) => {
|
|
4575
|
+
const fields = fields_by_event.get(event.name);
|
|
4576
|
+
return fields ? pii_gate(event, fields, disclose, actor) : event;
|
|
4577
|
+
};
|
|
4578
|
+
state2.message = (validated) => {
|
|
4579
|
+
const fields = fields_by_event.get(validated.name);
|
|
4580
|
+
return fields ? pii_split(validated, fields) : validated;
|
|
4581
|
+
};
|
|
4582
|
+
for (const [eventName, fields] of fields_by_event) {
|
|
4583
|
+
const original = state2.patch[eventName];
|
|
4584
|
+
state2.patch[eventName] = (event, s) => original(pii_merge(event, fields), s);
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
for (const [target, original] of batchHandlers) {
|
|
4588
|
+
const wrapped = async (events, stream) => {
|
|
4589
|
+
const stripped = events.map((e) => {
|
|
4590
|
+
const f = _sf.get(e.name);
|
|
4591
|
+
return f ? pii_strip(e, f) : e;
|
|
4592
|
+
});
|
|
4593
|
+
return original(stripped, stream);
|
|
4594
|
+
};
|
|
4595
|
+
batchHandlers.set(target, wrapped);
|
|
4596
|
+
}
|
|
4396
4597
|
_built = true;
|
|
4397
4598
|
}
|
|
4398
4599
|
return new Act(
|
|
@@ -4560,7 +4761,12 @@ function state(entry) {
|
|
|
4560
4761
|
name,
|
|
4561
4762
|
init,
|
|
4562
4763
|
patch: defaultPatch,
|
|
4563
|
-
on: {}
|
|
4764
|
+
on: {},
|
|
4765
|
+
// Step delegates initialized as identity. `act().build()`
|
|
4766
|
+
// overrides on states with `sensitive(...)` events to bake in
|
|
4767
|
+
// the gate / split.
|
|
4768
|
+
view: (event) => event,
|
|
4769
|
+
message: (validated) => validated
|
|
4564
4770
|
};
|
|
4565
4771
|
const builder = action_builder(internal);
|
|
4566
4772
|
return Object.assign(builder, {
|
|
@@ -4612,6 +4818,10 @@ function action_builder(state2) {
|
|
|
4612
4818
|
internal.snap = snap2;
|
|
4613
4819
|
return builder;
|
|
4614
4820
|
},
|
|
4821
|
+
discloses(disclose) {
|
|
4822
|
+
internal.disclose = disclose;
|
|
4823
|
+
return builder;
|
|
4824
|
+
},
|
|
4615
4825
|
build() {
|
|
4616
4826
|
return internal;
|
|
4617
4827
|
}
|
|
@@ -4802,6 +5012,8 @@ function writeLine(writer, line) {
|
|
|
4802
5012
|
NonRetryableError,
|
|
4803
5013
|
PackageSchema,
|
|
4804
5014
|
QuerySchema,
|
|
5015
|
+
REDACTED,
|
|
5016
|
+
SHREDDED,
|
|
4805
5017
|
SNAP_EVENT,
|
|
4806
5018
|
StreamClosedError,
|
|
4807
5019
|
TOMBSTONE_EVENT,
|
|
@@ -4818,6 +5030,7 @@ function writeLine(writer, line) {
|
|
|
4818
5030
|
port,
|
|
4819
5031
|
projection,
|
|
4820
5032
|
scoped,
|
|
5033
|
+
sensitive,
|
|
4821
5034
|
sleep,
|
|
4822
5035
|
slice,
|
|
4823
5036
|
state,
|