@hotmeshio/hotmesh 0.0.9 → 0.0.10

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.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # HotMesh
2
2
  ![alpha release](https://img.shields.io/badge/release-alpha-yellow)
3
3
 
4
- Elevate Redis from an in-memory data store to a game-changing **service mesh**, delivering *durable* workflows without the overhead of a dedicated control plane. With HotMesh, you can keep your code at the forefront, utilizing [Redis infrastructure](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/faq.md#what-is-hotmesh) you already trust and own.
4
+ Elevate Redis from an in-memory data store to a game-changing [service mesh](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/faq.md#what-is-hotmesh). Turn your unpredictable functions into unbreakable workflows.
5
5
 
6
6
  ## Install
7
7
  [![npm version](https://badge.fury.io/js/%40hotmeshio%2Fhotmesh.svg)](https://badge.fury.io/js/%40hotmeshio%2Fhotmesh)
@@ -11,85 +11,91 @@ npm install @hotmeshio/hotmesh
11
11
  ```
12
12
 
13
13
  ## Design
14
- HotMesh's TypeScript SDK is modeled after Temporal IO's developer-friendly approach. Design and deploy durable workflows using your preferred coding style. Write your functions as you normally would, then use the HotMesh to make them durable. Temporal's [hello-world tutorial](https://github.com/temporalio/samples-typescript/tree/main/hello-world/src), for example, requires few changes beyond importing the HotMesh SDK.
14
+ The HotMesh SDK is designed to keep your code front-and-center. Write functions as you normally would, then use the HotMesh to make them durable.
15
15
 
16
- >Start by defining activities. These are the functions that will be invoked by your workflow. They can be written in any style, using any framework, and can even be legacy functions you've already written. The only requirement is that they return a Promise.
16
+ 1. Start by defining **activities**. Activities are those functions that will be invoked by your workflow. They are commonly used to read and write to databases and invoke external services. They can be written in any style, using any framework, and can even be legacy functions you've already written. The only requirement is that they return a Promise.
17
+ ```javascript
18
+ //activities.ts
17
19
 
18
- **./activities.ts**
19
- ```javascript
20
- export async function greet(name: string): Promise<string> {
21
- return `Hello, ${name}!`;
22
- }
23
- ```
24
-
25
- >Next, define your workflow. Include conditional logic, loops, etc. It's vanilla code written in your own coding style--just make sure to call `proxyActivities` to run your activities durably.
26
-
27
- **./workflows.ts**
28
- ```javascript
29
- import { Durable } from '@hotmeshio/hotmesh';
30
- import * as activities from './activities';
31
-
32
- const { greet } = Durable.workflow
33
- .proxyActivities<typeof activities>({
34
- activities
35
- });
36
-
37
- export async function example(name: string): Promise<string> {
38
- return await greet(name);
39
- }
40
- ```
41
-
42
- >Finally, create a worker and client. The *client* triggers workflows, while the *worker* runs them, retrying as necessary until the workflow succeeds--all without the need for complicated retry logic.
43
-
44
- **./client.ts**
45
- ```javascript
46
- import { Durable } from '@hotmeshio/hotmesh';
47
- import Redis from 'ioredis'; //OR `import * as Redis from 'redis';`
48
- import { v4 as uuidv4 } from 'uuid';
49
-
50
- async function run() {
51
- const connection = await Durable.Connection.connect({
52
- class: Redis,
53
- options: { host: 'localhost', port: 6379 }
54
- });
55
-
56
- const client = new Durable.Client({
57
- connection
58
- });
59
-
60
- const handle = await client.workflow.start({
61
- args: ['HotMesh'],
62
- taskQueue: 'hello-world',
63
- workflowName: 'example',
64
- workflowId: 'workflow-' + uuidv4()
65
- });
66
-
67
- console.log(await handle.result());
68
- }
69
- ```
20
+ export async function greet(name: string): Promise<string> {
21
+ return `Hello, ${name}!`;
22
+ }
70
23
 
71
- **./worker.ts**
72
- ```javascript
73
- import { Durable } from '@hotmeshio/hotmesh';
74
- import Redis from 'ioredis';
75
- import * as workflows from './workflows';
76
-
77
- async function run() {
78
- const connection = await Durable.NativeConnection.connect({
79
- class: Redis,
80
- options: { host: 'localhost', port: 6379 },
81
- });
82
- const worker = await Durable.Worker.create({
83
- connection,
84
- namespace: 'default',
85
- taskQueue: 'hello-world',
86
- workflow: workflows.example,
87
- });
88
- await worker.run();
89
- }
90
- ```
24
+ export async function saludar(nombre: string): Promise<string> {
25
+ return `¡Hola, ${nombre}!`;
26
+ }
27
+ ```
28
+ 2. Define your **workflow** logic. Include conditional branching, loops, etc to control activity execution. It's vanilla code written in your own coding style. The only requirement is to use `proxyActivities`, ensuring your activities are executed with HotMesh's durability guarantee.
29
+ ```javascript
30
+ //workflows.ts
31
+
32
+ import { Durable } from '@hotmeshio/hotmesh';
33
+ import * as activities from './activities';
34
+
35
+ const { greet, saludar } = Durable.workflow
36
+ .proxyActivities<typeof activities>({
37
+ activities
38
+ });
39
+
40
+ export async function example(name: string, lang: string): Promise<string> {
41
+ if (lang === 'es') {
42
+ return await saludar(name);
43
+ } else {
44
+ return await greet(name);
45
+ }
46
+ }
47
+ ```
48
+
49
+ 3. Although you could call your workflow directly (it's just a vanilla function), it's only durable when invoked and orchestrated via HotMesh. By using a HotMesh **client** to send the request, the function is guaranteed to return a result.
50
+ ```javascript
51
+ //client.ts
52
+
53
+ import { Durable } from '@hotmeshio/hotmesh';
54
+ import Redis from 'ioredis'; //OR `import * as Redis from 'redis';`
55
+ import { v4 as uuidv4 } from 'uuid';
56
+
57
+ async function run(): Promise<string> {
58
+ const client = new Durable.Client({
59
+ connection: {
60
+ class: Redis,
61
+ options: { host: 'localhost', port: 6379 }
62
+ }
63
+ });
64
+
65
+ const handle = await client.workflow.start({
66
+ args: ['HotMesh', 'es'],
67
+ taskQueue: 'hello-world',
68
+ workflowName: 'example',
69
+ workflowId: uuidv4()
70
+ });
71
+
72
+ return await handle.result();
73
+ //returns '¡Hola, HotMesh!'
74
+ }
75
+ ```
76
+
77
+ 4. The last step is to create a **worker** that executes the workflow. Workers listen for tasks on their assigned channel, executing their assigned workflow function until it succeeds.
78
+ ```javascript
79
+ //worker.ts
80
+
81
+ import { Durable } from '@hotmeshio/hotmesh';
82
+ import Redis from 'ioredis';
83
+ import * as workflows from './workflows';
84
+
85
+ async function run() {
86
+ const worker = await Durable.Worker.create({
87
+ connection: {
88
+ class: Redis,
89
+ options: { host: 'localhost', port: 6379 },
90
+ },
91
+ taskQueue: 'hello-world',
92
+ workflow: workflows.example,
93
+ });
94
+ await worker.run();
95
+ }
96
+ ```
91
97
 
92
- >HotMesh delivers durable function execution using a [distributed service mesh](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/distributed_orchestration.md). The design delivers durable workflows without the cost and complexity of a centralized service mesh/control plane. Refer to the [hotmeshio/samples-typescript](https://github.com/hotmeshio/samples-typescript) Git Repo for a range of examples, including nested workflows.
98
+ >HotMesh delivers durable workflows without the cost and complexity of a centralized service mesh. Refer to the [samples-typescript](https://github.com/hotmeshio/samples-typescript) Git Repo for a range of examples, including compositional workflows (where one workflow calls another) and remote execution (where calls are brokered across microservices).
93
99
 
94
100
  ## Advanced Design
95
101
  HotMesh's TypeScript SDK is the easiest way to make your functions durable. But if you need full control over your function lifecycles (including high-volume, high-speed use cases), you can use HotMesh's underlying YAML models to optimize your durable workflows. The following model depicts a sequence of activities orchestrated by HotMesh. Any function you associate with a `topic` in your YAML definition is guaranteed to be durable.
@@ -25,7 +25,7 @@
25
25
  * hmsh:<appid>:sym:keys:<activityid|$subscribes> -> {hash} list of symbols based upon schema enums (initially) and adaptively optimized (later) during runtime; if '$subscribes' is used as the activityid, it is a top-level `job` symbol set (for job keys)
26
26
  * hmsh:<appid>:sym:vals: -> {hash} list of symbols for job values across all app versions
27
27
  */
28
- declare const PSNS = "hmsh";
28
+ declare const HMNS = "hmsh";
29
29
  declare enum KeyType {
30
30
  APP = 0,
31
31
  ENGINE_ID = 1,
@@ -72,4 +72,4 @@ declare class KeyService {
72
72
  */
73
73
  static mintKey(namespace: string, keyType: KeyType, params: KeyStoreParams): string;
74
74
  }
75
- export { KeyService, KeyType, KeyStoreParams, PSNS };
75
+ export { KeyService, KeyType, KeyStoreParams, HMNS };
@@ -27,10 +27,10 @@
27
27
  * hmsh:<appid>:sym:vals: -> {hash} list of symbols for job values across all app versions
28
28
  */
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.PSNS = exports.KeyType = exports.KeyService = void 0;
30
+ exports.HMNS = exports.KeyType = exports.KeyService = void 0;
31
31
  //default namespace for hotmesh
32
- const PSNS = "hmsh";
33
- exports.PSNS = PSNS;
32
+ const HMNS = "hmsh";
33
+ exports.HMNS = HMNS;
34
34
  //these are the entity types that are stored in the key/value store
35
35
  var KeyType;
36
36
  (function (KeyType) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "description": "Durable Workflows",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -22,7 +22,6 @@ async function run() {
22
22
  });
23
23
  const worker = await Worker.create({
24
24
  connection,
25
- namespace: 'default',
26
25
  taskQueue: 'hello-world',
27
26
  workflow: workflows.example,
28
27
  activities,
@@ -26,7 +26,6 @@ async function run() {
26
26
  });
27
27
  const worker = await Worker.create({
28
28
  connection,
29
- namespace: 'default',
30
29
  taskQueue: 'hello-world',
31
30
  workflow: workflows.example,
32
31
  activities,
@@ -17,7 +17,7 @@ class HotMeshService {
17
17
  }
18
18
  verifyAndSetNamespace(namespace) {
19
19
  if (!namespace) {
20
- this.namespace = key_1.PSNS;
20
+ this.namespace = key_1.HMNS;
21
21
  }
22
22
  else if (!namespace.match(/^[A-Za-z0-9-]+$/)) {
23
23
  throw new Error(`config.namespace [${namespace}] is invalid`);
@@ -58,7 +58,7 @@ class StoreService {
58
58
  };
59
59
  this.redisClient = redisClient;
60
60
  }
61
- async init(namespace = key_1.PSNS, appId, logger) {
61
+ async init(namespace = key_1.HMNS, appId, logger) {
62
62
  this.namespace = namespace;
63
63
  this.appId = appId;
64
64
  this.logger = logger;
@@ -111,7 +111,7 @@ class StoreService {
111
111
  if (bCreate) {
112
112
  const packageJson = await Promise.resolve().then(() => __importStar(require('../../package.json')));
113
113
  const version = packageJson['version'] || '0.0.0';
114
- settings = { namespace: key_1.PSNS, version };
114
+ settings = { namespace: key_1.HMNS, version };
115
115
  await this.setSettings(settings);
116
116
  return settings;
117
117
  }
@@ -7,7 +7,7 @@ class IORedisStreamService extends index_1.StreamService {
7
7
  constructor(redisClient) {
8
8
  super(redisClient);
9
9
  }
10
- async init(namespace = key_1.PSNS, appId, logger) {
10
+ async init(namespace = key_1.HMNS, appId, logger) {
11
11
  this.namespace = namespace;
12
12
  this.logger = logger;
13
13
  this.appId = appId;
@@ -7,7 +7,7 @@ class RedisStreamService extends index_1.StreamService {
7
7
  constructor(redisClient) {
8
8
  super(redisClient);
9
9
  }
10
- async init(namespace = key_1.PSNS, appId, logger) {
10
+ async init(namespace = key_1.HMNS, appId, logger) {
11
11
  this.namespace = namespace;
12
12
  this.logger = logger;
13
13
  this.appId = appId;
@@ -7,7 +7,7 @@ class IORedisSubService extends index_1.SubService {
7
7
  constructor(redisClient) {
8
8
  super(redisClient);
9
9
  }
10
- async init(namespace = key_1.PSNS, appId, engineId, logger) {
10
+ async init(namespace = key_1.HMNS, appId, engineId, logger) {
11
11
  this.namespace = namespace;
12
12
  this.logger = logger;
13
13
  this.appId = appId;
@@ -7,7 +7,7 @@ class RedisSubService extends index_1.SubService {
7
7
  constructor(redisClient) {
8
8
  super(redisClient);
9
9
  }
10
- async init(namespace = key_1.PSNS, appId, engineId, logger) {
10
+ async init(namespace = key_1.HMNS, appId, engineId, logger) {
11
11
  this.namespace = namespace;
12
12
  this.logger = logger;
13
13
  this.appId = appId;
@@ -32,7 +32,7 @@ type Registry = {
32
32
  };
33
33
  type WorkerConfig = {
34
34
  connection: Connection;
35
- namespace: string;
35
+ namespace?: string;
36
36
  taskQueue: string;
37
37
  workflow: Function;
38
38
  };
package/modules/key.ts CHANGED
@@ -27,7 +27,7 @@
27
27
  */
28
28
 
29
29
  //default namespace for hotmesh
30
- const PSNS = "hmsh";
30
+ const HMNS = "hmsh";
31
31
 
32
32
  //these are the entity types that are stored in the key/value store
33
33
  enum KeyType {
@@ -126,4 +126,4 @@ class KeyService {
126
126
  }
127
127
  }
128
128
 
129
- export { KeyService, KeyType, KeyStoreParams, PSNS };
129
+ export { KeyService, KeyType, KeyStoreParams, HMNS };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "description": "Durable Workflows",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -21,7 +21,6 @@ async function run() {
21
21
  });
22
22
  const worker = await Worker.create({
23
23
  connection,
24
- namespace: 'default',
25
24
  taskQueue: 'hello-world',
26
25
  workflow: workflows.example,
27
26
  activities,
@@ -25,7 +25,6 @@ async function run() {
25
25
  });
26
26
  const worker = await Worker.create({
27
27
  connection,
28
- namespace: 'default',
29
28
  taskQueue: 'hello-world',
30
29
  workflow: workflows.example,
31
30
  activities,
@@ -1,5 +1,5 @@
1
1
  import { v4 as uuidv4 } from 'uuid';
2
- import { PSNS } from '../../modules/key';
2
+ import { HMNS } from '../../modules/key';
3
3
  import { EngineService } from '../engine';
4
4
  import { LoggerService, ILogger } from '../logger';
5
5
  import { StreamSignaler } from '../signaler/stream';
@@ -33,7 +33,7 @@ class HotMeshService {
33
33
 
34
34
  verifyAndSetNamespace(namespace?: string) {
35
35
  if (!namespace) {
36
- this.namespace = PSNS;
36
+ this.namespace = HMNS;
37
37
  } else if (!namespace.match(/^[A-Za-z0-9-]+$/)) {
38
38
  throw new Error(`config.namespace [${namespace}] is invalid`);
39
39
  } else {
@@ -2,7 +2,7 @@ import {
2
2
  KeyService,
3
3
  KeyStoreParams,
4
4
  KeyType,
5
- PSNS} from '../../modules/key';
5
+ HMNS} from '../../modules/key';
6
6
  import { ILogger } from '../logger';
7
7
  import { MDATA_SYMBOLS, SerializerService as Serializer } from '../serializer';
8
8
  import { Cache } from './cache';
@@ -119,7 +119,7 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
119
119
  this.redisClient = redisClient;
120
120
  }
121
121
 
122
- async init(namespace = PSNS, appId: string, logger: ILogger): Promise<HotMeshApps> {
122
+ async init(namespace = HMNS, appId: string, logger: ILogger): Promise<HotMeshApps> {
123
123
  this.namespace = namespace;
124
124
  this.appId = appId;
125
125
  this.logger = logger;
@@ -178,7 +178,7 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
178
178
  if (bCreate) {
179
179
  const packageJson = await import('../../package.json');
180
180
  const version: string = packageJson['version'] || '0.0.0';
181
- settings = { namespace: PSNS, version } as HotMeshSettings;
181
+ settings = { namespace: HMNS, version } as HotMeshSettings;
182
182
  await this.setSettings(settings);
183
183
  return settings;
184
184
  }
@@ -1,4 +1,4 @@
1
- import { KeyService, KeyStoreParams, KeyType, PSNS } from '../../../modules/key';
1
+ import { KeyService, KeyStoreParams, KeyType, HMNS } from '../../../modules/key';
2
2
  import { ILogger } from '../../logger';
3
3
  import { StreamService } from '../index';
4
4
  import { RedisClientType, RedisMultiType } from '../../../types/ioredisclient';
@@ -14,7 +14,7 @@ class IORedisStreamService extends StreamService<RedisClientType, RedisMultiType
14
14
  super(redisClient);
15
15
  }
16
16
 
17
- async init(namespace = PSNS, appId: string, logger: ILogger): Promise<void> {
17
+ async init(namespace = HMNS, appId: string, logger: ILogger): Promise<void> {
18
18
  this.namespace = namespace;
19
19
  this.logger = logger;
20
20
  this.appId = appId;
@@ -1,4 +1,4 @@
1
- import { KeyService, KeyStoreParams, KeyType, PSNS } from '../../../modules/key';
1
+ import { KeyService, KeyStoreParams, KeyType, HMNS } from '../../../modules/key';
2
2
  import { ILogger } from '../../logger';
3
3
  import { StreamService } from '../index';
4
4
  import { RedisClientType, RedisMultiType } from '../../../types/redisclient';
@@ -14,7 +14,7 @@ class RedisStreamService extends StreamService<RedisClientType, RedisMultiType>
14
14
  super(redisClient);
15
15
  }
16
16
 
17
- async init(namespace = PSNS, appId: string, logger: ILogger): Promise<void> {
17
+ async init(namespace = HMNS, appId: string, logger: ILogger): Promise<void> {
18
18
  this.namespace = namespace;
19
19
  this.logger = logger;
20
20
  this.appId = appId;
@@ -1,4 +1,4 @@
1
- import { KeyService, KeyStoreParams, KeyType, PSNS } from '../../../modules/key';
1
+ import { KeyService, KeyStoreParams, KeyType, HMNS } from '../../../modules/key';
2
2
  import { ILogger } from '../../logger';
3
3
  import { SubService } from '../index';
4
4
  import { RedisClientType, RedisMultiType } from '../../../types/ioredisclient';
@@ -14,7 +14,7 @@ class IORedisSubService extends SubService<RedisClientType, RedisMultiType> {
14
14
  super(redisClient);
15
15
  }
16
16
 
17
- async init(namespace = PSNS, appId: string, engineId: string, logger: ILogger): Promise<void> {
17
+ async init(namespace = HMNS, appId: string, engineId: string, logger: ILogger): Promise<void> {
18
18
  this.namespace = namespace;
19
19
  this.logger = logger;
20
20
  this.appId = appId;
@@ -1,4 +1,4 @@
1
- import { KeyService, KeyStoreParams, KeyType, PSNS } from '../../../modules/key';
1
+ import { KeyService, KeyStoreParams, KeyType, HMNS } from '../../../modules/key';
2
2
  import { ILogger } from '../../logger';
3
3
  import { SubService } from '../index';
4
4
  import { RedisClientType, RedisMultiType } from '../../../types/redisclient';
@@ -14,7 +14,7 @@ class RedisSubService extends SubService<RedisClientType, RedisMultiType> {
14
14
  super(redisClient);
15
15
  }
16
16
 
17
- async init(namespace = PSNS, appId: string, engineId: string, logger: ILogger): Promise<void> {
17
+ async init(namespace = HMNS, appId: string, engineId: string, logger: ILogger): Promise<void> {
18
18
  this.namespace = namespace;
19
19
  this.logger = logger;
20
20
  this.appId = appId;
package/types/durable.ts CHANGED
@@ -39,7 +39,7 @@ type Registry = {
39
39
 
40
40
  type WorkerConfig = {
41
41
  connection: Connection;
42
- namespace: string; //`appid` in the YAML (e.g, 'default')
42
+ namespace?: string; //`appid` in the YAML (e.g, 'default')
43
43
  taskQueue: string; //`subscribes` in the YAML (e.g, 'hello-world')
44
44
  workflow: Function //target function to run
45
45
  }