@hotmeshio/hotmesh 0.7.0 → 0.9.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.
Files changed (138) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/README.md +158 -38
  3. package/build/index.d.ts +1 -3
  4. package/build/index.js +1 -5
  5. package/build/modules/utils.js +3 -31
  6. package/build/package.json +63 -79
  7. package/build/services/activities/activity.d.ts +97 -9
  8. package/build/services/activities/activity.js +323 -86
  9. package/build/services/activities/await.d.ts +101 -0
  10. package/build/services/activities/await.js +103 -2
  11. package/build/services/activities/cycle.d.ts +82 -0
  12. package/build/services/activities/cycle.js +86 -8
  13. package/build/services/activities/hook.d.ts +144 -1
  14. package/build/services/activities/hook.js +162 -21
  15. package/build/services/activities/interrupt.d.ts +112 -0
  16. package/build/services/activities/interrupt.js +134 -29
  17. package/build/services/activities/signal.d.ts +111 -4
  18. package/build/services/activities/signal.js +136 -28
  19. package/build/services/activities/trigger.d.ts +56 -4
  20. package/build/services/activities/trigger.js +119 -35
  21. package/build/services/activities/worker.d.ts +107 -0
  22. package/build/services/activities/worker.js +109 -2
  23. package/build/services/collator/index.d.ts +116 -30
  24. package/build/services/collator/index.js +211 -115
  25. package/build/services/connector/factory.d.ts +1 -1
  26. package/build/services/connector/factory.js +1 -11
  27. package/build/services/engine/index.d.ts +22 -6
  28. package/build/services/engine/index.js +49 -18
  29. package/build/services/exporter/index.d.ts +2 -0
  30. package/build/services/exporter/index.js +1 -0
  31. package/build/services/hotmesh/index.d.ts +471 -236
  32. package/build/services/hotmesh/index.js +473 -238
  33. package/build/services/memflow/client.js +2 -2
  34. package/build/services/memflow/handle.js +1 -1
  35. package/build/services/memflow/index.d.ts +1 -1
  36. package/build/services/memflow/index.js +1 -1
  37. package/build/services/memflow/workflow/all.d.ts +28 -3
  38. package/build/services/memflow/workflow/all.js +28 -3
  39. package/build/services/memflow/workflow/context.d.ts +44 -1
  40. package/build/services/memflow/workflow/context.js +44 -1
  41. package/build/services/memflow/workflow/didRun.d.ts +23 -3
  42. package/build/services/memflow/workflow/didRun.js +23 -3
  43. package/build/services/memflow/workflow/emit.d.ts +43 -4
  44. package/build/services/memflow/workflow/emit.js +43 -4
  45. package/build/services/memflow/workflow/enrich.d.ts +32 -4
  46. package/build/services/memflow/workflow/enrich.js +32 -4
  47. package/build/services/memflow/workflow/entityMethods.d.ts +54 -7
  48. package/build/services/memflow/workflow/entityMethods.js +54 -7
  49. package/build/services/memflow/workflow/execChild.d.ts +96 -8
  50. package/build/services/memflow/workflow/execChild.js +96 -8
  51. package/build/services/memflow/workflow/execHook.d.ts +54 -39
  52. package/build/services/memflow/workflow/execHook.js +52 -38
  53. package/build/services/memflow/workflow/execHookBatch.d.ts +82 -29
  54. package/build/services/memflow/workflow/execHookBatch.js +80 -28
  55. package/build/services/memflow/workflow/hook.d.ts +68 -3
  56. package/build/services/memflow/workflow/hook.js +69 -4
  57. package/build/services/memflow/workflow/index.d.ts +65 -10
  58. package/build/services/memflow/workflow/index.js +65 -10
  59. package/build/services/memflow/workflow/interrupt.d.ts +50 -4
  60. package/build/services/memflow/workflow/interrupt.js +50 -4
  61. package/build/services/memflow/workflow/interruption.d.ts +49 -16
  62. package/build/services/memflow/workflow/interruption.js +49 -16
  63. package/build/services/memflow/workflow/isSideEffectAllowed.d.ts +21 -4
  64. package/build/services/memflow/workflow/isSideEffectAllowed.js +21 -4
  65. package/build/services/memflow/workflow/proxyActivities.d.ts +70 -42
  66. package/build/services/memflow/workflow/proxyActivities.js +70 -42
  67. package/build/services/memflow/workflow/random.d.ts +33 -3
  68. package/build/services/memflow/workflow/random.js +33 -3
  69. package/build/services/memflow/workflow/searchMethods.d.ts +49 -2
  70. package/build/services/memflow/workflow/searchMethods.js +49 -2
  71. package/build/services/memflow/workflow/signal.d.ts +51 -22
  72. package/build/services/memflow/workflow/signal.js +52 -23
  73. package/build/services/memflow/workflow/sleepFor.d.ts +57 -18
  74. package/build/services/memflow/workflow/sleepFor.js +57 -18
  75. package/build/services/memflow/workflow/trace.d.ts +39 -6
  76. package/build/services/memflow/workflow/trace.js +39 -6
  77. package/build/services/memflow/workflow/waitFor.d.ts +55 -18
  78. package/build/services/memflow/workflow/waitFor.js +55 -18
  79. package/build/services/router/consumption/index.js +1 -1
  80. package/build/services/search/factory.js +1 -9
  81. package/build/services/store/factory.js +1 -9
  82. package/build/services/store/index.d.ts +6 -1
  83. package/build/services/store/providers/postgres/kvsql.d.ts +4 -0
  84. package/build/services/store/providers/postgres/kvsql.js +4 -0
  85. package/build/services/store/providers/postgres/kvtransaction.d.ts +2 -0
  86. package/build/services/store/providers/postgres/kvtransaction.js +23 -0
  87. package/build/services/store/providers/postgres/kvtypes/hash/basic.d.ts +51 -0
  88. package/build/services/store/providers/postgres/kvtypes/hash/basic.js +193 -1
  89. package/build/services/store/providers/postgres/kvtypes/hash/index.d.ts +4 -0
  90. package/build/services/store/providers/postgres/kvtypes/hash/index.js +6 -0
  91. package/build/services/store/providers/postgres/postgres.d.ts +21 -1
  92. package/build/services/store/providers/postgres/postgres.js +42 -4
  93. package/build/services/stream/factory.js +1 -17
  94. package/build/services/stream/providers/postgres/scout.js +2 -2
  95. package/build/services/sub/factory.js +1 -9
  96. package/build/services/sub/index.d.ts +1 -1
  97. package/build/services/sub/providers/postgres/postgres.d.ts +1 -1
  98. package/build/services/sub/providers/postgres/postgres.js +25 -10
  99. package/build/services/task/index.d.ts +1 -1
  100. package/build/services/task/index.js +2 -6
  101. package/build/services/telemetry/index.js +6 -0
  102. package/build/types/activity.d.ts +1 -1
  103. package/build/types/hotmesh.d.ts +1 -1
  104. package/build/types/index.d.ts +0 -1
  105. package/build/types/index.js +1 -4
  106. package/build/types/job.d.ts +1 -1
  107. package/build/types/memflow.d.ts +1 -1
  108. package/build/types/provider.d.ts +1 -1
  109. package/build/types/quorum.d.ts +2 -2
  110. package/build/vitest.config.d.ts +2 -0
  111. package/build/vitest.config.js +18 -0
  112. package/index.ts +0 -4
  113. package/package.json +63 -79
  114. package/vitest.config.ts +17 -0
  115. package/build/services/connector/providers/ioredis.d.ts +0 -9
  116. package/build/services/connector/providers/ioredis.js +0 -26
  117. package/build/services/connector/providers/redis.d.ts +0 -9
  118. package/build/services/connector/providers/redis.js +0 -38
  119. package/build/services/search/providers/redis/ioredis.d.ts +0 -23
  120. package/build/services/search/providers/redis/ioredis.js +0 -189
  121. package/build/services/search/providers/redis/redis.d.ts +0 -23
  122. package/build/services/search/providers/redis/redis.js +0 -202
  123. package/build/services/store/providers/redis/_base.d.ts +0 -137
  124. package/build/services/store/providers/redis/_base.js +0 -980
  125. package/build/services/store/providers/redis/ioredis.d.ts +0 -20
  126. package/build/services/store/providers/redis/ioredis.js +0 -190
  127. package/build/services/store/providers/redis/redis.d.ts +0 -18
  128. package/build/services/store/providers/redis/redis.js +0 -199
  129. package/build/services/stream/providers/redis/ioredis.d.ts +0 -61
  130. package/build/services/stream/providers/redis/ioredis.js +0 -272
  131. package/build/services/stream/providers/redis/redis.d.ts +0 -61
  132. package/build/services/stream/providers/redis/redis.js +0 -305
  133. package/build/services/sub/providers/redis/ioredis.d.ts +0 -20
  134. package/build/services/sub/providers/redis/ioredis.js +0 -161
  135. package/build/services/sub/providers/redis/redis.d.ts +0 -18
  136. package/build/services/sub/providers/redis/redis.js +0 -148
  137. package/build/types/redis.d.ts +0 -258
  138. package/build/types/redis.js +0 -11
@@ -42,85 +42,158 @@ class CollatorService {
42
42
  dimensions.push('0');
43
43
  return dimensions.join(',');
44
44
  }
45
+ // ──────────────────────────────────────────────────
46
+ // Leg1 notarization
47
+ // ──────────────────────────────────────────────────
48
+ /**
49
+ * Leg1 entry: increment attempt counter (+1T).
50
+ * NOT bundled with Leg1 work — exists only to mark entry.
51
+ */
45
52
  static async notarizeEntry(activity, transaction) {
46
- //decrement by -100_000_000_000_000
47
- const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, -100000000000000, this.getDimensionalAddress(activity), transaction);
53
+ const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, this.WEIGHTS.LEG1_ENTRY, this.getDimensionalAddress(activity), transaction);
48
54
  this.verifyInteger(amount, 1, 'enter');
49
55
  return amount;
50
56
  }
51
- static async authorizeReentry(activity, transaction) {
52
- //set second digit to 8, allowing for re-entry
53
- //decrement by -10_000_000_000_000
54
- const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, -10000000000000, this.getDimensionalAddress(activity), transaction);
55
- return amount;
57
+ /**
58
+ * Leg1 completion: increment +100B to mark Leg1 complete.
59
+ * For cycle=true activities, also pre-seeds the Leg2 entry counter (+1)
60
+ * so the first real Leg2 gets adjacentIndex=1 (new dimension).
61
+ * MUST be bundled in the same transaction as Leg1 durable work.
62
+ */
63
+ static async notarizeLeg1Completion(activity, transaction) {
64
+ const amount = activity.config.cycle
65
+ ? this.WEIGHTS.LEG1_COMPLETE + this.WEIGHTS.LEG2_ENTRY
66
+ : this.WEIGHTS.LEG1_COMPLETE;
67
+ return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, amount, this.getDimensionalAddress(activity), transaction);
56
68
  }
69
+ /**
70
+ * Leg1 early exit: marks Leg1 complete for activities that
71
+ * only run Leg1 and fully close (e.g., Cycle).
72
+ * Increment +100B (Leg1 completion marker).
73
+ */
57
74
  static async notarizeEarlyExit(activity, transaction) {
58
- //decrement the 2nd and 3rd digits to fully deactivate (`cycle` activities use this command to fully exit after leg 1) (should result in `888000000000000`)
59
- return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, -11000000000000, this.getDimensionalAddress(activity), transaction);
75
+ return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, this.WEIGHTS.LEG1_COMPLETE, this.getDimensionalAddress(activity), transaction);
60
76
  }
77
+ /**
78
+ * Leg1 early completion: marks Leg1 complete for Leg1-only
79
+ * activities that spawn children (e.g., Signal, Hook passthrough,
80
+ * Interrupt-another). Increment +100B.
81
+ */
61
82
  static async notarizeEarlyCompletion(activity, transaction) {
62
- //initialize both `possible` (1m) and `actualized` (1) zero dimension, while decrementing the 2nd
63
- //3rd digit is optionally kept open if the activity might be used in a cycle
64
- const decrement = activity.config.cycle
65
- ? 10000000000000
66
- : 11000000000000;
67
- return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1000001 - decrement, this.getDimensionalAddress(activity), transaction);
83
+ return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, this.WEIGHTS.LEG1_COMPLETE, this.getDimensionalAddress(activity), transaction);
68
84
  }
85
+ // ──────────────────────────────────────────────────
86
+ // Leg2 notarization (entry + 3-step protocol)
87
+ // ──────────────────────────────────────────────────
69
88
  /**
70
- * sets the synthetic inception key (in case of an overage occurs).
89
+ * Leg2 entry: atomically increments the activity ledger (+1) and
90
+ * seeds the GUID ledger with the ordinal IF NOT EXISTS.
91
+ * Returns [activityLedger, guidLedger] after the compound operation.
71
92
  */
72
- static async notarizeInception(activity, guid, transaction) {
73
- if (guid) {
74
- await activity.store.collateSynthetic(activity.context.metadata.jid, guid, 1000000, transaction);
75
- }
93
+ static async notarizeLeg2Entry(activity, guid, transaction) {
94
+ const jid = activity.context.metadata.jid;
95
+ const localMulti = transaction || activity.store.transact();
96
+ //compound: increment activity Leg2 counter and seed GUID ledger
97
+ await activity.store.collateLeg2Entry(jid, activity.metadata.aid, guid, this.getDimensionalAddress(activity, true), localMulti);
98
+ const results = await localMulti.exec();
99
+ const result = results[0];
100
+ const [amountConcrete, amountSynthetic] = Array.isArray(result)
101
+ ? result
102
+ : [result, result];
103
+ this.verifyInteger(amountConcrete, 2, 'enter');
104
+ this.verifySyntheticInteger(amountSynthetic);
105
+ return [amountConcrete, amountSynthetic];
76
106
  }
77
107
  /**
78
- * ignore those ID collisions that are due to re-entry overages
108
+ * Step 1: Mark Leg2 work done (+10B on GUID ledger only).
109
+ * MUST be bundled with Leg2 durable work writes.
79
110
  */
80
- static async isInceptionOverage(activity, guid) {
81
- if (guid) {
82
- const amount = await activity.store.collateSynthetic(activity.context.metadata.jid, guid, 1000000);
83
- return amount > 1000000;
84
- }
85
- return false;
111
+ static async notarizeStep1(activity, guid, transaction) {
112
+ const jid = activity.context.metadata.jid;
113
+ await activity.store.collateSynthetic(jid, guid, this.WEIGHTS.STEP1_WORK, transaction);
86
114
  }
87
115
  /**
88
- * verifies both the concrete and synthetic keys for the activity; concrete keys
89
- * exist in the original model and are effectively the 'real' keys. In reality,
90
- * hook activities are atomized during compilation to create a synthetic DAG that
91
- * is used to track the status of the graph in a distributed environment. The
92
- * synthetic key represents different dimensional realities and is used to
93
- * track re-entry overages (it distinguishes between the original and re-entry).
94
- * The essential challenge is: is this a re-entry that is purposeful in
95
- * order to induce cycles, or is the re-entry due to a failure in the system?
116
+ * Step 2: Mark children spawned (+1B on GUID ledger only).
117
+ * The job semaphore update and GUID job-closed snapshot are handled
118
+ * by the compound `setStatusAndCollateGuid` primitive, which MUST
119
+ * be called in the same transaction.
96
120
  */
97
- static async notarizeReentry(activity, guid, transaction) {
121
+ static async notarizeStep2(activity, guid, transaction) {
98
122
  const jid = activity.context.metadata.jid;
99
- const localMulti = transaction || activity.store.transact();
100
- //increment by 1_000_000 (indicates re-entry and is used to drive the 'dimensional address' for adjacent activities (minus 1))
101
- await activity.store.collate(jid, activity.metadata.aid, 1000000, this.getDimensionalAddress(activity, true), localMulti);
102
- await activity.store.collateSynthetic(jid, guid, 1000000, localMulti);
103
- const [_amountConcrete, _amountSynthetic] = await localMulti.exec();
104
- const amountConcrete = Array.isArray(_amountConcrete)
105
- ? _amountConcrete[1]
106
- : _amountConcrete;
107
- const amountSynthetic = Array.isArray(_amountSynthetic)
108
- ? _amountSynthetic[1]
109
- : _amountSynthetic;
110
- this.verifyInteger(amountConcrete, 2, 'enter');
111
- this.verifySyntheticInteger(amountSynthetic);
112
- return amountConcrete;
123
+ await activity.store.collateSynthetic(jid, guid, this.WEIGHTS.STEP2_SPAWN, transaction);
113
124
  }
114
- static async notarizeContinuation(activity, transaction) {
115
- //keep open; actualize the leg2 dimension (+1)
116
- return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1, this.getDimensionalAddress(activity), transaction);
125
+ /**
126
+ * Step 3: Mark job completion tasks done (+100M on GUID ledger only).
127
+ * MUST be bundled with job completion durable writes.
128
+ */
129
+ static async notarizeStep3(activity, guid, transaction) {
130
+ const jid = activity.context.metadata.jid;
131
+ await activity.store.collateSynthetic(jid, guid, this.WEIGHTS.STEP3_CLEANUP, transaction);
117
132
  }
118
- static async notarizeCompletion(activity, transaction) {
119
- //1) ALWAYS actualize leg2 dimension (+1)
120
- //2) IF the activity is used in a cycle, don't close leg 2!
121
- const decrement = activity.config.cycle ? 0 : 1000000000000;
122
- return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1 - decrement, this.getDimensionalAddress(activity), transaction);
133
+ /**
134
+ * Finalize: close the activity to new Leg2 GUIDs (+200T).
135
+ * Sets pos 1 to 2 (finalized).
136
+ * Only for non-cycle activities after final SUCCESS/ERROR.
137
+ */
138
+ static async notarizeFinalize(activity, transaction) {
139
+ if (!activity.config.cycle) {
140
+ await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, this.WEIGHTS.FINALIZE, this.getDimensionalAddress(activity), transaction);
141
+ }
123
142
  }
143
+ // ──────────────────────────────────────────────────
144
+ // GUID ledger extraction (step-level resume)
145
+ // ──────────────────────────────────────────────────
146
+ /**
147
+ * Check if Step 1 (work done) is complete on the GUID ledger.
148
+ * Position 5 (10B digit) > 0.
149
+ */
150
+ static isGuidStep1Done(guidLedger) {
151
+ return this.getDigitAtPosition(guidLedger, 5) > 0;
152
+ }
153
+ /**
154
+ * Check if Step 2 (children spawned) is complete on the GUID ledger.
155
+ * Position 6 (1B digit) > 0.
156
+ */
157
+ static isGuidStep2Done(guidLedger) {
158
+ return this.getDigitAtPosition(guidLedger, 6) > 0;
159
+ }
160
+ /**
161
+ * Check if Step 3 (job completion tasks) is complete on the GUID ledger.
162
+ * Position 7 (100M digit) > 0.
163
+ */
164
+ static isGuidStep3Done(guidLedger) {
165
+ return this.getDigitAtPosition(guidLedger, 7) > 0;
166
+ }
167
+ /**
168
+ * Check if this GUID was responsible for closing the job.
169
+ * Position 4 (100B digit) > 0 (job closed snapshot).
170
+ */
171
+ static isGuidJobClosed(guidLedger) {
172
+ return this.getDigitAtPosition(guidLedger, 4) > 0;
173
+ }
174
+ /**
175
+ * Get the attempt count from the GUID ledger (last 8 digits).
176
+ */
177
+ static getGuidAttemptCount(guidLedger) {
178
+ return guidLedger % 100000000;
179
+ }
180
+ // ──────────────────────────────────────────────────
181
+ // Digit extraction
182
+ // ──────────────────────────────────────────────────
183
+ /**
184
+ * Gets the digit at a 1-indexed position from a 15-digit ledger value.
185
+ * The value is left-padded to 15 digits before extraction.
186
+ */
187
+ static getDigitAtPosition(num, position) {
188
+ const numStr = num.toString().padStart(this.targetLength, '0');
189
+ if (position < 1 || position > this.targetLength) {
190
+ return 0;
191
+ }
192
+ return parseInt(numStr[position - 1], 10);
193
+ }
194
+ /**
195
+ * @deprecated Use getDigitAtPosition (1-indexed) instead
196
+ */
124
197
  static getDigitAtIndex(num, targetDigitIndex) {
125
198
  const numStr = num.toString();
126
199
  if (targetDigitIndex < 0 || targetDigitIndex >= numStr.length) {
@@ -129,77 +202,79 @@ class CollatorService {
129
202
  const digit = parseInt(numStr[targetDigitIndex], 10);
130
203
  return digit;
131
204
  }
205
+ /**
206
+ * Extracts the dimensional index from the Leg2 entry counter.
207
+ * Non-cycle activities: first Leg2 → leg2Count=1 → 1-1=0 (same dimension as Leg1).
208
+ * Cycle activities: first Leg2 → leg2Count=2 (pre-seeded +1) → 2-1=1 (new dimension).
209
+ */
132
210
  static getDimensionalIndex(num) {
133
- const numStr = num.toString();
134
- if (numStr.length < 9) {
211
+ const leg2EntryCount = num % 100000000;
212
+ if (leg2EntryCount <= 0) {
135
213
  return null;
136
214
  }
137
- const extractedStr = numStr.substring(3, 9);
138
- const extractedInt = parseInt(extractedStr, 10);
139
- return extractedInt - 1;
140
- }
141
- static isDuplicate(num, targetDigitIndex) {
142
- return this.getDigitAtIndex(num, targetDigitIndex) < 8;
143
- }
144
- static isInactive(num) {
145
- return this.getDigitAtIndex(num, 2) < 9;
146
- }
147
- static isPrimed(amount, leg) {
148
- //activity entry is not allowed if paths not properly pre-set
149
- if (leg == 1) {
150
- return amount != -100000000000000;
151
- }
152
- else {
153
- return (this.getDigitAtIndex(amount, 0) < 9 &&
154
- this.getDigitAtIndex(amount, 1) < 9);
155
- }
215
+ return leg2EntryCount - 1;
156
216
  }
217
+ // ──────────────────────────────────────────────────
218
+ // Verification
219
+ // ──────────────────────────────────────────────────
157
220
  /**
158
- * During compilation, the graphs are compiled into structures necessary
159
- * for distributed processing; these are referred to as 'synthetic DAGs',
160
- * because they are not part of the original graph, but are used to track
161
- * the status of the graph in a distributed environment. This check ensures
162
- * that the 'synthetic key' is not a duplicate. (which is different than
163
- * saying the 'key' is not a duplicate)
221
+ * Verifies the GUID ledger value for step-level resume decisions.
222
+ * The GUID ledger is seeded with an ordinal position (last 8 digits)
223
+ * on first entry; step markers drive all resume/reject logic.
224
+ *
225
+ * Fully processed: Step 3 done, or Steps 1+2 done without job closure.
226
+ * Crash recovery: Any incomplete step combination is allowed for resume.
164
227
  */
165
228
  static verifySyntheticInteger(amount) {
166
- const samount = amount.toString();
167
- const isCompletedValue = parseInt(samount[samount.length - 1], 10);
168
- if (isCompletedValue > 0) {
169
- //already done error (ack/delete clearly failed; this is a duplicate)
229
+ const step2Done = this.isGuidStep2Done(amount);
230
+ const step3Done = this.isGuidStep3Done(amount);
231
+ const jobClosed = this.isGuidJobClosed(amount);
232
+ if (step3Done) {
233
+ //all steps complete; nothing more to do
170
234
  throw new errors_1.CollationError(amount, 2, 'enter', collator_1.CollationFaultType.INACTIVE);
171
235
  }
172
- else if (amount >= 2000000) {
173
- //duplicate synthetic key (this is a duplicate job ID)
174
- throw new errors_1.CollationError(amount, 2, 'enter', collator_1.CollationFaultType.DUPLICATE);
236
+ if (step2Done && !jobClosed) {
237
+ //steps 1+2 done but this GUID didn't close the job; no Step 3 needed
238
+ throw new errors_1.CollationError(amount, 2, 'enter', collator_1.CollationFaultType.INACTIVE);
175
239
  }
240
+ //all other cases: allow entry
241
+ // - no steps done (fresh entry or pre-step-1 crash recovery)
242
+ // - step 1 done, step 2 not (crash after step 1)
243
+ // - steps 1+2 done, job closed, step 3 not (crash recovery for step 3)
176
244
  }
245
+ /**
246
+ * Verifies the activity ledger value at entry boundaries.
247
+ *
248
+ * Leg1 enter: pos 3 (1T digit) must be > 0 after +1T (proves seed exists).
249
+ * pos 4 (100B) must be 0 (Leg1 not yet complete).
250
+ * If pos 3 > 1 and pos 4 == 1, it's a stale/replayed message.
251
+ *
252
+ * Leg2 enter: pos 4 (100B) must be > 0 (Leg1 complete, reentry authorized).
253
+ * pos 1 (100T) must be < 2 (not finalized) — cycle activities exempt.
254
+ */
177
255
  static verifyInteger(amount, leg, stage) {
178
256
  let faultType;
179
257
  if (leg === 1 && stage === 'enter') {
180
- if (!this.isPrimed(amount, 1)) {
258
+ const leg1Attempts = this.getDigitAtPosition(amount, 3);
259
+ const leg1Complete = this.getDigitAtPosition(amount, 4);
260
+ if (leg1Attempts === 0) {
261
+ //seed was not set (no authorization)
181
262
  faultType = collator_1.CollationFaultType.MISSING;
182
263
  }
183
- else if (this.isDuplicate(amount, 0)) {
184
- faultType = collator_1.CollationFaultType.DUPLICATE;
185
- }
186
- else if (amount != 899000000000000) {
187
- faultType = collator_1.CollationFaultType.INVALID;
188
- }
189
- }
190
- else if (leg === 1 && stage === 'exit') {
191
- if (amount === -10000000000000) {
192
- faultType = collator_1.CollationFaultType.MISSING;
193
- }
194
- else if (this.isDuplicate(amount, 1)) {
264
+ else if (leg1Complete > 0) {
265
+ //Leg1 already completed — stale/replayed message
195
266
  faultType = collator_1.CollationFaultType.DUPLICATE;
196
267
  }
197
268
  }
198
269
  else if (leg === 2 && stage === 'enter') {
199
- if (!this.isPrimed(amount, 2)) {
270
+ const leg1Complete = this.getDigitAtPosition(amount, 4);
271
+ const finalized = this.getDigitAtPosition(amount, 1);
272
+ if (leg1Complete === 0) {
273
+ //Leg1 not complete — reentry not authorized
200
274
  faultType = collator_1.CollationFaultType.FORBIDDEN;
201
275
  }
202
- else if (this.isInactive(amount)) {
276
+ else if (finalized >= 2) {
277
+ //activity finalized (pos 1 = 2) — no new Leg2 GUIDs accepted
203
278
  faultType = collator_1.CollationFaultType.INACTIVE;
204
279
  }
205
280
  }
@@ -207,6 +282,9 @@ class CollatorService {
207
282
  throw new errors_1.CollationError(amount, leg, stage, faultType);
208
283
  }
209
284
  }
285
+ // ──────────────────────────────────────────────────
286
+ // Dimensional address resolution
287
+ // ──────────────────────────────────────────────────
210
288
  static getDimensionsById(ancestors, dad) {
211
289
  //ancestors is an ordered list of all ancestors, starting with the trigger (['t1', 'a1', 'a2'])
212
290
  //dad is the dimensional address of the ancestors list (',0,5,3')
@@ -221,18 +299,19 @@ class CollatorService {
221
299
  });
222
300
  return map;
223
301
  }
302
+ // ──────────────────────────────────────────────────
303
+ // Seeds
304
+ // ──────────────────────────────────────────────────
224
305
  /**
225
- * All non-trigger activities are assigned a status seed by their parent
226
- */
227
- static getSeed() {
228
- return '999000000000000';
229
- }
230
- /**
231
- * All trigger activities are assigned a status seed in a completed state
306
+ * All trigger activities are assigned a status seed in a completed state.
307
+ * Seed: 101100000000001 (authorized, 1 Leg1 entry, Leg1 complete, 1 Leg2 entry)
232
308
  */
233
309
  static getTriggerSeed() {
234
- return '888000001000001';
310
+ return '101100000000001';
235
311
  }
312
+ // ──────────────────────────────────────────────────
313
+ // Compiler
314
+ // ──────────────────────────────────────────────────
236
315
  /**
237
316
  * entry point for compiler-type activities. This is called by the compiler
238
317
  * to bind the sorted activity IDs to the trigger activity. These are then used
@@ -291,3 +370,20 @@ class CollatorService {
291
370
  exports.CollatorService = CollatorService;
292
371
  //max int digit count that supports `hincrby`
293
372
  CollatorService.targetLength = 15;
373
+ /**
374
+ * Positional weights for the 15-digit activity/GUID ledger.
375
+ *
376
+ * Position: 1 2 3 4 5 6 7 8-15
377
+ * Weight: 100T 10T 1T 100B 10B 1B 100M 10M..1
378
+ */
379
+ CollatorService.WEIGHTS = {
380
+ AUTH: 100000000000000,
381
+ FINALIZE: 200000000000000,
382
+ LEG1_ENTRY: 1000000000000,
383
+ LEG1_COMPLETE: 100000000000,
384
+ STEP1_WORK: 10000000000,
385
+ STEP2_SPAWN: 1000000000,
386
+ STEP3_CLEANUP: 100000000,
387
+ LEG2_ENTRY: 1,
388
+ GUID_SNAPSHOT: 100000000000, // 100B on GUID ledger (job closed snapshot)
389
+ };
@@ -3,7 +3,7 @@ import { ProviderConfig, ProviderNativeClient } from '../../types/provider';
3
3
  export declare class ConnectorService {
4
4
  static disconnectAll(): Promise<void>;
5
5
  /**
6
- * Connect to a provider (redis, nats, postgres) and return the native
6
+ * Connect to a provider (nats, postgres) and return the native
7
7
  * client. Connections are handled by the engine and worker routers at
8
8
  * initialization, but the factory method provided here is useful
9
9
  * for testing provider configurations.
@@ -2,19 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ConnectorService = void 0;
4
4
  const utils_1 = require("../../modules/utils");
5
- const ioredis_1 = require("./providers/ioredis");
6
5
  const nats_1 = require("./providers/nats");
7
6
  const postgres_1 = require("./providers/postgres");
8
- const redis_1 = require("./providers/redis");
9
7
  class ConnectorService {
10
8
  static async disconnectAll() {
11
- await redis_1.RedisConnection.disconnectAll();
12
- await ioredis_1.RedisConnection.disconnectAll();
13
9
  await postgres_1.PostgresConnection.disconnectAll();
14
10
  await nats_1.NatsConnection.disconnectAll();
15
11
  }
16
12
  /**
17
- * Connect to a provider (redis, nats, postgres) and return the native
13
+ * Connect to a provider (nats, postgres) and return the native
18
14
  * client. Connections are handled by the engine and worker routers at
19
15
  * initialization, but the factory method provided here is useful
20
16
  * for testing provider configurations.
@@ -81,12 +77,6 @@ class ConnectorService {
81
77
  let clientInstance;
82
78
  const id = (0, utils_1.guid)();
83
79
  switch (providerType) {
84
- case 'redis':
85
- clientInstance = await redis_1.RedisConnection.connect(id, providerClass, options, { provider: providerName });
86
- break;
87
- case 'ioredis':
88
- clientInstance = await ioredis_1.RedisConnection.connect(id, providerClass, options, { provider: providerName });
89
- break;
90
80
  case 'nats':
91
81
  clientInstance = await nats_1.NatsConnection.connect(id, providerClass, options, { provider: providerName });
92
82
  break;
@@ -31,11 +31,17 @@ declare class EngineService {
31
31
  appId: string;
32
32
  guid: string;
33
33
  exporter: ExporterService | null;
34
+ /** @hidden */
34
35
  search: SearchService<ProviderClient> | null;
36
+ /** @hidden */
35
37
  store: StoreService<ProviderClient, ProviderTransaction> | null;
38
+ /** @hidden */
36
39
  stream: StreamService<ProviderClient, ProviderTransaction> | null;
40
+ /** @hidden */
37
41
  subscribe: SubService<ProviderClient> | null;
42
+ /** @hidden */
38
43
  router: Router<typeof this.stream> | null;
44
+ /** @hidden */
39
45
  taskService: TaskService | null;
40
46
  logger: ILogger;
41
47
  cacheMode: CacheMode;
@@ -143,7 +149,7 @@ declare class EngineService {
143
149
  /**
144
150
  * @private
145
151
  */
146
- execAdjacentParent(context: JobState, jobOutput: JobOutput, emit?: boolean): Promise<string>;
152
+ execAdjacentParent(context: JobState, jobOutput: JobOutput, emit?: boolean, transaction?: ProviderTransaction): Promise<string>;
147
153
  /**
148
154
  * @private
149
155
  */
@@ -161,17 +167,27 @@ declare class EngineService {
161
167
  */
162
168
  scrub(jobId: string): Promise<void>;
163
169
  /**
170
+ * Delivers a signal (data payload) to a paused hook activity,
171
+ * resuming its Leg 2 execution. The `topic` must match a hook rule
172
+ * defined in the YAML graph's `hooks` section. The engine locates
173
+ * the target activity and dimension for reentry based on the hook
174
+ * rule's match conditions.
175
+ *
164
176
  * @private
165
177
  */
166
- hook(topic: string, data: JobData, status?: StreamStatus, code?: StreamCode): Promise<string>;
178
+ signal(topic: string, data: JobData, status?: StreamStatus, code?: StreamCode, transaction?: ProviderTransaction): Promise<string>;
167
179
  /**
168
180
  * @private
169
181
  */
170
182
  hookTime(jobId: string, gId: string, topicOrActivity: string, type?: WorkListTaskType): Promise<string | void>;
171
183
  /**
184
+ * Fan-out variant of `signal()` that delivers data to **all**
185
+ * paused workflows matching a search query. Useful for resuming
186
+ * a batch of workflows waiting on the same external event.
187
+ *
172
188
  * @private
173
189
  */
174
- hookAll(hookTopic: string, data: JobData, keyResolver: JobStatsInput, queryFacets?: string[]): Promise<string[]>;
190
+ signalAll(hookTopic: string, data: JobData, keyResolver: JobStatsInput, queryFacets?: string[]): Promise<string[]>;
175
191
  /**
176
192
  * @private
177
193
  */
@@ -199,7 +215,7 @@ declare class EngineService {
199
215
  /**
200
216
  * @private
201
217
  */
202
- pubOneTimeSubs(context: JobState, jobOutput: JobOutput, emit?: boolean): Promise<void>;
218
+ pubOneTimeSubs(context: JobState, jobOutput: JobOutput, emit?: boolean, transaction?: ProviderTransaction): Promise<void>;
203
219
  /**
204
220
  * @private
205
221
  */
@@ -207,7 +223,7 @@ declare class EngineService {
207
223
  /**
208
224
  * @private
209
225
  */
210
- pubPermSubs(context: JobState, jobOutput: JobOutput, emit?: boolean): Promise<void>;
226
+ pubPermSubs(context: JobState, jobOutput: JobOutput, emit?: boolean, transaction?: ProviderTransaction): Promise<void>;
211
227
  /**
212
228
  * @private
213
229
  */
@@ -227,7 +243,7 @@ declare class EngineService {
227
243
  /**
228
244
  * @private
229
245
  */
230
- runJobCompletionTasks(context: JobState, options?: JobCompletionOptions): Promise<string | void>;
246
+ runJobCompletionTasks(context: JobState, options?: JobCompletionOptions, transaction?: ProviderTransaction): Promise<string | void>;
231
247
  /**
232
248
  * Job hash expiration is typically reliant on the metadata field
233
249
  * if the activity concludes normally. However, if the job is `interrupted`,