@rotorsoft/act 1.11.0 → 1.12.1

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
@@ -19,7 +19,7 @@ import {
19
19
  sleep,
20
20
  store,
21
21
  validate
22
- } from "./chunk-5PWLGL5E.js";
22
+ } from "./chunk-QCSXT5MB.js";
23
23
  import {
24
24
  ActorSchema,
25
25
  CausationEventSchema,
@@ -271,7 +271,7 @@ var make_restart_candidate_pass = (deps, options) => {
271
271
  // names map is sparse — `__snapshot__` key absent when the
272
272
  // stream has never been snapshotted (a common case for the
273
273
  // restart-candidate signal).
274
- snaps: names["__snapshot__"] ?? 0
274
+ snaps: names.__snapshot__ ?? 0
275
275
  });
276
276
  },
277
277
  drain: () => findings
@@ -333,7 +333,7 @@ var make_snapshot_drift_pass = (deps, options) => {
333
333
  candidates.push({
334
334
  stream,
335
335
  total: count,
336
- snaps: names["__snapshot__"] ?? 0
336
+ snaps: names.__snapshot__ ?? 0
337
337
  });
338
338
  },
339
339
  async finalize(deps2) {
@@ -827,6 +827,76 @@ var AutocloseController = class {
827
827
  }
828
828
  };
829
829
 
830
+ // src/internal/autoclose-policy.ts
831
+ import { z as z2 } from "zod";
832
+ var MIN_AFTER_MS = 6e4;
833
+ var AfterSchema = z2.object({
834
+ days: z2.number({ message: "autocloses: after.days must be a number" }).positive("autocloses: after.days must be > 0")
835
+ }).refine((o) => o.days * 864e5 >= MIN_AFTER_MS, {
836
+ message: `autocloses: after resolves to < ${MIN_AFTER_MS}ms (one minute), which is too short to be a meaningful retention window`
837
+ });
838
+ var IsSchema = z2.union([
839
+ z2.string().min(1, "autocloses: is must be a non-empty event name"),
840
+ z2.array(
841
+ z2.string().min(1, "autocloses: is entries must be non-empty event names")
842
+ ).min(1, "autocloses: is must include at least one event name").readonly()
843
+ ]);
844
+ var ReachesSchema = z2.number({ message: "autocloses: reaches must be a number" }).int("autocloses: reaches must be an integer").min(1, "autocloses: reaches must be >= 1");
845
+ var OrBlockSchema = z2.object({
846
+ after: AfterSchema.optional(),
847
+ is: IsSchema.optional(),
848
+ reaches: ReachesSchema.optional()
849
+ }).strict().refine(
850
+ (o) => o.after !== void 0 || o.is !== void 0 || o.reaches !== void 0,
851
+ {
852
+ message: "autocloses: `or` block must include at least one of after / is / reaches"
853
+ }
854
+ );
855
+ var AutoclosePolicySchema = z2.object({
856
+ after: AfterSchema.optional(),
857
+ is: IsSchema.optional(),
858
+ reaches: ReachesSchema.optional(),
859
+ or: OrBlockSchema.optional()
860
+ }).strict().refine(
861
+ (o) => o.after !== void 0 || o.is !== void 0 || o.reaches !== void 0 || o.or !== void 0,
862
+ {
863
+ message: "autocloses: at least one of after / is / reaches / or must be specified \u2014 empty `{}` is a misconfiguration"
864
+ }
865
+ );
866
+ function compile_after(after) {
867
+ const after_ms = after.days * 864e5;
868
+ return (_stream, head) => Date.now() - head.created.getTime() >= after_ms;
869
+ }
870
+ function compile_is(is) {
871
+ const set = new Set(typeof is === "string" ? [is] : is);
872
+ return (_stream, head) => set.has(head.name);
873
+ }
874
+ function compile_reaches(reaches) {
875
+ return (_stream, _head, count) => count >= reaches;
876
+ }
877
+ function compile_autoclose_policy(options) {
878
+ const parsed = AutoclosePolicySchema.parse(options);
879
+ const and_preds = [];
880
+ if (parsed.after) and_preds.push(compile_after(parsed.after));
881
+ if (parsed.is) and_preds.push(compile_is(parsed.is));
882
+ if (parsed.reaches) and_preds.push(compile_reaches(parsed.reaches));
883
+ const or_preds = [];
884
+ if (parsed.or) {
885
+ if (parsed.or.after) or_preds.push(compile_after(parsed.or.after));
886
+ if (parsed.or.is) or_preds.push(compile_is(parsed.or.is));
887
+ if (parsed.or.reaches) or_preds.push(compile_reaches(parsed.or.reaches));
888
+ }
889
+ return (stream, head, count) => {
890
+ if (and_preds.length > 0 && and_preds.every((p) => p(stream, head, count))) {
891
+ return true;
892
+ }
893
+ if (or_preds.some((p) => p(stream, head, count))) {
894
+ return true;
895
+ }
896
+ return false;
897
+ };
898
+ }
899
+
830
900
  // src/internal/build-classify.ts
831
901
  var ALL_LANES = /* @__PURE__ */ Symbol("act-1103/all-lanes");
832
902
  function classify_registry(registry, states) {
@@ -1548,7 +1618,7 @@ function trace_cycle(logger, leased, fetched, handled, acked, blocked) {
1548
1618
  const detail = leased.map(({ stream, at, retry }) => {
1549
1619
  const f = fetch_by_stream.get(stream);
1550
1620
  const key = f?.source ? `${hue(C_STREAM, stream)}${dim(`<-${f.source}`)}` : hue(C_STREAM, stream);
1551
- const events = f && f.events.length ? ` ${dim(
1621
+ const events = f?.events.length ? ` ${dim(
1552
1622
  `[${f.events.map(({ id, name }) => `#${id} ${String(name)}`).join(", ")}]`
1553
1623
  )}` : "";
1554
1624
  const acked_at = acked_by_stream.get(stream);
@@ -1898,6 +1968,11 @@ function merge_event_register(target, source) {
1898
1968
  const target_reg = target[event_name];
1899
1969
  if (!target_reg) continue;
1900
1970
  for (const [name, reaction] of source_reg.reactions) {
1971
+ const existing = target_reg.reactions.get(name);
1972
+ if (existing !== void 0 && existing !== reaction)
1973
+ throw new Error(
1974
+ `Duplicate reaction "${name}" for event "${event_name}". Reaction handlers are keyed by function name; rename one of them.`
1975
+ );
1901
1976
  target_reg.reactions.set(name, reaction);
1902
1977
  }
1903
1978
  }
@@ -3348,6 +3423,10 @@ function act() {
3348
3423
  throw new Error(
3349
3424
  `Reaction handler for "${String(event)}" must be a named function`
3350
3425
  );
3426
+ if (registry.events[event].reactions.has(handler.name))
3427
+ throw new Error(
3428
+ `Duplicate reaction "${handler.name}" for event "${String(event)}". Reaction handlers are keyed by function name; rename one of them.`
3429
+ );
3351
3430
  registry.events[event].reactions.set(handler.name, reaction);
3352
3431
  return Object.assign(builder, {
3353
3432
  to(resolver) {
@@ -3464,6 +3543,10 @@ function _projection(target) {
3464
3543
  throw new Error(
3465
3544
  `Projection handler for "${event}" must be a named function`
3466
3545
  );
3546
+ if (register.reactions.has(handler.name))
3547
+ throw new Error(
3548
+ `Duplicate projection handler "${handler.name}" for event "${event}". Projection handlers are keyed by function name; rename one of them.`
3549
+ );
3467
3550
  register.reactions.set(handler.name, reaction);
3468
3551
  const widened = base;
3469
3552
  return Object.assign(widened, {
@@ -3539,6 +3622,10 @@ function slice() {
3539
3622
  throw new Error(
3540
3623
  `Reaction handler for "${String(event)}" must be a named function`
3541
3624
  );
3625
+ if (events[event].reactions.has(handler.name))
3626
+ throw new Error(
3627
+ `Duplicate reaction "${handler.name}" for event "${String(event)}". Reaction handlers are keyed by function name; rename one of them.`
3628
+ );
3542
3629
  events[event].reactions.set(handler.name, reaction);
3543
3630
  return Object.assign(builder, {
3544
3631
  to(resolver) {
@@ -3650,10 +3737,17 @@ function action_builder(state2) {
3650
3737
  internal.disclose = disclose;
3651
3738
  return builder;
3652
3739
  },
3653
- autocloses(predicate) {
3654
- if (typeof predicate !== "function") {
3740
+ autocloses(predicate_or_policy) {
3741
+ let predicate;
3742
+ if (typeof predicate_or_policy === "function") {
3743
+ predicate = predicate_or_policy;
3744
+ } else if (predicate_or_policy !== null && typeof predicate_or_policy === "object") {
3745
+ predicate = compile_autoclose_policy(
3746
+ predicate_or_policy
3747
+ );
3748
+ } else {
3655
3749
  throw new Error(
3656
- ".autocloses(predicate) requires a function; got " + typeof predicate
3750
+ ".autocloses(...) requires a function or a policy object; got " + typeof predicate_or_policy
3657
3751
  );
3658
3752
  }
3659
3753
  internal.autoclose = predicate;