@hotmeshio/hotmesh 0.3.29 → 0.3.31

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 (193) hide show
  1. package/build/index.d.ts +8 -2
  2. package/build/index.js +13 -7
  3. package/build/modules/enums.d.ts +65 -1
  4. package/build/modules/enums.js +79 -5
  5. package/build/modules/key.d.ts +52 -0
  6. package/build/modules/key.js +1 -1
  7. package/build/modules/utils.d.ts +73 -0
  8. package/build/modules/utils.js +1 -1
  9. package/build/package.json +6 -4
  10. package/build/services/activities/activity.d.ts +36 -0
  11. package/build/services/activities/activity.js +1 -1
  12. package/build/services/activities/await.js +1 -1
  13. package/build/services/activities/cycle.d.ts +7 -0
  14. package/build/services/activities/cycle.js +1 -1
  15. package/build/services/activities/hook.d.ts +6 -0
  16. package/build/services/activities/hook.js +1 -1
  17. package/build/services/activities/index.js +1 -1
  18. package/build/services/activities/interrupt.js +1 -1
  19. package/build/services/activities/signal.d.ts +6 -0
  20. package/build/services/activities/signal.js +1 -1
  21. package/build/services/activities/trigger.d.ts +7 -0
  22. package/build/services/activities/trigger.js +1 -1
  23. package/build/services/activities/worker.js +1 -1
  24. package/build/services/collator/index.d.ts +76 -0
  25. package/build/services/collator/index.js +1 -1
  26. package/build/services/compiler/deployer.js +1 -1
  27. package/build/services/compiler/index.d.ts +15 -0
  28. package/build/services/compiler/index.js +1 -1
  29. package/build/services/compiler/validator.d.ts +3 -0
  30. package/build/services/compiler/validator.js +1 -1
  31. package/build/services/connector/factory.d.ts +14 -0
  32. package/build/services/connector/factory.js +22 -2
  33. package/build/services/connector/index.d.ts +13 -1
  34. package/build/services/connector/index.js +13 -1
  35. package/build/services/connector/providers/postgres.js +7 -0
  36. package/build/services/engine/index.d.ts +162 -0
  37. package/build/services/engine/index.js +1 -1
  38. package/build/services/exporter/index.d.ts +27 -0
  39. package/build/services/exporter/index.js +1 -1
  40. package/build/services/hotmesh/index.d.ts +280 -0
  41. package/build/services/hotmesh/index.js +286 -0
  42. package/build/services/logger/index.js +1 -0
  43. package/build/services/mapper/index.d.ts +14 -0
  44. package/build/services/mapper/index.js +1 -1
  45. package/build/services/meshcall/index.d.ts +171 -0
  46. package/build/services/meshcall/index.js +190 -0
  47. package/build/services/meshcall/schemas/factory.d.ts +7 -0
  48. package/build/services/meshcall/schemas/factory.js +7 -0
  49. package/build/services/meshdata/index.d.ts +725 -3
  50. package/build/services/meshdata/index.js +689 -3
  51. package/build/services/meshflow/client.d.ts +89 -0
  52. package/build/services/meshflow/client.js +173 -4
  53. package/build/services/meshflow/connection.d.ts +17 -0
  54. package/build/services/meshflow/connection.js +17 -0
  55. package/build/services/meshflow/exporter.d.ts +22 -0
  56. package/build/services/meshflow/exporter.js +1 -1
  57. package/build/services/meshflow/handle.d.ts +68 -0
  58. package/build/services/meshflow/handle.js +75 -0
  59. package/build/services/meshflow/index.d.ts +92 -0
  60. package/build/services/meshflow/index.js +92 -0
  61. package/build/services/meshflow/schemas/factory.d.ts +25 -0
  62. package/build/services/meshflow/schemas/factory.js +26 -1
  63. package/build/services/meshflow/search.d.ts +120 -0
  64. package/build/services/meshflow/search.js +108 -0
  65. package/build/services/meshflow/worker.d.ts +97 -0
  66. package/build/services/meshflow/worker.js +116 -2
  67. package/build/services/meshflow/workflow/all.d.ts +6 -0
  68. package/build/services/meshflow/workflow/all.js +7 -0
  69. package/build/services/meshflow/workflow/context.d.ts +4 -0
  70. package/build/services/meshflow/workflow/context.js +4 -0
  71. package/build/services/meshflow/workflow/didRun.d.ts +6 -0
  72. package/build/services/meshflow/workflow/didRun.js +6 -0
  73. package/build/services/meshflow/workflow/emit.d.ts +7 -0
  74. package/build/services/meshflow/workflow/emit.js +7 -0
  75. package/build/services/meshflow/workflow/enrich.d.ts +7 -0
  76. package/build/services/meshflow/workflow/enrich.js +7 -0
  77. package/build/services/meshflow/workflow/execChild.d.ts +14 -0
  78. package/build/services/meshflow/workflow/execChild.js +18 -0
  79. package/build/services/meshflow/workflow/hook.d.ts +7 -0
  80. package/build/services/meshflow/workflow/hook.js +7 -0
  81. package/build/services/meshflow/workflow/index.d.ts +31 -0
  82. package/build/services/meshflow/workflow/index.js +31 -0
  83. package/build/services/meshflow/workflow/interrupt.d.ts +7 -0
  84. package/build/services/meshflow/workflow/interrupt.js +7 -0
  85. package/build/services/meshflow/workflow/isSideEffectAllowed.d.ts +8 -0
  86. package/build/services/meshflow/workflow/isSideEffectAllowed.js +8 -0
  87. package/build/services/meshflow/workflow/proxyActivities.d.ts +14 -0
  88. package/build/services/meshflow/workflow/proxyActivities.js +18 -3
  89. package/build/services/meshflow/workflow/random.d.ts +5 -0
  90. package/build/services/meshflow/workflow/random.js +5 -0
  91. package/build/services/meshflow/workflow/searchMethods.d.ts +4 -0
  92. package/build/services/meshflow/workflow/searchMethods.js +4 -0
  93. package/build/services/meshflow/workflow/signal.d.ts +6 -0
  94. package/build/services/meshflow/workflow/signal.js +6 -0
  95. package/build/services/meshflow/workflow/sleepFor.d.ts +7 -0
  96. package/build/services/meshflow/workflow/sleepFor.js +7 -0
  97. package/build/services/meshflow/workflow/trace.d.ts +10 -0
  98. package/build/services/meshflow/workflow/trace.js +10 -0
  99. package/build/services/meshflow/workflow/waitFor.d.ts +7 -0
  100. package/build/services/meshflow/workflow/waitFor.js +7 -0
  101. package/build/services/meshos/index.d.ts +213 -2
  102. package/build/services/meshos/index.js +221 -1
  103. package/build/services/pipe/functions/array.js +1 -1
  104. package/build/services/pipe/functions/bitwise.js +1 -1
  105. package/build/services/pipe/functions/conditional.js +1 -1
  106. package/build/services/pipe/functions/cron.d.ts +6 -0
  107. package/build/services/pipe/functions/cron.js +1 -1
  108. package/build/services/pipe/functions/date.d.ts +7 -0
  109. package/build/services/pipe/functions/date.js +1 -1
  110. package/build/services/pipe/functions/index.js +1 -1
  111. package/build/services/pipe/functions/json.js +1 -1
  112. package/build/services/pipe/functions/logical.js +1 -1
  113. package/build/services/pipe/functions/math.js +1 -1
  114. package/build/services/pipe/functions/number.js +1 -1
  115. package/build/services/pipe/functions/object.js +1 -1
  116. package/build/services/pipe/functions/string.js +1 -1
  117. package/build/services/pipe/functions/symbol.js +1 -1
  118. package/build/services/pipe/functions/unary.js +1 -1
  119. package/build/services/pipe/index.d.ts +15 -0
  120. package/build/services/pipe/index.js +1 -1
  121. package/build/services/quorum/index.d.ts +49 -0
  122. package/build/services/quorum/index.js +1 -1
  123. package/build/services/reporter/index.d.ts +5 -0
  124. package/build/services/reporter/index.js +1 -1
  125. package/build/services/router/index.d.ts +10 -0
  126. package/build/services/router/index.js +1 -1
  127. package/build/services/search/providers/postgres/postgres.d.ts +3 -0
  128. package/build/services/search/providers/postgres/postgres.js +1 -1
  129. package/build/services/search/providers/redis/ioredis.js +1 -1
  130. package/build/services/search/providers/redis/redis.js +1 -1
  131. package/build/services/serializer/index.js +1 -1
  132. package/build/services/store/cache.d.ts +19 -0
  133. package/build/services/store/cache.js +19 -0
  134. package/build/services/store/factory.js +1 -1
  135. package/build/services/store/providers/postgres/kvsql.d.ts +7 -0
  136. package/build/services/store/providers/postgres/kvsql.js +1 -1
  137. package/build/services/store/providers/postgres/kvtables.d.ts +4 -0
  138. package/build/services/store/providers/postgres/kvtables.js +1 -1
  139. package/build/services/store/providers/postgres/kvtransaction.js +1 -1
  140. package/build/services/store/providers/postgres/kvtypes/hash.d.ts +4 -0
  141. package/build/services/store/providers/postgres/kvtypes/hash.js +1 -1
  142. package/build/services/store/providers/postgres/kvtypes/list.js +1 -1
  143. package/build/services/store/providers/postgres/kvtypes/string.js +1 -1
  144. package/build/services/store/providers/postgres/kvtypes/zset.js +1 -1
  145. package/build/services/store/providers/postgres/postgres.d.ts +48 -1
  146. package/build/services/store/providers/postgres/postgres.js +1 -1
  147. package/build/services/store/providers/redis/_base.d.ts +42 -0
  148. package/build/services/store/providers/redis/_base.js +1 -1
  149. package/build/services/store/providers/redis/ioredis.d.ts +8 -0
  150. package/build/services/store/providers/redis/ioredis.js +1 -1
  151. package/build/services/store/providers/redis/redis.d.ts +6 -0
  152. package/build/services/store/providers/redis/redis.js +1 -1
  153. package/build/services/store/providers/store-initializable.js +1 -1
  154. package/build/services/stream/factory.js +2 -1
  155. package/build/services/stream/index.d.ts +4 -0
  156. package/build/services/stream/providers/nats/nats.js +1 -1
  157. package/build/services/stream/providers/postgres/kvtables.js +1 -1
  158. package/build/services/stream/providers/postgres/postgres.d.ts +21 -0
  159. package/build/services/stream/providers/postgres/postgres.js +1 -1
  160. package/build/services/stream/providers/redis/ioredis.js +1 -1
  161. package/build/services/stream/providers/redis/redis.js +1 -1
  162. package/build/services/stream/providers/stream-initializable.js +1 -1
  163. package/build/services/sub/providers/nats/nats.js +1 -1
  164. package/build/services/sub/providers/postgres/postgres.js +1 -1
  165. package/build/services/sub/providers/redis/ioredis.js +1 -1
  166. package/build/services/sub/providers/redis/redis.js +1 -1
  167. package/build/services/task/index.d.ts +9 -0
  168. package/build/services/task/index.js +1 -1
  169. package/build/services/telemetry/index.d.ts +7 -0
  170. package/build/services/telemetry/index.js +1 -1
  171. package/build/services/worker/index.d.ts +37 -0
  172. package/build/services/worker/index.js +1 -1
  173. package/build/types/activity.d.ts +81 -0
  174. package/build/types/exporter.d.ts +13 -0
  175. package/build/types/hotmesh.d.ts +144 -0
  176. package/build/types/hotmesh.js +3 -0
  177. package/build/types/job.d.ts +101 -0
  178. package/build/types/manifest.d.ts +8 -0
  179. package/build/types/meshcall.d.ts +148 -0
  180. package/build/types/meshdata.d.ts +193 -0
  181. package/build/types/meshflow.d.ts +297 -0
  182. package/build/types/nats.d.ts +55 -0
  183. package/build/types/pipe.d.ts +65 -0
  184. package/build/types/provider.d.ts +43 -0
  185. package/build/types/quorum.d.ts +12 -0
  186. package/build/types/redis.d.ts +6 -0
  187. package/build/types/stream.d.ts +65 -0
  188. package/build/types/stream.js +4 -0
  189. package/index.ts +8 -1
  190. package/package.json +6 -4
  191. package/types/manifest.ts +2 -2
  192. package/types/meshcall.ts +1 -2
  193. package/types/meshdata.ts +1 -1
@@ -1,19 +1,108 @@
1
1
  import { HotMesh } from '../hotmesh';
2
2
  import { ClientConfig, ClientWorkflow, Connection, WorkflowOptions } from '../../types/meshflow';
3
+ /**
4
+ * The MeshFlow `Client` service is functionally
5
+ * equivalent to the Temporal `Client` service.
6
+ * Start a new workflow execution by calling
7
+ * `workflow.start`. Note the direct connection to
8
+ * Postgres.
9
+ *
10
+ * NATS can be used as the message broker if advanced
11
+ * messaging is required (i.e, patterned subscriptions).
12
+ * @example
13
+
14
+ * ```typescript
15
+ * //client.ts
16
+ * import { Client, HotMesh } from '@hotmeshio/hotmesh';
17
+ * import { Client as Postgres } from 'pg';
18
+
19
+ * async function run(): Promise<string> {
20
+ * const client = new Client({
21
+ * connection: {
22
+ * class: Postgres,
23
+ * options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' }
24
+ * }
25
+ * });
26
+
27
+ * const handle = await client.workflow.start({
28
+ * args: ['HotMesh'],
29
+ * taskQueue: 'default',
30
+ * workflowName: 'example',
31
+ * workflowId: HotMesh.guid()
32
+ * });
33
+
34
+ * return await handle.result();
35
+ * //returns ['Hello HotMesh', '¡Hola, HotMesh!']
36
+ * }
37
+ * ```
38
+ */
3
39
  export declare class ClientService {
40
+ /**
41
+ * @private
42
+ */
4
43
  connection: Connection;
44
+ /**
45
+ * @private
46
+ */
5
47
  options: WorkflowOptions;
48
+ /**
49
+ * @private
50
+ */
6
51
  static topics: string[];
52
+ /**
53
+ * @private
54
+ */
7
55
  static instances: Map<string, HotMesh | Promise<HotMesh>>;
56
+ /**
57
+ * @private
58
+ */
8
59
  constructor(config: ClientConfig);
60
+ /**
61
+ * @private
62
+ */
9
63
  getHotMeshClient: (workflowTopic: string | null, namespace?: string) => Promise<HotMesh>;
64
+ /**
65
+ * Creates a stream where messages can be published to ensure there is a
66
+ * channel in place when the message arrives (a race condition for those
67
+ * platforms without implicit topic setup).
68
+ * @private
69
+ */
10
70
  static createStream: (hotMeshClient: HotMesh, workflowTopic: string, namespace?: string) => Promise<void>;
11
71
  hashOptions(): string;
72
+ /**
73
+ * It is possible for a client to invoke a workflow without first
74
+ * creating the stream. This method will verify that the stream
75
+ * exists and if not, create it.
76
+ * @private
77
+ */
12
78
  verifyStream: (hotMeshClient: HotMesh, workflowTopic: string, namespace?: string) => Promise<void>;
79
+ /**
80
+ * @private
81
+ */
13
82
  search: (hotMeshClient: HotMesh, index: string, query: string[]) => Promise<string[]>;
83
+ /**
84
+ * The MeshFlow `Client` service is functionally
85
+ * equivalent to the Temporal `Client` service.
86
+ * Starting a workflow is the primary use case and
87
+ * is accessed by calling workflow.start().
88
+ */
14
89
  workflow: ClientWorkflow;
90
+ /**
91
+ * Any router can be used to deploy and activate the HotMesh
92
+ * distributed executable to the active quorum EXCEPT for
93
+ * those routers in `readonly` mode.
94
+ */
15
95
  deployAndActivate(namespace?: string, version?: string): Promise<void>;
96
+ /**
97
+ * @private
98
+ */
16
99
  verifyWorkflowActive(hotMesh: HotMesh, appId?: string, count?: number): Promise<boolean>;
100
+ /**
101
+ * @private
102
+ */
17
103
  activateWorkflow(hotMesh: HotMesh, appId?: string, version?: string): Promise<void>;
104
+ /**
105
+ * @private
106
+ */
18
107
  static shutdown(): Promise<void>;
19
108
  }
@@ -10,9 +10,55 @@ const types_1 = require("../../types");
10
10
  const search_1 = require("./search");
11
11
  const handle_1 = require("./handle");
12
12
  const factory_1 = require("./schemas/factory");
13
+ /**
14
+ * The MeshFlow `Client` service is functionally
15
+ * equivalent to the Temporal `Client` service.
16
+ * Start a new workflow execution by calling
17
+ * `workflow.start`. Note the direct connection to
18
+ * Postgres.
19
+ *
20
+ * NATS can be used as the message broker if advanced
21
+ * messaging is required (i.e, patterned subscriptions).
22
+ * @example
23
+
24
+ * ```typescript
25
+ * //client.ts
26
+ * import { Client, HotMesh } from '@hotmeshio/hotmesh';
27
+ * import { Client as Postgres } from 'pg';
28
+
29
+ * async function run(): Promise<string> {
30
+ * const client = new Client({
31
+ * connection: {
32
+ * class: Postgres,
33
+ * options: { connectionString: 'postgresql://usr:pwd@localhost:5432/db' }
34
+ * }
35
+ * });
36
+
37
+ * const handle = await client.workflow.start({
38
+ * args: ['HotMesh'],
39
+ * taskQueue: 'default',
40
+ * workflowName: 'example',
41
+ * workflowId: HotMesh.guid()
42
+ * });
43
+
44
+ * return await handle.result();
45
+ * //returns ['Hello HotMesh', '¡Hola, HotMesh!']
46
+ * }
47
+ * ```
48
+ */
13
49
  class ClientService {
50
+ /**
51
+ * @private
52
+ */
14
53
  constructor(config) {
54
+ /**
55
+ * @private
56
+ */
15
57
  this.getHotMeshClient = async (workflowTopic, namespace) => {
58
+ //namespace isolation requires the connection options to be hashed
59
+ //as multiple intersecting databases can be used by the same service
60
+ //hashing options allows for reuse of the same connection without risk of
61
+ //overwriting data in another namespace.
16
62
  const optionsHash = this.hashOptions();
17
63
  const targetNS = namespace ?? factory_1.APP_ID;
18
64
  const connectionNS = `${optionsHash}.${targetNS}`;
@@ -21,18 +67,35 @@ class ClientService {
21
67
  await this.verifyWorkflowActive(hotMeshClient, targetNS);
22
68
  return hotMeshClient;
23
69
  }
24
- const hotMeshClient = hotmesh_1.HotMesh.init({
70
+ //init, but don't await
71
+ const readonly = this.connection.readonly ?? undefined;
72
+ let hotMeshClient = hotmesh_1.HotMesh.init({
25
73
  appId: targetNS,
26
74
  logLevel: enums_1.HMSH_LOGLEVEL,
27
75
  engine: {
28
- readonly: this.connection.readonly ?? undefined,
76
+ readonly,
29
77
  connection: this.connection,
30
78
  },
31
79
  });
80
+ //synchronously cache the promise (before awaiting)
32
81
  ClientService.instances.set(connectionNS, hotMeshClient);
33
- await this.activateWorkflow(await hotMeshClient, targetNS);
34
- return hotMeshClient;
82
+ //resolve, activate, and return the client
83
+ const resolvedClient = await hotMeshClient;
84
+ if (!readonly) {
85
+ resolvedClient.engine.logger.info('meshflow-readonly-client', {
86
+ guid: resolvedClient.engine.guid,
87
+ appId: targetNS,
88
+ });
89
+ await this.activateWorkflow(resolvedClient, targetNS);
90
+ }
91
+ return resolvedClient;
35
92
  };
93
+ /**
94
+ * It is possible for a client to invoke a workflow without first
95
+ * creating the stream. This method will verify that the stream
96
+ * exists and if not, create it.
97
+ * @private
98
+ */
36
99
  this.verifyStream = async (hotMeshClient, workflowTopic, namespace) => {
37
100
  const optionsHash = this.hashOptions();
38
101
  const targetNS = namespace ?? factory_1.APP_ID;
@@ -42,18 +105,33 @@ class ClientService {
42
105
  await ClientService.createStream(hotMeshClient, workflowTopic, namespace);
43
106
  }
44
107
  };
108
+ /**
109
+ * @private
110
+ */
45
111
  this.search = async (hotMeshClient, index, query) => {
46
112
  const searchClient = hotMeshClient.engine.search;
47
113
  return await searchClient.sendIndexedQuery(index, query);
48
114
  };
115
+ /**
116
+ * The MeshFlow `Client` service is functionally
117
+ * equivalent to the Temporal `Client` service.
118
+ * Starting a workflow is the primary use case and
119
+ * is accessed by calling workflow.start().
120
+ */
49
121
  this.workflow = {
122
+ /**
123
+ * Starts a workflow, verifies the idempotent id, and
124
+ * adds searchable data to the record.
125
+ */
50
126
  start: async (options) => {
51
127
  const taskQueueName = options.taskQueue ?? options.entity;
52
128
  const workflowName = options.entity ?? options.workflowName;
53
129
  const trc = options.workflowTrace;
54
130
  const spn = options.workflowSpan;
131
+ //hotmesh `topic` is equivalent to `queue+workflowname` pattern in other systems
55
132
  const workflowTopic = `${taskQueueName}-${workflowName}`;
56
133
  const hotMeshClient = await this.getHotMeshClient(workflowTopic, options.namespace);
134
+ //verify that the stream channel exists before enqueueing
57
135
  await this.verifyStream(hotMeshClient, workflowTopic, options.namespace);
58
136
  const payload = {
59
137
  arguments: [...options.args],
@@ -76,10 +154,32 @@ class ClientService {
76
154
  });
77
155
  return new handle_1.WorkflowHandleService(hotMeshClient, workflowTopic, jobId);
78
156
  },
157
+ /**
158
+ * Sends a message payload to a running workflow that is paused and awaiting the signal
159
+ */
79
160
  signal: async (signalId, data, namespace) => {
80
161
  const topic = `${namespace ?? factory_1.APP_ID}.wfs.signal`;
81
162
  return await (await this.getHotMeshClient(topic, namespace)).hook(topic, { id: signalId, data });
82
163
  },
164
+ /**
165
+ * Spawns an a new, isolated execution cycle within the same job.
166
+ * Similar to `worker` functions, `hook` functions have a linked
167
+ * function. But hooks do not start a new job and instead read/write
168
+ * their isolated activity data to an existing Job record (HASH).
169
+ *
170
+ * This example spawns a hook that will update workflow `guid123`.
171
+ *
172
+ * @example
173
+ * ```typescript
174
+ * await client.workflow.hook({
175
+ * namespace: 'demo',
176
+ * taskQueue: 'default',
177
+ * workflowName: 'myDemoFunction',
178
+ * workflowId: 'guid123',
179
+ * args: ['Hello'],
180
+ * });
181
+ * ```
182
+ */
83
183
  hook: async (options) => {
84
184
  const workflowTopic = `${options.taskQueue ?? options.entity}-${options.entity ?? options.workflowName}`;
85
185
  const payload = {
@@ -90,8 +190,10 @@ class ClientService {
90
190
  maximumAttempts: options.config?.maximumAttempts || enums_1.HMSH_MESHFLOW_MAX_ATTEMPTS,
91
191
  maximumInterval: (0, utils_1.s)(options.config?.maximumInterval || enums_1.HMSH_MESHFLOW_MAX_INTERVAL),
92
192
  };
193
+ //seed search data before entering
93
194
  const hotMeshClient = await this.getHotMeshClient(workflowTopic, options.namespace);
94
195
  const msgId = await hotMeshClient.hook(`${hotMeshClient.appId}.flow.signal`, payload, types_1.StreamStatus.PENDING, 202);
196
+ //todo: commit search data BEFORE enqueuing hook
95
197
  if (options.search?.data) {
96
198
  const searchSessionId = `-search-${hotmesh_1.HotMesh.guid()}-0`;
97
199
  const search = new search_1.Search(options.workflowId, hotMeshClient, searchSessionId);
@@ -100,11 +202,49 @@ class ClientService {
100
202
  }
101
203
  return msgId;
102
204
  },
205
+ /**
206
+ * Returns a reference to a running workflow,
207
+ * allowing callers to check the status of the workflow,
208
+ * interrupt it, and even await its eventual response
209
+ * if still in a pending state.
210
+ *
211
+ * @example
212
+ * ```typescript
213
+ * const handle = await client.workflow.getHandle(
214
+ * 'default',
215
+ * 'myFunction',
216
+ * 'someGuid123',
217
+ * 'demo',
218
+ * );
219
+ * ```
220
+ */
103
221
  getHandle: async (taskQueue, workflowName, workflowId, namespace) => {
104
222
  const workflowTopic = `${taskQueue}-${workflowName}`;
105
223
  const hotMeshClient = await this.getHotMeshClient(workflowTopic, namespace);
106
224
  return new handle_1.WorkflowHandleService(hotMeshClient, workflowTopic, workflowId);
107
225
  },
226
+ /**
227
+ * Provides direct access to the SEARCH backend when making
228
+ * queries. Taskqueues and workflow names are
229
+ * used to identify the point of presence to use. `...args` is
230
+ * the tokenized query. When querying Redis/FTSEARCH, the trailing
231
+ * ...args might be `'@_custom1:meshflow'`. For postgres,
232
+ * the trailing ...args would be: `'_custom', 'meshflow'`. In each case,
233
+ * the query looks for all job data where the field `_custom` is
234
+ * equal to `meshflow`.
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * await client.workflow.search(
239
+ * 'someTaskQueue'
240
+ * 'someWorkflowName',
241
+ * 'meshflow',
242
+ * 'user',
243
+ * ...args,
244
+ * );
245
+ * //returns [count, [id, fields[]], [id, fields[]], [id, fields[]], ...]]
246
+ * ```
247
+ */
108
248
  search: async (taskQueue, workflowName, namespace, index, ...query) => {
109
249
  const workflowTopic = `${taskQueue}-${workflowName}`;
110
250
  const hotMeshClient = await this.getHotMeshClient(workflowTopic, namespace);
@@ -123,9 +263,11 @@ class ClientService {
123
263
  }
124
264
  hashOptions() {
125
265
  if ('options' in this.connection) {
266
+ //shorthand format
126
267
  return (0, utils_1.hashOptions)(this.connection.options);
127
268
  }
128
269
  else {
270
+ //longhand format (sub, store, stream, pub, search)
129
271
  const response = [];
130
272
  for (const p in this.connection) {
131
273
  if (this.connection[p].options) {
@@ -135,6 +277,11 @@ class ClientService {
135
277
  return response.join('');
136
278
  }
137
279
  }
280
+ /**
281
+ * Any router can be used to deploy and activate the HotMesh
282
+ * distributed executable to the active quorum EXCEPT for
283
+ * those routers in `readonly` mode.
284
+ */
138
285
  async deployAndActivate(namespace = factory_1.APP_ID, version = factory_1.APP_VERSION) {
139
286
  if (isNaN(Number(version))) {
140
287
  throw new Error('Invalid version number');
@@ -142,6 +289,9 @@ class ClientService {
142
289
  const hotMesh = await this.getHotMeshClient('', namespace);
143
290
  await this.activateWorkflow(hotMesh, namespace, version);
144
291
  }
292
+ /**
293
+ * @private
294
+ */
145
295
  async verifyWorkflowActive(hotMesh, appId = factory_1.APP_ID, count = 0) {
146
296
  const app = await hotMesh.engine.store.getApp(appId);
147
297
  const appVersion = app?.version;
@@ -154,6 +304,9 @@ class ClientService {
154
304
  }
155
305
  return true;
156
306
  }
307
+ /**
308
+ * @private
309
+ */
157
310
  async activateWorkflow(hotMesh, appId = factory_1.APP_ID, version = factory_1.APP_VERSION) {
158
311
  const app = await hotMesh.engine.store.getApp(appId);
159
312
  const appVersion = app?.version;
@@ -181,6 +334,9 @@ class ClientService {
181
334
  }
182
335
  }
183
336
  }
337
+ /**
338
+ * @private
339
+ */
184
340
  static async shutdown() {
185
341
  for (const [_, hotMeshInstance] of ClientService.instances) {
186
342
  (await hotMeshInstance).stop();
@@ -188,8 +344,20 @@ class ClientService {
188
344
  }
189
345
  }
190
346
  _a = ClientService;
347
+ /**
348
+ * @private
349
+ */
191
350
  ClientService.topics = [];
351
+ /**
352
+ * @private
353
+ */
192
354
  ClientService.instances = new Map();
355
+ /**
356
+ * Creates a stream where messages can be published to ensure there is a
357
+ * channel in place when the message arrives (a race condition for those
358
+ * platforms without implicit topic setup).
359
+ * @private
360
+ */
193
361
  ClientService.createStream = async (hotMeshClient, workflowTopic, namespace) => {
194
362
  const params = { appId: namespace ?? factory_1.APP_ID, topic: workflowTopic };
195
363
  const streamKey = hotMeshClient.engine.store.mintKey(key_1.KeyType.STREAMS, params);
@@ -197,6 +365,7 @@ ClientService.createStream = async (hotMeshClient, workflowTopic, namespace) =>
197
365
  await hotMeshClient.engine.stream.createConsumerGroup(streamKey, 'WORKER');
198
366
  }
199
367
  catch (err) {
368
+ //ignore if already exists
200
369
  }
201
370
  };
202
371
  exports.ClientService = ClientService;
@@ -1,6 +1,23 @@
1
1
  import { Connection } from '../../types/meshflow';
2
2
  import { ProviderConfig, ProvidersConfig } from '../../types/provider';
3
+ /**
4
+ * The Connection service is used to declare the class
5
+ * and connection options but does not connect quite yet. Connection
6
+ * happens at a later lifecycle stage when a workflow
7
+ * is started by the MeshFlow Client module (`(new MeshFlow.Client())).start()`).
8
+ *
9
+ * The config options optionall support a multi-connection setup
10
+ * where the `store` connection explicitly defined along with `stream`, `sub`, etc.
11
+ * For example, Postgres can be used for stream and store while
12
+ * Redis is used for sub.
13
+ */
3
14
  export declare class ConnectionService {
15
+ /**
16
+ * @private
17
+ */
4
18
  constructor();
19
+ /**
20
+ * Instance initializer
21
+ */
5
22
  static connect(config: ProviderConfig | ProvidersConfig): Promise<Connection>;
6
23
  }
@@ -1,8 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ConnectionService = void 0;
4
+ /**
5
+ * The Connection service is used to declare the class
6
+ * and connection options but does not connect quite yet. Connection
7
+ * happens at a later lifecycle stage when a workflow
8
+ * is started by the MeshFlow Client module (`(new MeshFlow.Client())).start()`).
9
+ *
10
+ * The config options optionall support a multi-connection setup
11
+ * where the `store` connection explicitly defined along with `stream`, `sub`, etc.
12
+ * For example, Postgres can be used for stream and store while
13
+ * Redis is used for sub.
14
+ */
4
15
  class ConnectionService {
16
+ /**
17
+ * @private
18
+ */
5
19
  constructor() { }
20
+ /**
21
+ * Instance initializer
22
+ */
6
23
  static async connect(config) {
7
24
  return 'store' in config
8
25
  ? config
@@ -10,20 +10,42 @@ declare class ExporterService {
10
10
  symbols: Promise<Symbols> | Symbols;
11
11
  private static symbols;
12
12
  constructor(appId: string, store: StoreService<ProviderClient, ProviderTransaction>, logger: ILogger);
13
+ /**
14
+ * Convert the job hash from its compiles format into a MeshFlowJobExport object with
15
+ * facets that describe the workflow in terms relevant to narrative storytelling.
16
+ */
13
17
  export(jobId: string, options?: ExportOptions): Promise<MeshFlowJobExport>;
18
+ /**
19
+ * Inflates the job data from Redis into a MeshFlowJobExport object
20
+ * @param jobHash - the job data from Redis
21
+ * @param dependencyList - the list of dependencies for the job
22
+ * @returns - the inflated job data
23
+ */
14
24
  inflate(jobHash: StringStringType, options: ExportOptions): MeshFlowJobExport;
15
25
  resolveValue(raw: string, withValues: boolean): Record<string, any> | string | number | null;
26
+ /**
27
+ * Inflates the key from Redis, 3-character symbol
28
+ * into a human-readable JSON path, reflecting the
29
+ * tree-like structure of the unidimensional Hash
30
+ * @private
31
+ */
16
32
  inflateKey(key: string): string;
17
33
  filterFields(fullObject: MeshFlowJobExport, block?: ExportFields[], allow?: ExportFields[]): Partial<MeshFlowJobExport>;
18
34
  inflateTransition(match: RegExpMatchArray, value: string, transitionsObject: Record<string, TransitionType>): void;
19
35
  sortEntriesByCreated(obj: {
20
36
  [key: string]: TransitionType;
21
37
  }): TransitionType[];
38
+ /**
39
+ * marker names are overloaded with details like sequence, type, etc
40
+ */
22
41
  keyToObject(key: string): {
23
42
  index: number;
24
43
  dimension?: string;
25
44
  secondary?: number;
26
45
  };
46
+ /**
47
+ * idem list has a complicated sort order based on indexes and dimensions
48
+ */
27
49
  sortParts(parts: TimelineType[]): TimelineType[];
28
50
  }
29
51
  export { ExporterService };
@@ -1 +1 @@
1
- 'use strict';const q=b;(function(c,d){const p=b,e=c();while(!![]){try{const f=-parseInt(p(0x1d2))/0x1*(parseInt(p(0x1cc))/0x2)+-parseInt(p(0x1d1))/0x3*(-parseInt(p(0x1e6))/0x4)+parseInt(p(0x1d6))/0x5+parseInt(p(0x1d7))/0x6+-parseInt(p(0x1cd))/0x7+-parseInt(p(0x1ff))/0x8*(-parseInt(p(0x1e9))/0x9)+-parseInt(p(0x1d5))/0xa;if(f===d)break;else e['push'](e['shift']());}catch(g){e['push'](e['shift']());}}}(a,0xe9d7a));function a(){const D=['6222696jYiilY','data','split','$error','__esModule','sortEntriesByCreated','symbols','inflate','resolveValue','entries','updated','/output/metadata/ac','endsWith','get','allow','16gRXjjk','join','object','135ocUtZl','keyToObject','inflateKey','values','SerializerService','block','../serializer','getRaw','sort','length','defineProperty','appId','forEach','inflateTransition','filterFields','push','created','shift','sortParts','/output/metadata/au','export','restoreHierarchy','859864nNEeXX','../../modules/utils','set','10XnRBQv','6522250raRppZ','ExporterService','startsWith','localeCompare','951348VqfGas','347239lKJDnM','store','match','11681250eUeIaQ','4380315IOOTJD'];a=function(){return D;};return a();}function b(c,d){const e=a();return b=function(f,g){f=f-0x1cc;let h=e[f];return h;},b(c,d);}Object[q(0x1f3)](exports,q(0x1db),{'value':!![]}),exports[q(0x1ce)]=void 0x0;const utils_1=require(q(0x200)),serializer_1=require(q(0x1ef));class ExporterService{constructor(c,d,e){const r=q;this[r(0x1f4)]=c,this['logger']=e,this[r(0x1d3)]=d;}async[q(0x1fd)](c,d={}){const s=q;if(!ExporterService['symbols']['has'](this[s(0x1f4)])){const g=this[s(0x1d3)]['getAllSymbols']();ExporterService['symbols'][s(0x201)](this[s(0x1f4)],await g);}const e=await this[s(0x1d3)][s(0x1f0)](c),f=this[s(0x1de)](e,d);return f;}['inflate'](c,d){const t=q,e=[],f={},g={},h={},i=/^([a-zA-Z]{3}),(\d+(?:,\d+)*)/;return Object[t(0x1e0)](c)['forEach'](([j,k])=>{const u=t,l=j[u(0x1d4)](i);if(l)this[u(0x1f6)](l,k,h);else{if(j[u(0x1cf)]('_'))g[j['substring'](0x1)]=k;else{if(j[u(0x1cf)]('-')){const m=this[u(0x1ea)](j);e[u(0x1f8)]({...m,'key':j,'value':this[u(0x1df)](k,d[u(0x1ec)])});}else j['length']===0x3&&(f[this[u(0x1eb)](j)]=serializer_1[u(0x1ed)]['fromString'](k));}}}),this[t(0x1f7)]({'data':(0x0,utils_1[t(0x1fe)])(g),'state':Object[t(0x1e0)]((0x0,utils_1[t(0x1fe)])(f))[0x0][0x1],'status':parseInt(c[':'],0xa),'timeline':this[t(0x1fb)](e),'transitions':this['sortEntriesByCreated'](h)},d[t(0x1ee)],d[t(0x1e5)]);}[q(0x1df)](c,d){const v=q,e=serializer_1['SerializerService']['fromString'](c);if(d!==![])return e;return e&&typeof e===v(0x1e8)&&('data'in e&&(e[v(0x1d8)]={}),v(0x1da)in e&&(e[v(0x1da)]={})),e;}[q(0x1eb)](c){const w=q,d=ExporterService[w(0x1dd)][w(0x1e4)](this[w(0x1f4)]);if(c in d){const e=d[c],f=e[w(0x1d9)]('/');return f['join']('/');}return c;}['filterFields'](c,d=[],e=[]){const x=q;let f={};return e&&e[x(0x1f2)]>0x0?e['forEach'](g=>{g in c&&(f[g]=c[g]);}):f={...c},d&&d[x(0x1f2)]>0x0&&d[x(0x1f5)](g=>{g in f&&delete f[g];}),f;}['inflateTransition'](c,d,e){const y=q,[f,g,h]=c,i=this[y(0x1eb)](g),j=i['split']('/'),k=j[0x0],l=i['endsWith'](y(0x1e2)),m=i[y(0x1e3)](y(0x1fc));if(l||m){const n=k+','+h,o=e[n];!o?e[n]={'activity':k,'dimensions':h,'created':l?d:null,'updated':m?d:null}:o[l?y(0x1f9):'updated']=d;}}[q(0x1dc)](c){const z=q,d=Object['values'](c);return d[z(0x1f1)]((e,f)=>{const A=z;return(e[A(0x1f9)]||e[A(0x1e1)])[A(0x1d0)](f[A(0x1f9)]||f[A(0x1e1)]);}),d;}['keyToObject'](c){const C=q;function d(f){const B=b,g=f[B(0x1d9)](',');if(g['length']>0x1)return g[B(0x1fa)](),g[B(0x1e7)](',');}const e=c[C(0x1d9)]('-');return e[C(0x1f2)]===0x4?{'index':parseInt(e[0x2],0xa),'dimension':d(e[0x1])}:{'index':parseInt(e[0x2],0xa),'secondary':parseInt(e[0x3],0xa),'dimension':d(e[0x1])};}[q(0x1fb)](c){return c['sort']((d,e)=>{const {dimension:f,index:g,secondary:h}=d,{dimension:i,index:j,secondary:k}=e;if(f===undefined&&i!==undefined)return-0x1;if(f!==undefined&&i===undefined)return 0x1;if(f!==undefined&&i!==undefined){if(f<i)return-0x1;if(f>i)return 0x1;}if(g<j)return-0x1;if(g>j)return 0x1;if(h===undefined&&k!==undefined)return-0x1;if(h!==undefined&&k===undefined)return 0x1;if(h!==undefined&&k!==undefined){if(h<k)return-0x1;if(h>k)return 0x1;}return 0x0;});}}exports['ExporterService']=ExporterService,ExporterService['symbols']=new Map();
1
+ 'use strict';const y=b;(function(c,d){const x=b,e=c();while(!![]){try{const f=parseInt(x(0x168))/0x1*(-parseInt(x(0x16c))/0x2)+-parseInt(x(0x167))/0x3+-parseInt(x(0x151))/0x4*(-parseInt(x(0x16b))/0x5)+parseInt(x(0x174))/0x6*(-parseInt(x(0x16a))/0x7)+parseInt(x(0x146))/0x8*(parseInt(x(0x16e))/0x9)+parseInt(x(0x14b))/0xa+parseInt(x(0x15d))/0xb;if(f===d)break;else e['push'](e['shift']());}catch(g){e['push'](e['shift']());}}}(a,0x9fcc0));Object[y(0x17c)](exports,'__esModule',{'value':!0x0}),exports['ExporterService']=void 0x0;function b(c,d){const e=a();return b=function(f,g){f=f-0x145;let h=e[f];return h;},b(c,d);}const utils_1=require('../../modules/utils'),serializer_1=require(y(0x145));function a(){const L=['endsWith','3528489FyPZCf','3608msDCbM','substring','128030lXyhbD','43535OOFWQo','522vopEjC','/output/metadata/au','36594OfdJQc','getRaw','keyToObject','appId','get','split','54PVjLwy','entries','sortParts','SerializerService','sort','join','localeCompare','allow','defineProperty','../serializer','488GhczTX','ExporterService','inflateTransition','forEach','filterFields','5490300uibDlf','set','symbols','inflateKey','resolveValue','match','260guGtWx','inflate','length','created','$error','fromString','logger','/output/metadata/ac','data','restoreHierarchy','startsWith','has','17313758BYcTDU','block','store','sortEntriesByCreated','export','shift','values','updated','getAllSymbols'];a=function(){return L;};return a();}class ExporterService{constructor(c,d,f){const z=y;this['appId']=c,this[z(0x157)]=f,this[z(0x15f)]=d;}async[y(0x161)](c,d={}){const A=y;if(!ExporterService[A(0x14d)][A(0x15c)](this[A(0x171)])){const g=this['store'][A(0x165)]();ExporterService[A(0x14d)][A(0x14c)](this[A(0x171)],await g);}const f=await this[A(0x15f)][A(0x16f)](c);return this[A(0x152)](f,d);}[y(0x152)](c,d){const C=y,f=[],g={},h={},j={},k=/^([a-zA-Z]{3}),(\d+(?:,\d+)*)/;return Object['entries'](c)['forEach'](([m,p])=>{const B=b,q=m[B(0x150)](k);if(q)this[B(0x148)](q,p,j);else{if(m[B(0x15b)]('_'))h[m[B(0x169)](0x1)]=p;else{if(m[B(0x15b)]('-')){const u=this[B(0x170)](m);f['push']({...u,'key':m,'value':this[B(0x14f)](p,d[B(0x163)])});}else 0x3===m[B(0x153)]&&(g[this[B(0x14e)](m)]=serializer_1[B(0x177)]['fromString'](p));}}}),this['filterFields']({'data':(0x0,utils_1[C(0x15a)])(h),'state':Object[C(0x175)]((0x0,utils_1[C(0x15a)])(g))[0x0][0x1],'status':parseInt(c[':'],0xa),'timeline':this[C(0x176)](f),'transitions':this[C(0x160)](j)},d[C(0x15e)],d[C(0x17b)]);}[y(0x14f)](c,d){const D=y,f=serializer_1[D(0x177)][D(0x156)](c);return!0x1!==d||f&&'object'==typeof f&&('data'in f&&(f[D(0x159)]={}),D(0x155)in f&&(f['$error']={})),f;}['inflateKey'](c){const E=y,d=ExporterService[E(0x14d)][E(0x172)](this[E(0x171)]);if(c in d)return d[c][E(0x173)]('/')[E(0x179)]('/');return c;}[y(0x14a)](c,d=[],f=[]){const F=y;let g={};return f&&f[F(0x153)]>0x0?f[F(0x149)](h=>{h in c&&(g[h]=c[h]);}):g={...c},d&&d[F(0x153)]>0x0&&d[F(0x149)](h=>{h in g&&delete g[h];}),g;}[y(0x148)](c,f,g){const G=y,[h,j,k]=c,m=this['inflateKey'](j),p=m[G(0x173)]('/')[0x0],q=m['endsWith'](G(0x158)),u=m[G(0x166)](G(0x16d));if(q||u){const v=p+','+k,w=g[v];w?w[q?'created':G(0x164)]=f:g[v]={'activity':p,'dimensions':k,'created':q?f:null,'updated':u?f:null};}}[y(0x160)](c){const H=y,d=Object[H(0x163)](c);return d[H(0x178)]((f,g)=>(f['created']||f[H(0x164)])[H(0x17a)](g[H(0x154)]||g[H(0x164)])),d;}[y(0x170)](c){const J=y;function d(g){const I=b,h=g[I(0x173)](',');if(h[I(0x153)]>0x1)return h[I(0x162)](),h['join'](',');}const f=c[J(0x173)]('-');return 0x4===f['length']?{'index':parseInt(f[0x2],0xa),'dimension':d(f[0x1])}:{'index':parseInt(f[0x2],0xa),'secondary':parseInt(f[0x3],0xa),'dimension':d(f[0x1])};}[y(0x176)](c){const K=y;return c[K(0x178)]((d,f)=>{const {dimension:g,index:h,secondary:j}=d,{dimension:k,index:l,secondary:m}=f;if(void 0x0===g&&void 0x0!==k)return-0x1;if(void 0x0!==g&&void 0x0===k)return 0x1;if(void 0x0!==g&&void 0x0!==k){if(g<k)return-0x1;if(g>k)return 0x1;}if(h<l)return-0x1;if(h>l)return 0x1;if(void 0x0===j&&void 0x0!==m)return-0x1;if(void 0x0!==j&&void 0x0===m)return 0x1;if(void 0x0!==j&&void 0x0!==m){if(j<m)return-0x1;if(j>m)return 0x1;}return 0x0;});}}exports[y(0x147)]=ExporterService,ExporterService['symbols']=new Map();
@@ -3,18 +3,86 @@ import { MeshFlowJobExport, ExportOptions } from '../../types/exporter';
3
3
  import { JobInterruptOptions } from '../../types/job';
4
4
  import { StreamError } from '../../types/stream';
5
5
  import { ExporterService } from './exporter';
6
+ /**
7
+ * The WorkflowHandleService provides methods to interact with a running
8
+ * workflow. This includes exporting the workflow, sending signals, and
9
+ * querying the state of the workflow. It is instanced/accessed via the
10
+ * MeshFlow.Client class.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { Client } from '@hotmeshio/hotmesh';
15
+ * import { Client as Postgres } from 'pg';
16
+ *
17
+ * const client = new Client({ connection: {
18
+ * class: Postgres,
19
+ * options: { connectionString: 'postgres://user:pass@localhost:5432/db' }
20
+ * }});
21
+ *
22
+ * const handle = await client.workflow.start({
23
+ * args: ['HotMesh'],
24
+ * taskQueue: 'hello-world',
25
+ * });
26
+ *
27
+ * //perform actions like send a signal
28
+ * await handle.signal('my-signal', { data: 'Hello' });
29
+ * ```
30
+ */
6
31
  export declare class WorkflowHandleService {
32
+ /**
33
+ * @private
34
+ */
7
35
  exporter: ExporterService;
8
36
  hotMesh: HotMesh;
9
37
  workflowTopic: string;
10
38
  workflowId: string;
39
+ /**
40
+ * @private
41
+ */
11
42
  constructor(hotMesh: HotMesh, workflowTopic: string, workflowId: string);
43
+ /**
44
+ * Exports the workflow state to a JSON object.
45
+ */
12
46
  export(options?: ExportOptions): Promise<MeshFlowJobExport>;
47
+ /**
48
+ * Sends a signal to the workflow. This is a way to send
49
+ * a message to a workflow that is paused due to having
50
+ * executed `MeshFlow.workflow.waitFor`. The workflow
51
+ * will awaken if no other signals are pending.
52
+ */
13
53
  signal(signalId: string, data: Record<any, any>): Promise<void>;
54
+ /**
55
+ * Returns the job state of the workflow. If the workflow has completed
56
+ * this is also the job output. If the workflow is still running, this
57
+ * is the current state of the job, but it may change depending upon
58
+ * the activities that remain.
59
+ */
14
60
  state(metadata?: boolean): Promise<Record<string, any>>;
61
+ /**
62
+ * Returns the current search state of the workflow. This is
63
+ * different than the job state or individual activity state.
64
+ * Search state represents name/value pairs that were added
65
+ * to the workflow.
66
+ */
15
67
  queryState(fields: string[]): Promise<Record<string, any>>;
68
+ /**
69
+ * Returns the current status of the workflow. This is a semaphore
70
+ * value that represents the current state of the workflow, where
71
+ * 0 is complete and a negative value represents that the flow was
72
+ * interrupted.
73
+ */
16
74
  status(): Promise<number>;
75
+ /**
76
+ * Interrupts a running workflow. Standard Job Completion tasks will
77
+ * run. Subscribers will be notified and the job hash will be expired.
78
+ */
17
79
  interrupt(options?: JobInterruptOptions): Promise<string>;
80
+ /**
81
+ * Waits for the workflow to complete and returns the result. If
82
+ * the workflow response includes an error, this method will rethrow
83
+ * the error, including the stack trace if available.
84
+ * Wrap calls in a try/catch as necessary to avoid unhandled exceptions.
85
+ */
18
86
  result<T>(config?: {
19
87
  state?: boolean;
20
88
  throwOnError?: boolean;