@hotmeshio/hotmesh 0.7.0 → 0.8.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 (73) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/README.md +1 -1
  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 +16 -27
  7. package/build/services/activities/activity.d.ts +43 -6
  8. package/build/services/activities/activity.js +262 -54
  9. package/build/services/activities/await.js +2 -2
  10. package/build/services/activities/cycle.js +1 -1
  11. package/build/services/activities/hook.d.ts +5 -0
  12. package/build/services/activities/hook.js +22 -19
  13. package/build/services/activities/interrupt.js +17 -25
  14. package/build/services/activities/signal.d.ts +4 -2
  15. package/build/services/activities/signal.js +27 -24
  16. package/build/services/activities/worker.js +2 -2
  17. package/build/services/collator/index.d.ts +123 -25
  18. package/build/services/collator/index.js +224 -101
  19. package/build/services/connector/factory.d.ts +1 -1
  20. package/build/services/connector/factory.js +1 -11
  21. package/build/services/engine/index.d.ts +5 -5
  22. package/build/services/engine/index.js +36 -15
  23. package/build/services/router/consumption/index.js +1 -1
  24. package/build/services/search/factory.js +1 -9
  25. package/build/services/store/factory.js +1 -9
  26. package/build/services/store/index.d.ts +5 -0
  27. package/build/services/store/providers/postgres/kvsql.d.ts +4 -0
  28. package/build/services/store/providers/postgres/kvsql.js +4 -0
  29. package/build/services/store/providers/postgres/kvtransaction.d.ts +2 -0
  30. package/build/services/store/providers/postgres/kvtransaction.js +23 -0
  31. package/build/services/store/providers/postgres/kvtypes/hash/basic.d.ts +51 -0
  32. package/build/services/store/providers/postgres/kvtypes/hash/basic.js +193 -1
  33. package/build/services/store/providers/postgres/kvtypes/hash/index.d.ts +4 -0
  34. package/build/services/store/providers/postgres/kvtypes/hash/index.js +6 -0
  35. package/build/services/store/providers/postgres/postgres.d.ts +20 -0
  36. package/build/services/store/providers/postgres/postgres.js +38 -1
  37. package/build/services/stream/factory.js +1 -17
  38. package/build/services/stream/providers/postgres/scout.js +2 -2
  39. package/build/services/sub/factory.js +1 -9
  40. package/build/services/sub/index.d.ts +1 -1
  41. package/build/services/sub/providers/postgres/postgres.d.ts +1 -1
  42. package/build/services/sub/providers/postgres/postgres.js +25 -10
  43. package/build/services/task/index.d.ts +1 -1
  44. package/build/services/task/index.js +2 -6
  45. package/build/types/index.d.ts +0 -1
  46. package/build/types/index.js +1 -4
  47. package/build/types/provider.d.ts +1 -1
  48. package/index.ts +0 -4
  49. package/package.json +16 -27
  50. package/build/services/connector/providers/ioredis.d.ts +0 -9
  51. package/build/services/connector/providers/ioredis.js +0 -26
  52. package/build/services/connector/providers/redis.d.ts +0 -9
  53. package/build/services/connector/providers/redis.js +0 -38
  54. package/build/services/search/providers/redis/ioredis.d.ts +0 -23
  55. package/build/services/search/providers/redis/ioredis.js +0 -189
  56. package/build/services/search/providers/redis/redis.d.ts +0 -23
  57. package/build/services/search/providers/redis/redis.js +0 -202
  58. package/build/services/store/providers/redis/_base.d.ts +0 -137
  59. package/build/services/store/providers/redis/_base.js +0 -980
  60. package/build/services/store/providers/redis/ioredis.d.ts +0 -20
  61. package/build/services/store/providers/redis/ioredis.js +0 -190
  62. package/build/services/store/providers/redis/redis.d.ts +0 -18
  63. package/build/services/store/providers/redis/redis.js +0 -199
  64. package/build/services/stream/providers/redis/ioredis.d.ts +0 -61
  65. package/build/services/stream/providers/redis/ioredis.js +0 -272
  66. package/build/services/stream/providers/redis/redis.d.ts +0 -61
  67. package/build/services/stream/providers/redis/redis.js +0 -305
  68. package/build/services/sub/providers/redis/ioredis.d.ts +0 -20
  69. package/build/services/sub/providers/redis/ioredis.js +0 -161
  70. package/build/services/sub/providers/redis/redis.d.ts +0 -18
  71. package/build/services/sub/providers/redis/redis.js +0 -148
  72. package/build/types/redis.d.ts +0 -258
  73. package/build/types/redis.js +0 -11
@@ -20,34 +20,36 @@ class Signal extends activity_1.Activity {
20
20
  });
21
21
  let telemetry;
22
22
  try {
23
- await this.verifyEntry();
23
+ //Category B: entry with step resume
24
+ await this.verifyLeg1Entry();
24
25
  telemetry = new telemetry_1.TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
25
26
  telemetry.startActivitySpan(this.leg);
26
- //save state and notarize early completion (signals only run leg1)
27
- const transaction = this.store.transact();
28
27
  this.adjacencyList = await this.filterAdjacent();
29
28
  this.mapOutputData();
30
29
  this.mapJobData();
31
- await this.setState(transaction);
32
- await collator_1.CollatorService.notarizeEarlyCompletion(this, transaction);
33
- await this.setStatus(this.adjacencyList.length - 1, transaction);
34
- const multiResponse = (await transaction.exec());
35
- //todo: this should execute BEFORE the status is decremented
36
- if (this.config.subtype === 'all') {
37
- await this.hookAll();
38
- }
39
- else {
40
- await this.hookOne();
41
- }
42
- //transition to adjacent activities
43
- const jobStatus = this.resolveStatus(multiResponse);
44
- const attrs = { 'app.job.jss': jobStatus };
45
- const messageIds = await this.transition(this.adjacencyList, jobStatus);
46
- if (messageIds.length) {
47
- attrs['app.activity.mids'] = messageIds.join(',');
30
+ //Step A: Bundle signal hook with Leg1 completion marker.
31
+ //hookOne is transactional; hookAll is best-effort (complex multi-step).
32
+ if (!collator_1.CollatorService.isGuidStep1Done(this.guidLedger)) {
33
+ const txn1 = this.store.transact();
34
+ if (this.config.subtype === 'all') {
35
+ await this.hookAll();
36
+ }
37
+ else {
38
+ await this.hookOne(txn1);
39
+ }
40
+ await this.setState(txn1);
41
+ if (this.adjacentIndex === 0) {
42
+ await collator_1.CollatorService.notarizeLeg1Completion(this, txn1);
43
+ }
44
+ await collator_1.CollatorService.notarizeStep1(this, this.context.metadata.guid, txn1);
45
+ await txn1.exec();
46
+ //update in-memory guidLedger so executeLeg1StepProtocol skips Step A
47
+ this.guidLedger += collator_1.CollatorService.WEIGHTS.STEP1_WORK;
48
48
  }
49
+ //Steps B and C: spawn children + semaphore + edge capture
50
+ await this.executeLeg1StepProtocol(this.adjacencyList.length - 1);
49
51
  telemetry.mapActivityAttributes();
50
- telemetry.setActivityAttributes(attrs);
52
+ telemetry.setActivityAttributes({});
51
53
  return this.context.metadata.aid;
52
54
  }
53
55
  catch (error) {
@@ -102,14 +104,15 @@ class Signal extends activity_1.Activity {
102
104
  }
103
105
  }
104
106
  /**
105
- * The signal activity will hook one
107
+ * The signal activity will hook one. Accepts an optional transaction
108
+ * so the hook publish can be bundled with the Leg1 completion marker.
106
109
  */
107
- async hookOne() {
110
+ async hookOne(transaction) {
108
111
  const topic = pipe_1.Pipe.resolve(this.config.topic, this.context);
109
112
  const signalInputData = this.mapSignalData();
110
113
  const status = pipe_1.Pipe.resolve(this.config.status, this.context);
111
114
  const code = pipe_1.Pipe.resolve(this.config.code, this.context);
112
- return await this.engine.hook(topic, signalInputData, status, code);
115
+ return await this.engine.hook(topic, signalInputData, status, code, transaction);
113
116
  }
114
117
  /**
115
118
  * The signal activity will hook all paused jobs that share the same job key.
@@ -24,11 +24,11 @@ class Worker extends activity_1.Activity {
24
24
  telemetry = new telemetry_1.TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
25
25
  telemetry.startActivitySpan(this.leg);
26
26
  this.mapInputData();
27
- //save state and authorize reentry
27
+ //save state and mark Leg1 complete
28
28
  const transaction = this.store.transact();
29
29
  //todo: await this.registerTimeout();
30
30
  const messageId = await this.execActivity(transaction);
31
- await collator_1.CollatorService.authorizeReentry(this, transaction);
31
+ await collator_1.CollatorService.notarizeLeg1Completion(this, transaction);
32
32
  await this.setState(transaction);
33
33
  await this.setStatus(0, transaction);
34
34
  const txResponse = (await transaction.exec());
@@ -6,6 +6,23 @@ import { Activity } from '../activities/activity';
6
6
  import { Cycle } from '../activities/cycle';
7
7
  declare class CollatorService {
8
8
  static targetLength: number;
9
+ /**
10
+ * Positional weights for the 15-digit activity/GUID ledger.
11
+ *
12
+ * Position: 1 2 3 4 5 6 7 8-15
13
+ * Weight: 100T 10T 1T 100B 10B 1B 100M 10M..1
14
+ */
15
+ static WEIGHTS: {
16
+ AUTH: number;
17
+ FINALIZE: number;
18
+ LEG1_ENTRY: number;
19
+ LEG1_COMPLETE: number;
20
+ STEP1_WORK: number;
21
+ STEP2_SPAWN: number;
22
+ STEP3_CLEANUP: number;
23
+ LEG2_ENTRY: number;
24
+ GUID_SNAPSHOT: number;
25
+ };
9
26
  /**
10
27
  * Upon re/entry, verify that the job status is active
11
28
  */
@@ -26,12 +43,60 @@ declare class CollatorService {
26
43
  * the origin node can be queried for approval/entry.
27
44
  */
28
45
  static resolveReentryDimension(activity: Cycle): string;
46
+ /**
47
+ * Leg1 entry: increment attempt counter (+1T).
48
+ * NOT bundled with Leg1 work — exists only to mark entry.
49
+ */
29
50
  static notarizeEntry(activity: Activity, transaction?: ProviderTransaction): Promise<number>;
30
- static authorizeReentry(activity: Activity, transaction?: ProviderTransaction): Promise<number>;
51
+ /**
52
+ * Leg1 completion: increment +100B to mark Leg1 complete.
53
+ * For cycle=true activities, also pre-seeds the Leg2 entry counter (+1)
54
+ * so the first real Leg2 gets adjacentIndex=1 (new dimension).
55
+ * MUST be bundled in the same transaction as Leg1 durable work.
56
+ */
57
+ static notarizeLeg1Completion(activity: Activity, transaction?: ProviderTransaction): Promise<number>;
58
+ /**
59
+ * Leg1 early exit: marks Leg1 complete for activities that
60
+ * only run Leg1 and fully close (e.g., Cycle).
61
+ * Increment +100B (Leg1 completion marker).
62
+ */
31
63
  static notarizeEarlyExit(activity: Activity, transaction?: ProviderTransaction): Promise<number>;
64
+ /**
65
+ * Leg1 early completion: marks Leg1 complete for Leg1-only
66
+ * activities that spawn children (e.g., Signal, Hook passthrough,
67
+ * Interrupt-another). Increment +100B.
68
+ */
32
69
  static notarizeEarlyCompletion(activity: Activity, transaction?: ProviderTransaction): Promise<number>;
33
70
  /**
34
- * sets the synthetic inception key (in case of an overage occurs).
71
+ * Leg2 entry: atomically increments the activity ledger (+1) and
72
+ * seeds the GUID ledger with the ordinal IF NOT EXISTS.
73
+ * Returns [activityLedger, guidLedger] after the compound operation.
74
+ */
75
+ static notarizeLeg2Entry(activity: Activity, guid: string, transaction?: ProviderTransaction): Promise<[number, number]>;
76
+ /**
77
+ * Step 1: Mark Leg2 work done (+10B on GUID ledger only).
78
+ * MUST be bundled with Leg2 durable work writes.
79
+ */
80
+ static notarizeStep1(activity: Activity, guid: string, transaction: ProviderTransaction): Promise<void>;
81
+ /**
82
+ * Step 2: Mark children spawned (+1B on GUID ledger only).
83
+ * The job semaphore update and GUID job-closed snapshot are handled
84
+ * by the compound `setStatusAndCollateGuid` primitive, which MUST
85
+ * be called in the same transaction.
86
+ */
87
+ static notarizeStep2(activity: Activity, guid: string, transaction: ProviderTransaction): Promise<void>;
88
+ /**
89
+ * Step 3: Mark job completion tasks done (+100M on GUID ledger only).
90
+ * MUST be bundled with job completion durable writes.
91
+ */
92
+ static notarizeStep3(activity: Activity, guid: string, transaction: ProviderTransaction): Promise<void>;
93
+ /**
94
+ * Finalize: close the activity to new Leg2 GUIDs (+10T).
95
+ * Only for non-cycle activities after final SUCCESS/ERROR.
96
+ */
97
+ static notarizeFinalize(activity: Activity, transaction: ProviderTransaction): Promise<void>;
98
+ /**
99
+ * sets the synthetic inception key (in case an overage occurs).
35
100
  */
36
101
  static notarizeInception(activity: Activity, guid: string, transaction: ProviderTransaction): Promise<void>;
37
102
  /**
@@ -39,40 +104,73 @@ declare class CollatorService {
39
104
  */
40
105
  static isInceptionOverage(activity: Activity, guid: string): Promise<boolean>;
41
106
  /**
42
- * verifies both the concrete and synthetic keys for the activity; concrete keys
43
- * exist in the original model and are effectively the 'real' keys. In reality,
44
- * hook activities are atomized during compilation to create a synthetic DAG that
45
- * is used to track the status of the graph in a distributed environment. The
46
- * synthetic key represents different dimensional realities and is used to
47
- * track re-entry overages (it distinguishes between the original and re-entry).
48
- * The essential challenge is: is this a re-entry that is purposeful in
49
- * order to induce cycles, or is the re-entry due to a failure in the system?
50
- */
51
- static notarizeReentry(activity: Activity, guid: string, transaction?: ProviderTransaction): Promise<number>;
52
- static notarizeContinuation(activity: Activity, transaction?: ProviderTransaction): Promise<number>;
53
- static notarizeCompletion(activity: Activity, transaction?: ProviderTransaction): Promise<number>;
107
+ * Check if Step 1 (work done) is complete on the GUID ledger.
108
+ * Position 5 (10B digit) > 0.
109
+ */
110
+ static isGuidStep1Done(guidLedger: number): boolean;
111
+ /**
112
+ * Check if Step 2 (children spawned) is complete on the GUID ledger.
113
+ * Position 6 (1B digit) > 0.
114
+ */
115
+ static isGuidStep2Done(guidLedger: number): boolean;
116
+ /**
117
+ * Check if Step 3 (job completion tasks) is complete on the GUID ledger.
118
+ * Position 7 (100M digit) > 0.
119
+ */
120
+ static isGuidStep3Done(guidLedger: number): boolean;
121
+ /**
122
+ * Check if this GUID was responsible for closing the job.
123
+ * Position 4 (100B digit) > 0 (job closed snapshot).
124
+ */
125
+ static isGuidJobClosed(guidLedger: number): boolean;
126
+ /**
127
+ * Get the attempt count from the GUID ledger (last 8 digits).
128
+ */
129
+ static getGuidAttemptCount(guidLedger: number): number;
130
+ /**
131
+ * Gets the digit at a 1-indexed position from a 15-digit ledger value.
132
+ * The value is left-padded to 15 digits before extraction.
133
+ */
134
+ static getDigitAtPosition(num: number, position: number): number;
135
+ /**
136
+ * @deprecated Use getDigitAtPosition (1-indexed) instead
137
+ */
54
138
  static getDigitAtIndex(num: number, targetDigitIndex: number): number | null;
139
+ /**
140
+ * Extracts the dimensional index from the Leg2 entry counter.
141
+ * Non-cycle activities: first Leg2 → leg2Count=1 → 1-1=0 (same dimension as Leg1).
142
+ * Cycle activities: first Leg2 → leg2Count=2 (pre-seeded +1) → 2-1=1 (new dimension).
143
+ */
55
144
  static getDimensionalIndex(num: number): number | null;
56
- static isDuplicate(num: number, targetDigitIndex: number): boolean;
57
- static isInactive(num: number): boolean;
58
- static isPrimed(amount: number, leg: ActivityDuplex): boolean;
59
145
  /**
60
- * During compilation, the graphs are compiled into structures necessary
61
- * for distributed processing; these are referred to as 'synthetic DAGs',
62
- * because they are not part of the original graph, but are used to track
63
- * the status of the graph in a distributed environment. This check ensures
64
- * that the 'synthetic key' is not a duplicate. (which is different than
65
- * saying the 'key' is not a duplicate)
146
+ * Verifies the GUID ledger value for step-level resume decisions.
147
+ * The GUID ledger is seeded with an ordinal position (last 8 digits)
148
+ * on first entry; step markers drive all resume/reject logic.
149
+ *
150
+ * Fully processed: Step 3 done, or Steps 1+2 done without job closure.
151
+ * Crash recovery: Any incomplete step combination is allowed for resume.
66
152
  */
67
153
  static verifySyntheticInteger(amount: number): void;
154
+ /**
155
+ * Verifies the activity ledger value at entry boundaries.
156
+ *
157
+ * Leg1 enter: pos 3 (1T digit) must be > 0 after +1T (proves seed exists).
158
+ * pos 4 (100B) must be 0 (Leg1 not yet complete).
159
+ * If pos 3 > 1 and pos 4 == 1, it's a stale/replayed message.
160
+ *
161
+ * Leg2 enter: pos 4 (100B) must be > 0 (Leg1 complete, reentry authorized).
162
+ * pos 2 (10T) must be 0 (not finalized) — cycle activities exempt.
163
+ */
68
164
  static verifyInteger(amount: number, leg: ActivityDuplex, stage: CollationStage): void;
69
165
  static getDimensionsById(ancestors: string[], dad: string): Record<string, string>;
70
166
  /**
71
- * All non-trigger activities are assigned a status seed by their parent
167
+ * All non-trigger activities are assigned a status seed by their parent.
168
+ * Seed: 100000000000000 (pos 1 = 1, authorized for entry)
72
169
  */
73
170
  static getSeed(): string;
74
171
  /**
75
- * All trigger activities are assigned a status seed in a completed state
172
+ * All trigger activities are assigned a status seed in a completed state.
173
+ * Seed: 101100000000001 (authorized, 1 Leg1 entry, Leg1 complete, 1 Leg2 entry)
76
174
  */
77
175
  static getTriggerSeed(): string;
78
176
  /**