@rotorsoft/act 1.31.5 → 1.31.7

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/index.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  sleep,
25
25
  store,
26
26
  validate
27
- } from "./chunk-GQYI2NMQ.js";
27
+ } from "./chunk-IO2OOCOR.js";
28
28
  import {
29
29
  ActorSchema,
30
30
  CausationEventSchema,
@@ -45,12 +45,13 @@ import {
45
45
  TargetSchema,
46
46
  ValidationError,
47
47
  ZodEmpty,
48
+ is_pii,
48
49
  make_gate,
49
50
  pii_fields,
50
51
  pii_split,
51
52
  pii_strip,
52
53
  sensitive
53
- } from "./chunk-TOJ5DZ5B.js";
54
+ } from "./chunk-D5MBFIKL.js";
54
55
 
55
56
  // src/signals.ts
56
57
  process.once("SIGINT", async (arg) => {
@@ -3875,10 +3876,9 @@ var Act = class {
3875
3876
  async query_array(query) {
3876
3877
  return this._scoped(async () => {
3877
3878
  const events = [];
3878
- await store().query(
3879
- (e) => events.push(this.registry.query_gate(e.name)(e)),
3880
- query
3881
- );
3879
+ await store().query((e) => {
3880
+ events.push(this.registry.query_gate(e.name)(e));
3881
+ }, query);
3882
3882
  return events;
3883
3883
  });
3884
3884
  }
@@ -4788,6 +4788,144 @@ function reaction_on(event, events, builder) {
4788
4788
  };
4789
4789
  }
4790
4790
 
4791
+ // src/builders/event-builder.ts
4792
+ import { z as z4 } from "zod";
4793
+ var def_of = (schema) => schema._zod?.def ?? schema.def;
4794
+ function to_read_schema(schema, found) {
4795
+ const def = def_of(schema);
4796
+ if (!def) return schema;
4797
+ switch (def.type) {
4798
+ case "date":
4799
+ found.date = true;
4800
+ return z4.coerce.date();
4801
+ case "object": {
4802
+ const shape = def.shape;
4803
+ if (!shape) return schema;
4804
+ const next = {};
4805
+ for (const [key, inner] of Object.entries(shape))
4806
+ next[key] = to_read_schema(inner, found);
4807
+ return z4.looseObject(next);
4808
+ }
4809
+ case "array":
4810
+ return z4.array(to_read_schema(def.element, found));
4811
+ case "record":
4812
+ return z4.record(
4813
+ z4.string(),
4814
+ to_read_schema(def.valueType, found)
4815
+ );
4816
+ case "union":
4817
+ return z4.union(
4818
+ def.options.map(
4819
+ (o) => to_read_schema(o, found)
4820
+ )
4821
+ );
4822
+ case "optional":
4823
+ return to_read_schema(def.innerType, found).optional();
4824
+ case "nullable":
4825
+ return to_read_schema(def.innerType, found).nullable();
4826
+ default:
4827
+ return schema;
4828
+ }
4829
+ }
4830
+ function event_tags(schema) {
4831
+ const sensitive2 = [];
4832
+ const collect = (node) => {
4833
+ const shape = def_of(node)?.shape;
4834
+ if (shape) {
4835
+ for (const key of Object.keys(shape))
4836
+ if (is_pii(shape[key])) sensitive2.push(key);
4837
+ return;
4838
+ }
4839
+ const options = node.options;
4840
+ if (Array.isArray(options)) for (const option of options) collect(option);
4841
+ };
4842
+ collect(schema);
4843
+ const found = { date: false };
4844
+ const read_schema = to_read_schema(schema, found);
4845
+ return {
4846
+ sensitive: [...new Set(sensitive2)],
4847
+ parse: found.date ? (data) => read_schema.parse(data) : void 0
4848
+ };
4849
+ }
4850
+ function make_event_reader(tags, disclosure, predicate = null) {
4851
+ const { sensitive: sensitive2, parse: parse2 } = tags;
4852
+ if (!parse2 && sensitive2.length === 0) return void 0;
4853
+ const gate = sensitive2.length === 0 ? IDENTITY_GATE : disclosure === "strip" ? ((event) => pii_strip(event, sensitive2)) : make_gate(sensitive2, predicate);
4854
+ if (!parse2) return gate;
4855
+ return ((event, actor) => gate({ ...event, data: parse2(event.data) }, actor));
4856
+ }
4857
+ function build_events(registry, states, lanes, owners) {
4858
+ const declared = /* @__PURE__ */ new Set([DEFAULT_LANE, ...lanes.map((l) => l.name)]);
4859
+ const lane_errors = [];
4860
+ const tags = /* @__PURE__ */ new Map();
4861
+ const sensitive2 = /* @__PURE__ */ new Map();
4862
+ const query_readers = /* @__PURE__ */ new Map();
4863
+ const handler_readers = /* @__PURE__ */ new Map();
4864
+ for (const [event_name, def] of Object.entries(
4865
+ registry.events
4866
+ )) {
4867
+ for (const [handler_name, reaction] of def.reactions) {
4868
+ if (typeof reaction.resolver === "function") continue;
4869
+ const resolver = reaction.resolver;
4870
+ const claimed = owners.batch_handlers.has(resolver.target) || owners.fold_targets.has(resolver.target);
4871
+ if (claimed && !owners.projection_reactions.has(reaction))
4872
+ throw new Error(
4873
+ `Reaction on target "${resolver.target}" conflicts with the projection that already serves it \u2014 a target is served by one batch handler or one state projection, and a reaction to it would never run`
4874
+ );
4875
+ if (resolver.lane && !declared.has(resolver.lane))
4876
+ lane_errors.push(
4877
+ `Reaction "${handler_name}" on "${event_name}" targets undeclared lane "${resolver.lane}". Declared lanes: ${[...declared].map((l) => `"${l}"`).join(", ")}. Add \`.withLane({ name: "${resolver.lane}", ... })\` to act() or correct the .to() declaration.`
4878
+ );
4879
+ }
4880
+ const event = event_tags(def.schema);
4881
+ tags.set(event_name, event);
4882
+ if (event.sensitive.length > 0) sensitive2.set(event_name, event.sensitive);
4883
+ const query_reader = make_event_reader(event, "redact", null);
4884
+ if (query_reader) query_readers.set(event_name, query_reader);
4885
+ const handler_reader = make_event_reader(event, "strip");
4886
+ if (handler_reader) {
4887
+ handler_readers.set(event_name, handler_reader);
4888
+ for (const [name, reaction] of def.reactions) {
4889
+ const inner = reaction.handler;
4890
+ const wrapped = (evt, stream, app) => inner(handler_reader(evt), stream, app);
4891
+ Object.defineProperty(wrapped, "name", { value: inner.name });
4892
+ reaction.handler = wrapped;
4893
+ def.reactions.set(name, reaction);
4894
+ }
4895
+ }
4896
+ }
4897
+ if (lane_errors.length > 0) throw new Error(lane_errors[0]);
4898
+ for (const state2 of states.values()) {
4899
+ const state_fields = /* @__PURE__ */ new Map();
4900
+ for (const event_name of Object.keys(state2.events)) {
4901
+ const fields = sensitive2.get(event_name);
4902
+ if (fields) state_fields.set(event_name, fields);
4903
+ }
4904
+ if (state_fields.size > 0) {
4905
+ if (state2.snap)
4906
+ throw new Error(
4907
+ `State "${state2.name}" cannot snapshot \u2014 events {${[...state_fields.keys()].join(", ")}} carry sensitive fields. Snapshots write derived state into __snapshot__.data, which forget_pii cannot reach. Remove .snap() or remove sensitive(...) markers.`
4908
+ );
4909
+ state2.pii_aware = true;
4910
+ state2.message = (validated) => {
4911
+ const fields = state_fields.get(validated.name);
4912
+ return fields ? pii_split(validated, fields) : validated;
4913
+ };
4914
+ }
4915
+ const readers = /* @__PURE__ */ new Map();
4916
+ for (const event_name of Object.keys(state2.events)) {
4917
+ const event = tags.get(event_name);
4918
+ const reader = event && make_event_reader(event, "redact", state2.disclose ?? null);
4919
+ if (reader) readers.set(event_name, reader);
4920
+ }
4921
+ const snap2 = make_event_reader(event_tags(state2.state), "redact", null);
4922
+ if (snap2) readers.set(SNAP_EVENT, snap2);
4923
+ if (readers.size === 0) continue;
4924
+ state2.view = (event, actor) => (readers.get(event.name) ?? IDENTITY_GATE)(event, actor);
4925
+ }
4926
+ return { sensitive: sensitive2, query_readers, handler_readers };
4927
+ }
4928
+
4791
4929
  // src/builders/act-builder.ts
4792
4930
  function register_batch_handler(proj, batch_handlers) {
4793
4931
  if (!proj.batchHandler || !proj.target) return;
@@ -4799,25 +4937,10 @@ function register_batch_handler(proj, batch_handlers) {
4799
4937
  }
4800
4938
  batch_handlers.set(proj.target, proj.batchHandler);
4801
4939
  }
4802
- function validate_lane_references(registry, lanes) {
4803
- const declared = /* @__PURE__ */ new Set([DEFAULT_LANE, ...lanes.map((l) => l.name)]);
4804
- for (const [event_name, def] of Object.entries(registry.events)) {
4805
- const entry = def;
4806
- for (const [handlerName, reaction] of entry.reactions) {
4807
- const resolver = reaction.resolver;
4808
- if (typeof resolver === "function") continue;
4809
- const lane = resolver.lane;
4810
- if (lane && !declared.has(lane)) {
4811
- throw new Error(
4812
- `Reaction "${handlerName}" on "${event_name}" targets undeclared lane "${lane}". Declared lanes: ${[...declared].map((l) => `"${l}"`).join(", ")}. Add \`.withLane({ name: "${lane}", ... })\` to act() or correct the .to() declaration.`
4813
- );
4814
- }
4815
- }
4816
- }
4817
- }
4818
4940
  function act() {
4819
4941
  const states = /* @__PURE__ */ new Map();
4820
4942
  const _sf = /* @__PURE__ */ new Map();
4943
+ const _hr = /* @__PURE__ */ new Map();
4821
4944
  const _qg = /* @__PURE__ */ new Map();
4822
4945
  const _dp = /* @__PURE__ */ new Map();
4823
4946
  const _de = /* @__PURE__ */ new Map();
@@ -4848,13 +4971,10 @@ function act() {
4848
4971
  const fold_specs = [];
4849
4972
  const lanes = [];
4850
4973
  let _built = false;
4851
- const pii_wrap = (original) => {
4974
+ const read_wrap = (original) => {
4852
4975
  const wrapped = async (events, stream) => {
4853
- const stripped = events.map((e) => {
4854
- const f = _sf.get(e.name);
4855
- return f ? pii_strip(e, f) : e;
4856
- });
4857
- return original(stripped, stream);
4976
+ const read = events.map((e) => _hr.get(e.name)?.(e) ?? e);
4977
+ return original(read, stream);
4858
4978
  };
4859
4979
  const reset = original[FOLD_RESET];
4860
4980
  if (reset) Object.defineProperty(wrapped, FOLD_RESET, { value: reset });
@@ -4865,14 +4985,14 @@ function act() {
4865
4985
  for (const spec of fold_specs) {
4866
4986
  handlers.set(
4867
4987
  spec.target,
4868
- pii_wrap(
4988
+ read_wrap(
4869
4989
  make_fold_handler(
4870
4990
  spec.merged,
4871
4991
  spec.flush,
4872
4992
  spec.config,
4873
4993
  patch_fn,
4874
4994
  // Head loads strip sensitive keys like the warm path (#1320).
4875
- (event_name) => _sf.get(event_name) ?? []
4995
+ registry.sensitive_fields
4876
4996
  )
4877
4997
  )
4878
4998
  );
@@ -4990,67 +5110,22 @@ function act() {
4990
5110
  config: fold.config
4991
5111
  });
4992
5112
  }
4993
- for (const register of Object.values(registry.events)) {
4994
- for (const reaction of register.reactions.values()) {
4995
- if (typeof reaction.resolver === "function") continue;
4996
- const target = reaction.resolver.target;
4997
- const claimed = batch_handlers.has(target) || fold_specs.some((f) => f.target === target);
4998
- if (claimed && !projection_reactions.has(reaction))
4999
- throw new Error(
5000
- `Reaction on target "${target}" conflicts with the projection that already serves it \u2014 a target is served by one batch handler or one state projection, and a reaction to it would never run`
5001
- );
5002
- }
5003
- }
5113
+ const built = build_events(registry, states, lanes, {
5114
+ batch_handlers,
5115
+ fold_targets: new Set(fold_specs.map((f) => f.target)),
5116
+ projection_reactions
5117
+ });
5118
+ for (const [name, fields] of built.sensitive) _sf.set(name, fields);
5119
+ for (const [name, gate] of built.query_readers) _qg.set(name, gate);
5120
+ for (const [name, gate] of built.handler_readers) _hr.set(name, gate);
5004
5121
  finalize_deprecations();
5005
- validate_lane_references(registry, lanes);
5006
- for (const [event_name, reg] of Object.entries(
5007
- registry.events
5008
- )) {
5009
- const fields = pii_fields(reg.schema);
5010
- if (fields.length === 0) continue;
5011
- _sf.set(event_name, fields);
5012
- _qg.set(event_name, make_gate(fields, null));
5013
- for (const [name, reaction] of reg.reactions) {
5014
- const inner = reaction.handler;
5015
- const wrapped = (event, stream, app) => inner(pii_strip(event, fields), stream, app);
5016
- Object.defineProperty(wrapped, "name", { value: inner.name });
5017
- reaction.handler = wrapped;
5018
- reg.reactions.set(name, reaction);
5019
- }
5020
- }
5021
5122
  for (const state2 of states.values()) {
5022
5123
  if (state2.disclose) _dp.set(state2.name, state2.disclose);
5023
5124
  if (state2.autoclose) _ac.set(state2.name, state2.autoclose);
5024
5125
  if (state2.archive) _aa.set(state2.name, state2.archive);
5025
- const fields_by_event = /* @__PURE__ */ new Map();
5026
- for (const event_name of Object.keys(state2.events)) {
5027
- const fields = _sf.get(event_name);
5028
- if (fields) fields_by_event.set(event_name, fields);
5029
- }
5030
- if (fields_by_event.size === 0) continue;
5031
- if (state2.snap) {
5032
- const offending = [...fields_by_event.keys()];
5033
- throw new Error(
5034
- `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.`
5035
- );
5036
- }
5037
- const disclose = state2.disclose ?? null;
5038
- state2.pii_aware = true;
5039
- const view_gates = /* @__PURE__ */ new Map();
5040
- for (const [event_name, fields] of fields_by_event) {
5041
- view_gates.set(event_name, make_gate(fields, disclose));
5042
- }
5043
- state2.view = (event, actor) => (view_gates.get(event.name) ?? IDENTITY_GATE)(
5044
- event,
5045
- actor
5046
- );
5047
- state2.message = (validated) => {
5048
- const fields = fields_by_event.get(validated.name);
5049
- return fields ? pii_split(validated, fields) : validated;
5050
- };
5051
5126
  }
5052
5127
  for (const [target, original] of batch_handlers) {
5053
- batch_handlers.set(target, pii_wrap(original));
5128
+ batch_handlers.set(target, read_wrap(original));
5054
5129
  }
5055
5130
  synthesize_autoclose_reactions(
5056
5131
  registry,
@@ -5409,11 +5484,11 @@ var CsvFile = class {
5409
5484
  // payload must not depend on whether it came back through a store or
5410
5485
  // through a CSV restore (#1399). The `created` column below has
5411
5486
  // always been parsed explicitly; the JSON columns were missed.
5412
- data: JSON.parse(fields[2], dateReviver),
5487
+ data: JSON.parse(fields[2]),
5413
5488
  stream: fields[3],
5414
5489
  version: Number.parseInt(fields[4], 10),
5415
5490
  created: new Date(fields[5]),
5416
- meta: JSON.parse(fields[6], dateReviver)
5491
+ meta: JSON.parse(fields[6])
5417
5492
  };
5418
5493
  await Promise.resolve(callback(event));
5419
5494
  count++;