@hotmeshio/hotmesh 0.3.32 → 0.4.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 (254) hide show
  1. package/README.md +128 -823
  2. package/build/index.d.ts +9 -9
  3. package/build/index.js +10 -10
  4. package/build/package.json +22 -21
  5. package/build/types/error.d.ts +5 -5
  6. package/build/types/exporter.d.ts +1 -1
  7. package/build/types/index.d.ts +3 -3
  8. package/build/types/manifest.d.ts +2 -2
  9. package/build/types/{meshflow.d.ts → memflow.d.ts} +15 -15
  10. package/build/types/meshdata.d.ts +3 -3
  11. package/build/types/postgres.d.ts +7 -0
  12. package/build/types/stream.d.ts +3 -0
  13. package/index.ts +11 -11
  14. package/package.json +22 -21
  15. package/typedoc.json +8 -7
  16. package/types/error.ts +5 -5
  17. package/types/exporter.ts +1 -1
  18. package/types/index.ts +7 -7
  19. package/types/manifest.ts +2 -2
  20. package/types/{meshflow.ts → memflow.ts} +15 -15
  21. package/types/meshdata.ts +3 -3
  22. package/types/postgres.ts +9 -0
  23. package/types/stream.ts +4 -0
  24. package/build/modules/enums.d.ts +0 -110
  25. package/build/modules/enums.js +0 -134
  26. package/build/modules/errors.d.ts +0 -124
  27. package/build/modules/errors.js +0 -191
  28. package/build/modules/key.d.ts +0 -66
  29. package/build/modules/key.js +0 -1
  30. package/build/modules/storage.d.ts +0 -3
  31. package/build/modules/storage.js +0 -5
  32. package/build/modules/utils.d.ts +0 -119
  33. package/build/modules/utils.js +0 -1
  34. package/build/services/activities/activity.d.ts +0 -104
  35. package/build/services/activities/activity.js +0 -1
  36. package/build/services/activities/await.d.ts +0 -12
  37. package/build/services/activities/await.js +0 -1
  38. package/build/services/activities/cycle.d.ts +0 -19
  39. package/build/services/activities/cycle.js +0 -1
  40. package/build/services/activities/hook.d.ts +0 -27
  41. package/build/services/activities/hook.js +0 -1
  42. package/build/services/activities/index.d.ts +0 -19
  43. package/build/services/activities/index.js +0 -1
  44. package/build/services/activities/interrupt.d.ts +0 -16
  45. package/build/services/activities/interrupt.js +0 -1
  46. package/build/services/activities/signal.d.ts +0 -20
  47. package/build/services/activities/signal.js +0 -1
  48. package/build/services/activities/trigger.d.ts +0 -37
  49. package/build/services/activities/trigger.js +0 -1
  50. package/build/services/activities/worker.d.ts +0 -12
  51. package/build/services/activities/worker.js +0 -1
  52. package/build/services/collator/index.d.ts +0 -111
  53. package/build/services/collator/index.js +0 -1
  54. package/build/services/compiler/deployer.d.ts +0 -40
  55. package/build/services/compiler/deployer.js +0 -1
  56. package/build/services/compiler/index.d.ts +0 -32
  57. package/build/services/compiler/index.js +0 -1
  58. package/build/services/compiler/validator.d.ts +0 -34
  59. package/build/services/compiler/validator.js +0 -1
  60. package/build/services/connector/factory.d.ts +0 -22
  61. package/build/services/connector/factory.js +0 -99
  62. package/build/services/connector/index.d.ts +0 -30
  63. package/build/services/connector/index.js +0 -54
  64. package/build/services/connector/providers/ioredis.d.ts +0 -9
  65. package/build/services/connector/providers/ioredis.js +0 -26
  66. package/build/services/connector/providers/nats.d.ts +0 -9
  67. package/build/services/connector/providers/nats.js +0 -34
  68. package/build/services/connector/providers/postgres.d.ts +0 -20
  69. package/build/services/connector/providers/postgres.js +0 -102
  70. package/build/services/connector/providers/redis.d.ts +0 -9
  71. package/build/services/connector/providers/redis.js +0 -38
  72. package/build/services/engine/index.d.ts +0 -264
  73. package/build/services/engine/index.js +0 -1
  74. package/build/services/exporter/index.d.ts +0 -44
  75. package/build/services/exporter/index.js +0 -1
  76. package/build/services/hotmesh/index.d.ts +0 -340
  77. package/build/services/hotmesh/index.js +0 -479
  78. package/build/services/logger/index.d.ts +0 -16
  79. package/build/services/logger/index.js +0 -54
  80. package/build/services/mapper/index.d.ts +0 -28
  81. package/build/services/mapper/index.js +0 -1
  82. package/build/services/meshcall/index.d.ts +0 -194
  83. package/build/services/meshcall/index.js +0 -452
  84. package/build/services/meshcall/schemas/factory.d.ts +0 -9
  85. package/build/services/meshcall/schemas/factory.js +0 -189
  86. package/build/services/meshdata/index.d.ts +0 -795
  87. package/build/services/meshdata/index.js +0 -1235
  88. package/build/services/meshflow/client.d.ts +0 -108
  89. package/build/services/meshflow/client.js +0 -371
  90. package/build/services/meshflow/connection.d.ts +0 -23
  91. package/build/services/meshflow/connection.js +0 -33
  92. package/build/services/meshflow/exporter.d.ts +0 -51
  93. package/build/services/meshflow/exporter.js +0 -1
  94. package/build/services/meshflow/handle.d.ts +0 -90
  95. package/build/services/meshflow/handle.js +0 -176
  96. package/build/services/meshflow/index.d.ts +0 -111
  97. package/build/services/meshflow/index.js +0 -117
  98. package/build/services/meshflow/schemas/factory.d.ts +0 -29
  99. package/build/services/meshflow/schemas/factory.js +0 -2492
  100. package/build/services/meshflow/search.d.ts +0 -142
  101. package/build/services/meshflow/search.js +0 -320
  102. package/build/services/meshflow/worker.d.ts +0 -124
  103. package/build/services/meshflow/worker.js +0 -514
  104. package/build/services/meshflow/workflow/all.d.ts +0 -7
  105. package/build/services/meshflow/workflow/all.js +0 -15
  106. package/build/services/meshflow/workflow/common.d.ts +0 -18
  107. package/build/services/meshflow/workflow/common.js +0 -45
  108. package/build/services/meshflow/workflow/context.d.ts +0 -6
  109. package/build/services/meshflow/workflow/context.js +0 -45
  110. package/build/services/meshflow/workflow/didRun.d.ts +0 -7
  111. package/build/services/meshflow/workflow/didRun.js +0 -22
  112. package/build/services/meshflow/workflow/emit.d.ts +0 -11
  113. package/build/services/meshflow/workflow/emit.js +0 -29
  114. package/build/services/meshflow/workflow/enrich.d.ts +0 -9
  115. package/build/services/meshflow/workflow/enrich.js +0 -17
  116. package/build/services/meshflow/workflow/execChild.d.ts +0 -18
  117. package/build/services/meshflow/workflow/execChild.js +0 -102
  118. package/build/services/meshflow/workflow/hook.d.ts +0 -9
  119. package/build/services/meshflow/workflow/hook.js +0 -40
  120. package/build/services/meshflow/workflow/index.d.ts +0 -70
  121. package/build/services/meshflow/workflow/index.js +0 -83
  122. package/build/services/meshflow/workflow/interrupt.d.ts +0 -9
  123. package/build/services/meshflow/workflow/interrupt.js +0 -24
  124. package/build/services/meshflow/workflow/isSideEffectAllowed.d.ts +0 -10
  125. package/build/services/meshflow/workflow/isSideEffectAllowed.js +0 -33
  126. package/build/services/meshflow/workflow/proxyActivities.d.ts +0 -20
  127. package/build/services/meshflow/workflow/proxyActivities.js +0 -97
  128. package/build/services/meshflow/workflow/random.d.ts +0 -6
  129. package/build/services/meshflow/workflow/random.js +0 -16
  130. package/build/services/meshflow/workflow/searchMethods.d.ts +0 -6
  131. package/build/services/meshflow/workflow/searchMethods.js +0 -25
  132. package/build/services/meshflow/workflow/signal.d.ts +0 -7
  133. package/build/services/meshflow/workflow/signal.js +0 -28
  134. package/build/services/meshflow/workflow/sleepFor.d.ts +0 -8
  135. package/build/services/meshflow/workflow/sleepFor.js +0 -35
  136. package/build/services/meshflow/workflow/trace.d.ts +0 -14
  137. package/build/services/meshflow/workflow/trace.js +0 -33
  138. package/build/services/meshflow/workflow/waitFor.d.ts +0 -8
  139. package/build/services/meshflow/workflow/waitFor.js +0 -35
  140. package/build/services/meshos/index.d.ts +0 -293
  141. package/build/services/meshos/index.js +0 -547
  142. package/build/services/pipe/functions/array.d.ts +0 -17
  143. package/build/services/pipe/functions/array.js +0 -1
  144. package/build/services/pipe/functions/bitwise.d.ts +0 -9
  145. package/build/services/pipe/functions/bitwise.js +0 -1
  146. package/build/services/pipe/functions/conditional.d.ts +0 -13
  147. package/build/services/pipe/functions/conditional.js +0 -1
  148. package/build/services/pipe/functions/cron.d.ts +0 -12
  149. package/build/services/pipe/functions/cron.js +0 -1
  150. package/build/services/pipe/functions/date.d.ts +0 -58
  151. package/build/services/pipe/functions/date.js +0 -1
  152. package/build/services/pipe/functions/index.d.ts +0 -29
  153. package/build/services/pipe/functions/index.js +0 -1
  154. package/build/services/pipe/functions/json.d.ts +0 -5
  155. package/build/services/pipe/functions/json.js +0 -1
  156. package/build/services/pipe/functions/logical.d.ts +0 -5
  157. package/build/services/pipe/functions/logical.js +0 -1
  158. package/build/services/pipe/functions/math.d.ts +0 -42
  159. package/build/services/pipe/functions/math.js +0 -1
  160. package/build/services/pipe/functions/number.d.ts +0 -21
  161. package/build/services/pipe/functions/number.js +0 -1
  162. package/build/services/pipe/functions/object.d.ts +0 -25
  163. package/build/services/pipe/functions/object.js +0 -1
  164. package/build/services/pipe/functions/string.d.ts +0 -23
  165. package/build/services/pipe/functions/string.js +0 -1
  166. package/build/services/pipe/functions/symbol.d.ts +0 -12
  167. package/build/services/pipe/functions/symbol.js +0 -1
  168. package/build/services/pipe/functions/unary.d.ts +0 -7
  169. package/build/services/pipe/functions/unary.js +0 -1
  170. package/build/services/pipe/index.d.ts +0 -48
  171. package/build/services/pipe/index.js +0 -1
  172. package/build/services/quorum/index.d.ts +0 -90
  173. package/build/services/quorum/index.js +0 -1
  174. package/build/services/reporter/index.d.ts +0 -50
  175. package/build/services/reporter/index.js +0 -1
  176. package/build/services/router/index.d.ts +0 -60
  177. package/build/services/router/index.js +0 -1
  178. package/build/services/search/factory.d.ts +0 -7
  179. package/build/services/search/factory.js +0 -24
  180. package/build/services/search/index.d.ts +0 -22
  181. package/build/services/search/index.js +0 -10
  182. package/build/services/search/providers/postgres/postgres.d.ts +0 -24
  183. package/build/services/search/providers/postgres/postgres.js +0 -1
  184. package/build/services/search/providers/redis/ioredis.d.ts +0 -18
  185. package/build/services/search/providers/redis/ioredis.js +0 -1
  186. package/build/services/search/providers/redis/redis.d.ts +0 -18
  187. package/build/services/search/providers/redis/redis.js +0 -1
  188. package/build/services/serializer/index.d.ts +0 -42
  189. package/build/services/serializer/index.js +0 -1
  190. package/build/services/store/cache.d.ts +0 -67
  191. package/build/services/store/cache.js +0 -128
  192. package/build/services/store/factory.d.ts +0 -8
  193. package/build/services/store/factory.js +0 -24
  194. package/build/services/store/index.d.ts +0 -89
  195. package/build/services/store/index.js +0 -9
  196. package/build/services/store/providers/postgres/kvsql.d.ts +0 -168
  197. package/build/services/store/providers/postgres/kvsql.js +0 -1
  198. package/build/services/store/providers/postgres/kvtables.d.ts +0 -20
  199. package/build/services/store/providers/postgres/kvtables.js +0 -1
  200. package/build/services/store/providers/postgres/kvtransaction.d.ts +0 -36
  201. package/build/services/store/providers/postgres/kvtransaction.js +0 -1
  202. package/build/services/store/providers/postgres/kvtypes/hash.d.ts +0 -60
  203. package/build/services/store/providers/postgres/kvtypes/hash.js +0 -1
  204. package/build/services/store/providers/postgres/kvtypes/list.d.ts +0 -33
  205. package/build/services/store/providers/postgres/kvtypes/list.js +0 -1
  206. package/build/services/store/providers/postgres/kvtypes/string.d.ts +0 -20
  207. package/build/services/store/providers/postgres/kvtypes/string.js +0 -1
  208. package/build/services/store/providers/postgres/kvtypes/zset.d.ts +0 -41
  209. package/build/services/store/providers/postgres/kvtypes/zset.js +0 -1
  210. package/build/services/store/providers/postgres/postgres.d.ts +0 -145
  211. package/build/services/store/providers/postgres/postgres.js +0 -1
  212. package/build/services/store/providers/redis/_base.d.ts +0 -137
  213. package/build/services/store/providers/redis/_base.js +0 -1
  214. package/build/services/store/providers/redis/ioredis.d.ts +0 -20
  215. package/build/services/store/providers/redis/ioredis.js +0 -1
  216. package/build/services/store/providers/redis/redis.d.ts +0 -18
  217. package/build/services/store/providers/redis/redis.js +0 -1
  218. package/build/services/store/providers/store-initializable.d.ts +0 -5
  219. package/build/services/store/providers/store-initializable.js +0 -1
  220. package/build/services/stream/factory.d.ts +0 -8
  221. package/build/services/stream/factory.js +0 -37
  222. package/build/services/stream/index.d.ts +0 -64
  223. package/build/services/stream/index.js +0 -11
  224. package/build/services/stream/providers/nats/nats.d.ts +0 -59
  225. package/build/services/stream/providers/nats/nats.js +0 -1
  226. package/build/services/stream/providers/postgres/kvtables.d.ts +0 -2
  227. package/build/services/stream/providers/postgres/kvtables.js +0 -1
  228. package/build/services/stream/providers/postgres/postgres.d.ts +0 -88
  229. package/build/services/stream/providers/postgres/postgres.js +0 -1
  230. package/build/services/stream/providers/redis/ioredis.d.ts +0 -60
  231. package/build/services/stream/providers/redis/ioredis.js +0 -1
  232. package/build/services/stream/providers/redis/redis.d.ts +0 -60
  233. package/build/services/stream/providers/redis/redis.js +0 -1
  234. package/build/services/stream/providers/stream-initializable.d.ts +0 -4
  235. package/build/services/stream/providers/stream-initializable.js +0 -1
  236. package/build/services/sub/factory.d.ts +0 -7
  237. package/build/services/sub/factory.js +0 -29
  238. package/build/services/sub/index.d.ts +0 -22
  239. package/build/services/sub/index.js +0 -10
  240. package/build/services/sub/providers/nats/nats.d.ts +0 -19
  241. package/build/services/sub/providers/nats/nats.js +0 -1
  242. package/build/services/sub/providers/postgres/postgres.d.ts +0 -19
  243. package/build/services/sub/providers/postgres/postgres.js +0 -1
  244. package/build/services/sub/providers/redis/ioredis.d.ts +0 -17
  245. package/build/services/sub/providers/redis/ioredis.js +0 -1
  246. package/build/services/sub/providers/redis/redis.d.ts +0 -17
  247. package/build/services/sub/providers/redis/redis.js +0 -1
  248. package/build/services/task/index.d.ts +0 -36
  249. package/build/services/task/index.js +0 -1
  250. package/build/services/telemetry/index.d.ts +0 -52
  251. package/build/services/telemetry/index.js +0 -1
  252. package/build/services/worker/index.d.ts +0 -77
  253. package/build/services/worker/index.js +0 -1
  254. /package/build/types/{meshflow.js → memflow.js} +0 -0
package/README.md CHANGED
@@ -1,867 +1,172 @@
1
- # HotMesh
2
- ![beta release](https://img.shields.io/badge/release-beta-blue.svg)
1
+ # HotMesh MemFlow
3
2
 
4
- **HotMesh** offers the power of [Temporal.io](https://temporal.io) in a fully serverless architecture.
3
+ **Permanent-Memory Workflows & AI Agents**
5
4
 
5
+ ![beta release](https://img.shields.io/badge/release-beta-blue.svg) ![made with typescript](https://img.shields.io/badge/built%20with-typescript-lightblue.svg)
6
6
 
7
- <br/>
7
+ MemFlow is a drop-in Temporal-style engine that runs natively on Postgres — but with a twist:
8
+ every workflow owns a *permanent*, JSON-backed context that lives beyond the main workflow.
9
+ Any number of *hooks* (lightweight, thread-safe workers) can attach to that record at any
10
+ time, read it, and safely write back incremental knowledge.
11
+ Think **durable execution** + **shared, evolving memory** → perfect for human-in-the-loop
12
+ processes and AI agents that learn over time.
8
13
 
9
- ## Features
14
+ ---
10
15
 
11
- - **Serverless Orchestration**: Orchestrate without adding infrastructure
12
- - **No Vendor Lock-in**: Use your preferred database: *Postgres*, *Redis*, ...
13
- - **Linear Scalability**: Scale your database to scale your application
14
- - **Process Analytics**: Gain process insights with optional analytics
16
+ ## Table of Contents
15
17
 
18
+ 1. 🚀 Quick Start
19
+ 2. 🧠 How Permanent Memory Works
20
+ 3. 🔌 Hooks & Context API
21
+ 4. 🤖 Building Durable AI Agents
22
+ 5. 🔬 Advanced Patterns & Recipes
23
+ 6. 📚 Documentation & Links
16
24
 
17
- <br/>
25
+ ---
18
26
 
19
- ## Install
27
+ ## 🚀 Quick Start
20
28
 
21
- ```sh
29
+ ### Install
30
+ ```bash
22
31
  npm install @hotmeshio/hotmesh
23
32
  ```
24
33
 
25
- <br/>
26
-
27
- ## Learn
28
- [🏠 Home](https://hotmesh.io/) | [📄 SDK Docs](https://hotmeshio.github.io/sdk-typescript/) | [💼 General Examples](https://github.com/hotmeshio/samples-typescript) | [💼 Temporal Examples](https://github.com/hotmeshio/temporal-patterns-typescript)
29
-
30
- <br/>
31
-
32
- ## MeshCall
33
- [MeshCall](https://hotmeshio.github.io/sdk-typescript/classes/services_meshcall.MeshCall.html) connects any function to the mesh
34
-
35
- <details style="padding: .5em">
36
- <summary style="font-size:1.25em;">Run an idempotent cron job <small>[more]</small></summary>
37
-
38
- ### Run a Cron
39
- This example demonstrates an *idempotent* cron that runs daily at midnight. The `id` makes each cron job unique and ensures that only one instance runs, despite repeated invocations. *The `cron` method returns `false` if a workflow is already running with the same `id`.*
40
-
41
- Optionally set a `delay` and/or set `maxCycles` to limit the number of cycles. The `interval` can be any human-readable time format (e.g., `1 day`, `2 hours`, `30 minutes`, etc) or a standard cron expression.
42
-
43
- 1. Define the cron function.
44
- ```typescript
45
- //cron.ts
46
- import { MeshCall } from '@hotmeshio/hotmesh';
47
- import { Client as Postgres } from 'pg';
48
-
49
- export const runMyCron = async (id: string, interval = '0 0 * * *'): Promise<boolean> => {
50
- return await MeshCall.cron({
51
- topic: 'my.cron.function',
52
- connection: {
53
- class: Postgres,
54
- options: {
55
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
56
- }
57
- },
58
- callback: async () => {
59
- //your code here...
60
- },
61
- options: { id, interval, maxCycles: 24 }
62
- });
63
- };
64
- ```
65
-
66
- 2. Call `runMyCron` at server startup (or call as needed to run multiple crons).
67
- ```typescript
68
- //server.ts
69
- import { runMyCron } from './cron';
70
-
71
- runMyCron('myNightlyCron123');
72
- ```
73
- </details>
74
-
75
- <details style="padding: .5em">
76
- <summary style="font-size:1.25em;">Interrupt a cron job <small>[more]</small></summary>
77
-
78
- ### Interrupt a Cron
79
- This example demonstrates how to cancel a running cron job.
80
-
81
- 1. Use the same `id` and `topic` that were used to create the cron to cancel it.
82
- ```typescript
83
- import { MeshCall } from '@hotmeshio/hotmesh';
84
- import { Client as Postgres } from 'pg';
85
-
86
- MeshCall.interrupt({
87
- topic: 'my.cron.function',
88
- connection: {
89
- class: Postgres,
90
- options: {
91
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
92
- }
93
- },
94
- options: { id: 'myNightlyCron123' }
95
- });
96
- ```
97
- </details>
98
-
99
- <details style="padding: .5em">
100
- <summary style="font-size:1.25em;">Call any function in any service <small>[more]</small></summary>
101
-
102
- ### Call a Function
103
- Make interservice calls that behave like HTTP but without the setup and performance overhead. This example demonstrates how to connect and call a function.
104
-
105
- 1. Call `MeshCall.connect` and provide a `topic` to uniquely identify the function.
106
-
107
- ```typescript
108
- //myFunctionWrapper.ts
109
- import { MeshCall, Types } from '@hotmeshio/hotmesh';
110
- import { Client as Postgres } from 'pg';
111
-
112
- export const connectMyFunction = async () => {
113
- return await MeshCall.connect({
114
- topic: 'my.demo.function',
115
- connection: {
116
- class: Postgres,
117
- options: {
118
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
119
- }
120
- },
121
- callback: async (input: string) => {
122
- //your code goes here; response must be JSON serializable
123
- return { hello: input }
124
- },
125
- });
126
- };
127
- ```
128
-
129
- 2. Call `connectMyFunction` at server startup to connect your function to the mesh.
130
-
131
- ```typescript
132
- //server.ts
133
- import { connectMyFunction } from './myFunctionWrapper';
134
- connectMyFunction();
135
- ```
136
-
137
- 3. Call your function from anywhere on the network (or even from the same service). Send any payload as long as it's JSON serializable.
138
-
139
- ```typescript
140
- import { MeshCall } from '@hotmeshio/hotmesh';
141
- import { Client as Postgres } from 'pg';
142
-
143
- const result = await MeshCall.exec({
144
- topic: 'my.demo.function',
145
- args: ['something'],
146
- connection: {
147
- class: Postgres,
148
- options: {
149
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
150
- }
151
- },
152
- }); //returns `{ hello: 'something'}`
153
- ```
154
- </details>
155
-
156
- <details style="padding: .5em">
157
- <summary style="font-size:1.25em;">Call and <b>cache</b> a function <small>[more]</small></summary>
158
-
159
- ### Cache a Function
160
- This solution builds upon the previous example, caching the response. The linked function will only be re/called when the cached result expires. Everything remains the same, except the caller which specifies an `id` and `ttl`.
161
-
162
- 1. Make the call from another service (or even the same service). Include an `id` and `ttl` to cache the result for the specified duration.
163
-
164
- ```typescript
165
- import { MeshCall } from '@hotmeshio/hotmesh';
166
- import { Client as Postgres } from 'pg';
167
-
168
- const result = await MeshCall.exec({
169
- topic: 'my.demo.function',
170
- args: ['anything'],
171
- connection: {
172
- class: Postgres,
173
- options: {
174
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
175
- }
176
- },
177
- options: { id: 'myid123', ttl: '15 minutes' },
178
- }); //returns `{ hello: 'anything'}`
179
- ```
180
-
181
- 2. Flush the cache at any time, using the same `topic` and cache `id`.
182
-
183
- ```typescript
184
- import { MeshCall } from '@hotmeshio/hotmesh';
185
- import { Client as Postgres } from 'pg';
186
-
187
- await MeshCall.flush({
188
- topic: 'my.demo.function',
189
- connection: {
190
- class: Postgres,
191
- options: {
192
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
193
- }
194
- },
195
- options: { id: 'myid123' },
196
- });
197
- ```
198
- </details>
199
-
200
-
201
- <br/>
202
-
203
- ## MeshFlow
204
- [MeshFlow](https://hotmeshio.github.io/sdk-typescript/classes/services_meshflow.MeshFlow.html) is a serverless alternative to *Temporal.io*
205
-
206
- <details style="padding: .5em">
207
- <summary style="font-size:1.25em;">Orchestrate unpredictable activities <small>[more]</small></summary>
208
-
209
- ### Proxy Activities
210
- When an endpoint is unpredictable, use `proxyActivities`. HotMesh will retry as necessary until the call succeeds. This example demonstrates a workflow that greets a user in both English and Spanish. Even though both activities throw random errors, the workflow always returns a successful result.
211
-
212
- 1. Start by defining **activities**. Note how each throws an error 50% of the time.
213
-
214
- ```typescript
215
- //activities.ts
216
- export async function greet(name: string): Promise<string> {
217
- if (Math.random() > 0.5) throw new Error('Random error');
218
- return `Hello, ${name}!`;
219
- }
220
-
221
- export async function saludar(nombre: string): Promise<string> {
222
- if (Math.random() > 0.5) throw new Error('Random error');
223
- return `¡Hola, ${nombre}!`;
224
- }
225
- ```
226
-
227
- 2. Define the **workflow** logic. Include conditional branching, loops, etc to control activity execution. It's vanilla JavaScript written in your own coding style. The only requirement is to use `proxyActivities`, ensuring your activities are executed with HotMesh's durability wrapper.
228
-
229
- ```typescript
230
- //workflows.ts
231
- import { workflow } from '@hotmeshio/hotmesh';
232
- import * as activities from './activities';
233
-
234
- const { greet, saludar } = workflow
235
- .proxyActivities<typeof activities>({
236
- activities
237
- });
238
-
239
- export async function example(name: string): Promise<[string, string]> {
240
- return Promise.all([
241
- greet(name),
242
- saludar(name)
243
- ]);
244
- }
245
- ```
246
-
247
- 3. Instance a HotMesh **client** to invoke the workflow.
248
-
249
- ```typescript
250
- //client.ts
251
- import { Client, HotMesh } from '@hotmeshio/hotmesh';
252
- import { Client as Postgres } from 'pg';
253
-
254
- async function run(): Promise<string> {
255
- const client = new Client({
256
- connection: {
257
- class: Postgres,
258
- options: {
259
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
260
- }
261
- }
262
- });
263
-
264
- const handle = await client.workflow.start<[string,string]>({
265
- args: ['HotMesh'],
266
- taskQueue: 'default',
267
- workflowName: 'example',
268
- workflowId: HotMesh.guid()
269
- });
270
-
271
- return await handle.result();
272
- //returns ['Hello HotMesh', '¡Hola, HotMesh!']
273
- }
274
- ```
275
-
276
- 4. Finally, create a **worker** and link the workflow function. Workers listen for tasks on their assigned task queue and invoke the workflow function each time they receive an event.
277
-
278
- ```typescript
279
- //worker.ts
280
- import { worker } from '@hotmeshio/hotmesh';
281
- import { Client as Postgres } from 'pg';
282
- import * as workflows from './workflows';
283
-
284
- async function run() {
285
- const worker = await Worker.create({
286
- connection: {
287
- class: Postgres,
288
- options: {
289
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
290
- }
291
- },
292
- taskQueue: 'default',
293
- workflow: workflows.example,
294
- });
295
-
296
- await worker.run();
297
- }
298
- ```
299
- </details>
300
-
301
- <details style="padding: .5em">
302
- <summary style="font-size:1.25em;">Pause and wait for a signal <small>[more]</small></summary>
303
-
304
- ### Wait for Signal
305
- Pause a function and only awaken when a matching signal is received from the outide.
306
-
307
- 1. Define the **workflow** logic. This one waits for the `my-sig-nal` signal, returning the signal payload (`{ hello: 'world' }`) when it eventually arrives. Interleave additional logic to meet your use case.
308
-
309
- ```typescript
310
- //waitForWorkflow.ts
311
- import { workflow } from '@hotmeshio/hotmesh';
312
-
313
- export async function waitForExample(): Promise<{hello: string}> {
314
- return await workflow.waitFor<{hello: string}>('my-sig-nal');
315
- //continue processing, use the payload, etc...
316
- }
317
- ```
318
-
319
- 2. Instance a HotMesh **client** and start a workflow. Use a custom workflow ID (`myWorkflow123`).
320
-
321
- ```typescript
322
- //client.ts
323
- import { Client, HotMesh } from '@hotmeshio/hotmesh';
324
- import { Client as Postgres } from 'pg';
325
-
326
- async function run(): Promise<string> {
327
- const client = new Client({
328
- connection: {
329
- class: Postgres,
330
- options: {
331
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
332
- }
333
- }
334
- });
335
-
336
- //start a workflow; it will immediately pause
337
- await client.workflow.start({
338
- args: ['HotMesh'],
339
- taskQueue: 'default',
340
- workflowName: 'waitForExample',
341
- workflowId: 'myWorkflow123',
342
- await: false,
343
- });
344
- }
345
- ```
346
-
347
- 3. Create a **worker** and link the `waitForExample` workflow function.
348
-
349
- ```typescript
350
- //worker.ts
351
- import { Worker } from '@hotmeshio/hotmesh';
352
- import { Client as Postgres } from 'pg';
353
- import * as workflows from './waitForWorkflow';
354
-
355
- async function run() {
356
- const worker = await Worker.create({
357
- connection: {
358
- class: Postgres,
359
- options: {
360
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
361
- }
362
- },
363
- taskQueue: 'default',
364
- workflow: workflows.waitForExample,
365
- });
366
-
367
- await worker.run();
368
- }
369
- ```
370
-
371
- 4. Send a signal to awaken the paused function; await the function result.
372
-
373
- ```typescript
374
- import { Client } from '@hotmeshio/hotmesh';
375
- import { Client as Postgres } from 'pg';
376
-
377
- const client = new Client({
378
- connection: {
379
- class: Postgres,
380
- options: {
381
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
382
- }
383
- }
384
- });
385
-
386
- //awaken the function by sending a signal
387
- await client.signal('my-sig-nal', { hello: 'world' });
388
-
389
- //get the workflow handle and await the result
390
- const handle = await client.getHandle({
391
- taskQueue: 'default',
392
- workflowId: 'myWorkflow123'
393
- });
394
-
395
- const result = await handle.result();
396
- //returns { hello: 'world' }
397
- ```
398
- </details>
399
-
400
- <details style="padding: .5em">
401
- <summary style="font-size:1.25em;">Wait for multiple signals (collation) <small>[more]</small></summary>
402
-
403
- ### Collate Multiple Signals
404
- Use a standard `Promise` to collate and cache multiple signals. HotMesh will only awaken once **all** signals have arrived. HotMesh will track up to 25 concurrent signals.
405
-
406
- 1. Update the **workflow** logic to await two signals using a promise: `my-sig-nal-1` and `my-sig-nal-2`. Add additional logic to meet your use case.
407
-
408
- ```typescript
409
- //waitForWorkflows.ts
410
- import { workflow } from '@hotmeshio/hotmesh';
411
-
412
- export async function waitForExample(): Promise<[boolean, number]> {
413
- const [s1, s2] = await Promise.all([
414
- workflow.waitFor<boolean>('my-sig-nal-1'),
415
- workflow.waitFor<number>('my-sig-nal-2')
416
- ]);
417
- //do something with the signal payloads (s1, s2)
418
- return [s1, s2];
419
- }
420
- ```
421
-
422
- 2. Send **two** signals to awaken the paused function.
423
-
424
- ```typescript
425
- import { Client } from '@hotmeshio/hotmesh';
426
- import { Client as Postgres } from 'pg';
427
-
428
- const client = new Client({
429
- connection: {
430
- class: Postgres,
431
- options: {
432
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
433
- }
434
- }
435
- });
436
-
437
- //send 2 signals to awaken the function; order is unimportant
438
- await client.signal('my-sig-nal-2', 12345);
439
- await client.signal('my-sig-nal-1', true);
440
-
441
- //get the workflow handle and await the collated result
442
- const handle = await client.getHandle({
443
- taskQueue: 'default',
444
- workflowId: 'myWorkflow123'
445
- });
446
-
447
- const result = await handle.result();
448
- //returns [true, 12345]
449
- ```
450
- </details>
451
-
452
- <details style="padding: .5em">
453
- <summary style="font-size:1.25em;">Create a recurring, cyclical workflow <small>[more]</small></summary>
454
-
455
- ### Cyclical Workflow
456
- This example calls an activity and then sleeps for a week. It runs indefinitely until it's manually stopped. It takes advantage of durable execution and can safely sleep for months or years.
457
-
458
- >Container restarts have no impact on actively executing workflows as all state is retained in the backend.
459
-
460
- 1. Define the **workflow** logic. This one calls a legacy `statusDiagnostic` function once a week.
461
-
462
- ```typescript
463
- //recurringWorkflow.ts
464
- import { workflow } from '@hotmeshio/hotmesh';
465
- import * as activities from './activities';
466
-
467
- const { statusDiagnostic } = workflow
468
- .proxyActivities<typeof activities>({
469
- activities
470
- });
471
-
472
- export async function recurringExample(someValue: number): Promise<void> {
473
- do {
474
- await statusDiagnostic(someValue);
475
- } while (await workflow.sleepFor('1 week'));
476
- }
477
- ```
478
-
479
- 2. Instance a HotMesh **client** and start a workflow. Assign a custom workflow ID (e.g., `myRecurring123`) if the workflow should be idempotent.
480
-
481
- ```typescript
482
- //client.ts
483
- import { Client, HotMesh } from '@hotmeshio/hotmesh';
484
- import { Client as Postgres } from 'pg';
485
-
486
- async function run(): Promise<string> {
487
- const client = new Client({
488
- connection: {
489
- class: Postgres,
490
- options: {
491
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
492
- }
493
- }
494
- });
495
-
496
- //start a workflow; it will immediately pause
497
- await client.workflow.start({
498
- args: [55],
499
- taskQueue: 'default',
500
- workflowName: 'recurringExample',
501
- workflowId: 'myRecurring123',
502
- await: false,
503
- });
504
- }
505
- ```
506
-
507
- 3. Create a **worker** and link the `recurringExample` workflow function.
508
-
509
- ```typescript
510
- //worker.ts
511
- import { Worker } from '@hotmeshio/hotmesh';
512
- import { Client as Postgres } from 'pg';
513
- import * as workflows from './recurringWorkflow';
514
-
515
- async function run() {
516
- const worker = await Worker.create({
517
- connection: {
518
- class: Postgres,
519
- options: {
520
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
521
- }
522
- },
523
- taskQueue: 'default',
524
- workflow: workflows.recurringExample,
525
- });
526
-
527
- await worker.run();
528
- }
529
- ```
530
-
531
- 4. Cancel the recurring workflow (`myRecurring123`) by calling `interrupt`.
532
-
533
- ```typescript
534
- import { Client } from '@hotmeshio/hotmesh';
535
- import { Client as Postgres } from 'pg';
34
+ ### Start a workflow
35
+ ```typescript
36
+ // index.ts
37
+ import { MemFlow } from '@hotmeshio/hotmesh';
38
+ import { Client as Postgres } from 'pg';
536
39
 
537
- const client = new Client({
40
+ async function main() {
41
+ const mf = await MemFlow.init({
42
+ appId: 'my-app',
43
+ engine: {
538
44
  connection: {
539
45
  class: Postgres,
540
- options: {
541
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
542
- }
46
+ options: { connectionString: process.env.DATABASE_URL }
543
47
  }
544
- });
545
-
546
- //get the workflow handle and interrupt it
547
- const handle = await client.getHandle({
548
- taskQueue: 'default',
549
- workflowId: 'myRecurring123'
550
- });
551
-
552
- const result = await handle.interrupt();
553
- ```
554
- </details>
555
-
556
- <br/>
557
-
558
- ## MeshData
559
- [MeshData](https://hotmeshio.github.io/sdk-typescript/classes/services_meshdata.MeshData.html) adds analytics to running workflows
560
-
561
- <details style="padding: .5em">
562
- <summary style="font-size:1.25em;">Create a search index <small>[more]</small></summary>
563
-
564
- ### Workflow Data Indexes
565
-
566
- This example demonstrates how to define a schema and deploy an index for a 'user' entity type.
567
-
568
- 1. Define the **schema** for the `user` entity. This one includes the 3 formats supported by the FT.SEARCH module: `TEXT`, `TAG` and `NUMERIC`.
569
-
570
- ```typescript
571
- //schema.ts
572
- export const schema: Types.WorkflowSearchOptions = {
573
- schema: {
574
- id: { type: 'TAG', sortable: false },
575
- first: { type: 'TEXT', sortable: false, nostem: true },
576
- active: { type: 'TAG', sortable: false },
577
- created: { type: 'NUMERIC', sortable: true },
578
- },
579
- index: 'user',
580
- prefix: ['user'],
581
- };
582
- ```
583
-
584
- 2. Create the index upon server startup. This one initializes the 'user' index, using the schema defined in the previous step. It's OK to call `createSearchIndex` multiple times; it will only create the index if it doesn't already exist.
585
-
586
- ```typescript
587
- //server.ts
588
- import { MeshData } from '@hotmeshio/hotmesh';
589
- import { Client as Postgres } from 'pg';
590
- import { schema } from './schema';
591
-
592
- const meshData = new MeshData({
593
- class: Postgres,
594
- options: {
595
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
596
- }
597
- },
598
- schema,
599
- );
600
- await meshData.createSearchIndex('user', { namespace: 'meshdata' });
601
- ```
602
- </details>
603
-
604
- <details style="padding: .5em">
605
- <summary style="font-size:1.25em;">Create an indexed, searchable record <small>[more]</small></summary>
606
-
607
- ### Workflow Record Data
608
- This example demonstrates how to create a 'user' workflow backed by the searchable schema from the prior example.
609
-
610
- 1. Call MeshData `connect` to initialize a 'user' entity *worker*. It references a target worker function which will run the workflow. Data fields that are documented in the schema (like `active`) will be automatically indexed when set on the workflow record.
611
-
612
- ```typescript
613
- //connect.ts
614
- import { MeshData } from '@hotmeshio/hotmesh';
615
- import { Client as Postgres } from 'pg';
616
- import { schema } from './schema';
617
-
618
- export const connectUserWorker = async (): Promise<void> => {
619
- const meshData = new MeshData({
620
- class: Postgres,
621
- options: {
622
- connectionString: 'postgresql:// usr:pwd@localhost:5432/db'
623
- }
624
- },
625
- schema,
626
- );
627
-
628
- await meshData.connect({
629
- entity: 'user',
630
- target: async function(name: string): Promise<string> {
631
- //add custom, searchable data (`active`) and return
632
- const search = await MeshData.workflow.search();
633
- await search.set('active', 'yes');
634
- return `Welcome, ${name}.`;
635
- },
636
- options: { namespace: 'meshdata' },
637
- });
638
48
  }
639
- ```
49
+ });
640
50
 
641
- 2. Wire up the worker at server startup, so it's ready to process incoming requests.
51
+ // Kick off a workflow
52
+ const handle = await mf.workflow.start({
53
+ workflowName: 'example',
54
+ args: ['Jane'],
55
+ taskQueue: 'contextual'
56
+ });
642
57
 
643
- ```typescript
644
- //server.ts
645
- import { connectUserWorker } from './connect';
646
- await connectUserWorker();
647
- ```
58
+ console.log('Result:', await handle.result());
59
+ }
648
60
 
649
- 3. Call MeshData `exec` to create a 'user' workflow. Searchable data can be set throughout the workflow's lifecycle. This one initializes the workflow with 3 data fields: `id`, `name` and `timestamp`. *An additional data field (`active`) is set within the workflow function in order to demonstrate both mechanisms for reading/writing data to a workflow.*
650
-
651
- ```typescript
652
- //exec.ts
653
- import { MeshData } from '@hotmeshio/hotmesh';
654
- import { Client as Postgres } from 'pg';
655
-
656
- const meshData = new MeshData({
657
- class: Postgres,
658
- options: {
659
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
660
- }
661
- },
662
- schema,
663
- );
664
-
665
- export const newUser = async (id: string, name: string): Promise<string> => {
666
- const response = await meshData.exec({
667
- entity: 'user',
668
- args: [name],
669
- options: {
670
- ttl: 'infinity',
671
- id,
672
- search: {
673
- data: { id, name, timestamp: Date.now() }
674
- },
675
- namespace: 'meshdata',
676
- },
677
- });
678
- return response;
679
- };
680
- ```
681
-
682
- 4. Call the `newUser` function to create a searchable 'user' record.
683
-
684
- ```typescript
685
- import { newUser } from './exec';
686
- const response = await newUser('jim123', 'James');
687
- ```
688
- </details>
689
-
690
- <details style="padding: .5em">
691
- <summary style="font-size:1.25em;">Fetch record data <small>[more]</small></summary>
692
-
693
- ### Read Record Data
694
- This example demonstrates how to read data fields directly from a workflow.
695
-
696
- 1. Read data fields directly from the *jimbo123* 'user' record.
697
-
698
- ```typescript
699
- //read.ts
700
- import { MeshData } from '@hotmeshio/hotmesh';
701
- import { Client as Postgres } from 'pg';
702
- import { schema } from './schema';
703
-
704
- const meshData = new MeshData({
705
- class: Postgres,
706
- options: {
707
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
708
- }
709
- },
710
- schema,
711
- );
712
-
713
- const data = await meshData.get(
714
- 'user',
715
- 'jimbo123',
716
- {
717
- fields: ['id', 'name', 'timestamp', 'active'],
718
- namespace: 'meshdata'
719
- },
720
- );
721
- ```
722
- </details>
723
-
724
- <details style="padding: .5em">
725
- <summary style="font-size:1.25em;">Search record data <small>[more]</small></summary>
726
-
727
- ### Query Record Data
728
- This example demonstrates how to search for those workflows where a given condition exists in the data. This one searches for active users. *NOTE: The native Redis FT.SEARCH syntax and SQL are currently supported. The JSON abstraction shown here is a convenience method for straight-forward, one-dimensional queries.*
729
-
730
- 1. Search for active users (where the value of the `active` field is `yes`).
731
-
732
- ```typescript
733
- //read.ts
734
- import { MeshData } from '@hotmeshio/hotmesh';
735
- import { Client as Postgres } from 'pg';
736
- import { schema } from './schema';
737
-
738
- const meshData = new MeshData({
739
- class: Postgres,
740
- options: {
741
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
742
- }
743
- },
744
- schema,
745
- );
746
-
747
- const results = await meshData.findWhere('user', {
748
- query: [{ field: 'active', is: '=', value: 'yes' }],
749
- limit: { start: 0, size: 100 },
750
- return: ['id', 'name', 'timestamp', 'active']
751
- });
752
- ```
753
- </details>
754
-
755
- <br/>
756
-
757
- ## Connect
758
- HotMesh is pluggable and fully supports **Postgres** and **Redis/ValKey** backends.
759
-
760
- **NATS** can be added for *pub-sub* support (when extended pattern matching is desired). And *streams* support is currently in alpha (**NATS** + **JetStream** + **Postgres**).
761
-
762
- <details style="padding: .5em">
763
- <summary style="font-size:1.25em;">Postgres <small>[more]</small></summary>
61
+ main().catch(console.error);
62
+ ```
764
63
 
765
- ### Connect Postgres Client
766
- ```typescript
767
- import { Client as PostgresClient } from 'pg';
768
64
 
769
- //provide these credentials to HotMesh
770
- const connection = {
771
- class: PostgresClient,
772
- options: {
773
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
774
- }
775
- };
65
+ ## 🧠 How Permanent Memory Works
776
66
 
777
- ```
778
- ### Connect Postgres Pool
779
- Pool connections are recommended for high-throughput applications. The pool will manage connections and automatically handle connection pooling.
780
- ```typescript
781
- import { Pool as PostgresPool } from 'pg';
67
+ * **Context = JSONB row** in `<yourappname>.jobs` table
68
+ * **Atomic operations** (`set`, `merge`, `append`, `increment`, `toggle`, `delete`, …)
69
+ * **Transactional** every update participates in the workflow/DB transaction
70
+ * **Time-travel-safe** – full replay compatibility; side-effect detector guarantees determinism
71
+ * **Hook-friendly** any worker with the record ID can attach and mutate its slice of the JSON
782
72
 
783
- const PostgresPoolClient = new PostgresPool({
784
- connectionString: 'postgresql://usr:pwd@localhost:5432/db'
785
- });
73
+ * Context data is stored as JSONB; add partial indexes for improved query analysis.
786
74
 
787
- //provide these credentials to HotMesh
788
- const connection = {
789
- class: PostgresPoolClient,
790
- options: {},
791
- };
75
+ **Example: Adding a Partial Index for Specific Entity Types**
76
+ ```sql
77
+ -- Create a partial index for 'user' entities with specific context values
78
+ CREATE INDEX idx_user_premium ON your_app.jobs (id)
79
+ WHERE entity = 'user' AND (context->>'isPremium')::boolean = true;
792
80
  ```
81
+ This index will only be used for queries that match both conditions, making lookups for premium users much faster.
793
82
 
794
- </details>
83
+ ---
795
84
 
796
- <details style="padding: .5em">
797
- <summary style="font-size:1.25em;">Redis <small>[more]</small></summary>
85
+ ## 🔌 Hooks & Context API – Full Example
798
86
 
799
- ### Redis/IORedis
800
87
  ```typescript
801
- import * as Redis from 'redis';
802
- //OR `import Redis from 'ioredis';`
803
-
804
- const connection = {
805
- class: Redis,
806
- options: {
807
- url: 'redis://:your_password@localhost:6379',
808
- }
809
- };
88
+ import { MemFlow } from '@hotmeshio/hotmesh';
89
+
90
+ /* ------------ Main workflow ------------ */
91
+ export async function example(name: string): Promise<any> {
92
+ //the context method provides transactional, replayable access to shared job state
93
+ const ctx = await MemFlow.workflow.context();
94
+
95
+ //create the initial context (even arrays are supported)
96
+ await ctx.set({
97
+ user: { name },
98
+ hooks: {},
99
+ metrics: { count: 0 }
100
+ });
101
+
102
+ // Call two hooks in parallel to updaet the same shared context
103
+ const [r1, r2] = await Promise.all([
104
+ MemFlow.workflow.execHook({
105
+ taskQueue: 'contextual',
106
+ workflowName: 'hook1',
107
+ args: [name, 'hook1'],
108
+ signalId: 'hook1-complete',
109
+ }),
110
+ MemFlow.workflow.execHook({
111
+ taskQueue: 'contextual',
112
+ workflowName: 'hook2',
113
+ args: [name, 'hook2'],
114
+ signalId: 'hook2-complete',
115
+ })
116
+ ]);
117
+
118
+ // merge here (or have the hooks merge in...everyone can access context)
119
+ await ctx.merge({ hooks: { r1, r2 } });
120
+ await ctx.increment('metrics.count', 2);
121
+
122
+ return "The main has completed; the db record persists and can be hydrated; hook in from the outside!";
123
+ }
124
+
125
+ /* ------------ Hook 1 (hooks have access to methods like sleepFor) ------------ */
126
+ export async function hook1(name: string, kind: string): Promise<any> {
127
+ await MemFlow.workflow.sleepFor('2 seconds');
128
+ const res = { kind, processed: true, at: Date.now() };
129
+ await MemFlow.workflow.signal('hook1-complete', res);
130
+ }
131
+
132
+ /* ------------ Hook 2 (hooks can access shared job context) ------------ */
133
+ export async function hook2(name: string, kind: string): Promise<void> {
134
+ const ctx = await MemFlow.workflow.context();
135
+ await ctx.merge({ user: { lastSeen: new Date().toISOString() } });
136
+ await MemFlow.workflow.signal('hook2-complete', { ok: true });
137
+ }
810
138
  ```
811
- </details>
812
-
813
- <details style="padding: .5em">
814
- <summary style="font-size:1.25em;">NATS <small>[more]</small></summary>
815
-
816
- ### NATS PubSub
817
- Add NATS for improved PubSub support, including patterned subscriptions. Note the explicit channel subscription in the example below. *The NATS provider supports version 2.0 of the NATS client (the latest version). See ./package.json for details.*
818
139
 
819
- ```typescript
820
- import { Client as Postgres } from 'pg';
821
- import { connect as NATS } from 'nats';
140
+ **Highlights**
822
141
 
823
- const connection = {
824
- store: {
825
- class: Postgres,
826
- options: {
827
- connectionString: 'postgresql://usr:pwd@localhost:5432/db',
828
- }
829
- },
830
- stream: {
831
- class: Postgres,
832
- options: {
833
- connectionString: 'postgresql://usr:pwd@localhost:5432/db',
834
- }
835
- },
836
- sub: {
837
- class: NATS,
838
- options: { servers: ['nats:4222'] }
839
- },
840
- };
841
- ```
842
- </details>
142
+ * Hook functions are replay-safe.
143
+ * Hook functions can safely read and write to the the *same* JSON context.
144
+ * All context operations (`set`, `merge`, `append`, etc.) execute transactionally.
145
+ * Context data is stored as JSONB; add partial indexes for improved query analysis.
843
146
 
844
- <br/>
147
+ ---
845
148
 
846
- ## Metrics and Monitoring
847
- HotMesh's **OpenTelemetry** output provides insight into long-running, cross-service transactions. Add an OpenTelemetry sink to any service where HotMesh is deployed and HotMesh will emit the full **OpenTelemetry** execution tree organized as a single, unified DAG.
149
+ ## 🤖 Building Durable AI Agents
848
150
 
849
- The **HotMesh Dashboard** provides a detailed overview of all running workflows. It includes an LLM to simplify querying and analyzing workflow data. An example Web server with REST APIs and the Dashboard (a WebApp) is included in the [samples-typescript](https://github.com/hotmeshio/samples-typescript) Git repo.
151
+ Permanent memory unlocks a straightforward pattern for agentic systems:
850
152
 
851
- <br/>
153
+ 1. **Planner workflow** – sketches a task list, seeds context.
154
+ 2. **Tool hooks** – execute individual tasks, feeding intermediate results back into context.
155
+ 3. **Reflector hook** – periodically summarises context into long-term memory embeddings.
156
+ 4. **Supervisor workflow** – monitors metrics stored in context and decides when to finish.
852
157
 
853
- ## Examples
854
- Refer to the [hotmeshio/samples-typescript](https://github.com/hotmeshio/samples-typescript) Git repo for *tutorials* and instructions on deploying the *HotMesh Dashboard* for visualizing workflows and managing network health.
158
+ Because every step is durable *and* shares the same knowledge object, agents can pause,
159
+ restart, scale horizontally, and keep evolving their world-model indefinitely.
855
160
 
856
- Refer to the [hotmeshio/temporal-patterns-typescript](https://github.com/hotmeshio/temporal-patterns-typescript) Git repo for examples of common Temporal.io patterns implemented using HotMesh.
161
+ ---
857
162
 
858
- <br/>
163
+ ## 📚 Documentation & Links
859
164
 
860
- ## Advanced
861
- The theory that underlies the architecture is applicable to any number of data storage and streaming backends: [A Message-Oriented Approach to Decentralized Process Orchestration](https://zenodo.org/records/12168558).
165
+ * SDK API – [https://hotmeshio.github.io/sdk-typescript](https://hotmeshio.github.io/sdk-typescript)
166
+ * Examples [https://github.com/hotmeshio/samples-typescript](https://github.com/hotmeshio/samples-typescript)
862
167
 
863
- <br/>
168
+ ---
864
169
 
865
- ## Disclaimer
170
+ ## License
866
171
 
867
- This project is not affiliated with, endorsed by, or sponsored by Temporal Technologies, Inc. Temporal is a trademark of Temporal Technologies, Inc., and all references to Temporal and related technologies are for educational and demonstration purposes only.
172
+ Apache 2.0 see `LICENSE` for details.