@hotmeshio/hotmesh 0.25.1 → 0.25.2

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.25.1",
3
+ "version": "0.25.2",
4
4
  "description": "Durable Workflow",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -496,7 +496,16 @@ class Hook extends activity_1.Activity {
496
496
  }
497
497
  async getHookRule(topic) {
498
498
  const rules = await this.store.getHookRules();
499
- return rules?.[topic]?.[0];
499
+ const forTopic = rules?.[topic];
500
+ if (!forTopic?.length)
501
+ return undefined;
502
+ // A topic may carry one rule per waiter activity — the durable factory
503
+ // registers `waiter` and `signaler_waiter` on the same wait topic. Select
504
+ // the rule that targets THIS activity: the positional first rule addresses
505
+ // a sibling branch for every activity but its own, so its expected-pipe
506
+ // resolves against output that does not exist in this flow (which is how
507
+ // execHook-context waits were writing signal_key-less escalation rows).
508
+ return (forTopic.find((rule) => rule.to === this.metadata.aid) ?? forTopic[0]);
500
509
  }
501
510
  /**
502
511
  * Register the time hook (sleep) inside the Leg1 transaction.
@@ -654,10 +663,20 @@ class Hook extends activity_1.Activity {
654
663
  const store = this.store;
655
664
  if (typeof store.expireEscalationBySignalKey !== 'function')
656
665
  return;
666
+ // Hydration here is THROWAWAY. getState() REPLACES this.context with
667
+ // restored job state, which does not carry the per-message dimensional
668
+ // address (dad) — the stream message is its only carrier for this leg.
669
+ // processEvent must receive the pristine dispatch state (verifyReentry
670
+ // hydrates for itself, reading dad from context.metadata), so snapshot
671
+ // everything getState mutates — the context reference, metadata.dad,
672
+ // and leg — and restore them on every exit path. Without this, a wait
673
+ // executing below the root (any dimension) crashes Leg2 notarization.
674
+ const dispatchContext = this.context;
675
+ const dispatchDad = this.metadata.dad;
676
+ const dispatchLeg = this.leg;
657
677
  try {
658
- // The TIMEHOOK dispatch context carries metadata only hydrate job
659
- // state so the signal-key pipe expression resolves to the same value
660
- // Leg1 stored on the row.
678
+ // Hydrate job state so the signal-key pipe expression resolves to the
679
+ // same value Leg1 stored on the row.
661
680
  this.setLeg(2);
662
681
  await this.getState();
663
682
  const appId = this.engine.appId;
@@ -689,6 +708,11 @@ class Hook extends activity_1.Activity {
689
708
  error: e.message,
690
709
  });
691
710
  }
711
+ finally {
712
+ this.context = dispatchContext;
713
+ this.metadata.dad = dispatchDad;
714
+ this.setLeg(dispatchLeg);
715
+ }
692
716
  }
693
717
  }
694
718
  exports.Hook = Hook;
@@ -72,6 +72,15 @@ import { ConditionQueueConfig } from '../../../types/hmsh_escalations';
72
72
  * await client.escalations.resolve({ id: item.id, resolverPayload: { approved: true } });
73
73
  * ```
74
74
  *
75
+ * ## Placement: call escalation-bearing waits from main workflow code
76
+ *
77
+ * The resolve/signal delivery pipeline routes to the main flow's waiter.
78
+ * Inside a hook function (`execHook`), an escalation-bearing wait writes its
79
+ * row and honors `timeout` (the row expires and the hook resumes with
80
+ * `false`), while resolution delivery targets the main flow — so structure
81
+ * SLA-gated human waits in the workflow body and let hook functions report
82
+ * back via `signal()`.
83
+ *
75
84
  * ## Fan-in: wait for multiple signals in parallel
76
85
  *
77
86
  * ```typescript
@@ -77,6 +77,15 @@ const didRun_1 = require("./didRun");
77
77
  * await client.escalations.resolve({ id: item.id, resolverPayload: { approved: true } });
78
78
  * ```
79
79
  *
80
+ * ## Placement: call escalation-bearing waits from main workflow code
81
+ *
82
+ * The resolve/signal delivery pipeline routes to the main flow's waiter.
83
+ * Inside a hook function (`execHook`), an escalation-bearing wait writes its
84
+ * row and honors `timeout` (the row expires and the hook resumes with
85
+ * `false`), while resolution delivery targets the main flow — so structure
86
+ * SLA-gated human waits in the workflow body and let hook functions report
87
+ * back via `signal()`.
88
+ *
80
89
  * ## Fan-in: wait for multiple signals in parallel
81
90
  *
82
91
  * ```typescript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.25.1",
3
+ "version": "0.25.2",
4
4
  "description": "Durable Workflow",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",