@hotmeshio/hotmesh 0.0.24 → 0.0.26
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 +39 -32
- package/build/modules/utils.d.ts +1 -0
- package/build/modules/utils.js +6 -1
- package/build/package.json +1 -1
- package/build/services/durable/meshos.d.ts +24 -12
- package/build/services/durable/meshos.js +91 -16
- package/build/services/durable/workflow.d.ts +7 -0
- package/build/services/durable/workflow.js +13 -0
- package/build/services/store/clients/redis.js +1 -2
- package/build/services/stream/clients/redis.js +1 -1
- package/build/services/sub/clients/redis.js +1 -1
- package/build/types/durable.d.ts +26 -9
- package/build/types/index.d.ts +1 -1
- package/modules/utils.ts +5 -0
- package/package.json +1 -1
- package/services/durable/meshos.ts +105 -23
- package/services/durable/workflow.ts +14 -0
- package/services/store/clients/redis.ts +1 -2
- package/services/stream/clients/redis.ts +1 -1
- package/services/sub/clients/redis.ts +1 -1
- package/types/durable.ts +32 -6
- package/types/index.ts +4 -1
package/README.md
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
# HotMesh
|
|
2
2
|

|
|
3
3
|
|
|
4
|
-
Elevate Redis from an in-memory data cache
|
|
4
|
+
Elevate Redis from an in-memory data cache, and turn your unpredictable functions into unbreakable workflows.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
HotMesh is a wrapper for Redis that exposes a higher level set of domain constructs like ‘activities’, ‘workflows’, 'jobs', etc. Behind the scenes, it uses *Redis Data* (Hash, ZSet, and List); *Redis Streams* (XReadGroup, XAdd, XLen); and *Redis Publish/Subscribe*.
|
|
6
|
+
**HotMesh** is a wrapper for Redis that exposes a higher level set of domain constructs like ‘activities’, ‘workflows’, 'jobs', etc. Behind the scenes, it uses *Redis Data* (Hash, ZSet, List); *Redis Streams* (XReadGroup, XAdd, XLen, etc); and *Redis Publish/Subscribe*.
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
It's still Redis in the background, but the information flow is fundamentally different. Your functions no longer call Redis (e.g., to cache a document) and instead are called by Redis.
|
|
8
|
+
It's still Redis in the background, but the information flow is reversed. Instead of your functions calling Redis (e.g., for caching a document), Redis governs your function execution. If your microservice container goes down or your function simply fails, HotMesh will restore function state at the point of failure and retry until it succeeds.
|
|
12
9
|
|
|
13
10
|
## Install
|
|
14
11
|
[](https://badge.fury.io/js/%40hotmeshio%2Fhotmesh)
|
|
@@ -20,34 +17,32 @@ npm install @hotmeshio/hotmesh
|
|
|
20
17
|
## Design
|
|
21
18
|
The HotMesh SDK is designed to keep your code front-and-center. Write code as you normally would, then use HotMesh to make it durable.
|
|
22
19
|
|
|
23
|
-
1. Start with any ordinary class. Pay attention to unpredictable functions: those that execute slowly, cause problems at scale, or simply fail to return. *Note how the `
|
|
20
|
+
1. Start with any ordinary class. Pay attention to unpredictable functions: those that execute slowly, cause problems at scale, or simply fail to return. *Note how the `flaky` function throws an error 50% of the time. This is exactly the type of function that can be fixed using HotMesh.*
|
|
24
21
|
```javascript
|
|
25
22
|
//myworkflow.ts
|
|
26
23
|
|
|
27
24
|
export class MyWorkflow {
|
|
28
25
|
|
|
29
|
-
//main method
|
|
30
26
|
async run(name: string): Promise<string> {
|
|
31
|
-
const
|
|
27
|
+
const hi = await this.flaky(name);
|
|
32
28
|
const hello = await this.greet(name);
|
|
33
|
-
return `${
|
|
29
|
+
return `${hi} ${hello}`;
|
|
34
30
|
}
|
|
35
31
|
|
|
36
|
-
//this function
|
|
37
|
-
async
|
|
32
|
+
//this function is unpredictable and will fail 50% of the time
|
|
33
|
+
async flaky(name: string): Promise<string> {
|
|
38
34
|
if (Math.random() < 0.5) {
|
|
39
|
-
throw new Error('
|
|
35
|
+
throw new Error('Ooops!');
|
|
40
36
|
}
|
|
41
|
-
return
|
|
37
|
+
return `Hi, ${name}!`;
|
|
42
38
|
}
|
|
43
39
|
|
|
44
|
-
//this function always succeeds
|
|
45
40
|
async greet(name: string): Promise<string> {
|
|
46
41
|
return `Hello, ${name}!`;
|
|
47
42
|
}
|
|
48
43
|
}
|
|
49
44
|
```
|
|
50
|
-
2. Import `
|
|
45
|
+
2. Import `Redis` and `MeshOS` and configure host, port, etc. List those functions that Redis should govern as durable workflows (like `run` and `flaky`). And that's it! *Your functions don't actually change; rather, their governance does.*
|
|
51
46
|
```javascript
|
|
52
47
|
//myworkflow.ts
|
|
53
48
|
|
|
@@ -63,40 +58,52 @@ The HotMesh SDK is designed to keep your code front-and-center. Write code as yo
|
|
|
63
58
|
workflowFunctions = ['run'];
|
|
64
59
|
|
|
65
60
|
//list functions to retry and cache
|
|
66
|
-
proxyFunctions = ['
|
|
61
|
+
proxyFunctions = ['flaky'];
|
|
67
62
|
|
|
68
|
-
//main method (Redis will now govern its execution)
|
|
69
63
|
async run(name: string): Promise<string> {
|
|
70
|
-
const
|
|
64
|
+
const hi = await this.flaky(name);
|
|
71
65
|
const hello = await this.greet(name);
|
|
72
|
-
return `${
|
|
66
|
+
return `${hi} ${hello}`;
|
|
73
67
|
}
|
|
74
68
|
|
|
75
|
-
//this function
|
|
76
|
-
async
|
|
69
|
+
//this function is now durable and will be retried until it succeeds!
|
|
70
|
+
async flaky(name: string): Promise<string> {
|
|
77
71
|
if (Math.random() < 0.5) {
|
|
78
|
-
throw new Error('
|
|
72
|
+
throw new Error('Ooops!');
|
|
79
73
|
}
|
|
80
|
-
return
|
|
74
|
+
return `Hi, ${name}!`;
|
|
81
75
|
}
|
|
82
76
|
|
|
83
|
-
//this vanilla function is unchanged
|
|
84
77
|
async greet(name: string): Promise<string> {
|
|
85
78
|
return `Hello, ${name}!`;
|
|
86
79
|
}
|
|
87
80
|
}
|
|
88
81
|
```
|
|
89
|
-
3. Invoke your class, providing a unique
|
|
82
|
+
3. Invoke your class, providing a unique id (it's now an idempotent workflow and needs a GUID). Nothing changes from the outside, *but Redis now governs the end-to-end execution.* It's guaranteed to succeed, even if it takes a while.
|
|
90
83
|
```javascript
|
|
91
84
|
//mycaller.ts
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const response = await workflow.run('John');
|
|
97
|
-
//Hello, John! ¡Hola, John!
|
|
86
|
+
const workflow = new MyWorkflow({ id: 'my123', await: true });
|
|
87
|
+
const response = await workflow.run('World');
|
|
88
|
+
//Hi, World! Hello, World!
|
|
98
89
|
```
|
|
99
90
|
|
|
91
|
+
Redis governance delivers more than just reliability. Externalizing state fundamentally changes the execution profile for your functions, allowing you to design long-running, durable workflows. Use the following methods to solve the most common state management challenges.
|
|
92
|
+
|
|
93
|
+
- `waitForSignal` | Pause and wait for external event(s) before continuing. The *waitForSignal* method will collate and cache the signals and only awaken your function once they've all arrived.
|
|
94
|
+
- `signal` | Send a signal (and optional payload) to any paused function.
|
|
95
|
+
- `hook` | Redis governance supercharges your functions, transforming them into 're-entrant processes'. Optionally use the *hook* method to spawn parallel execution threads within any running function.
|
|
96
|
+
- `sleep` | Pause function execution for a ridiculous amount of time (months, years, etc). There's no risk of information loss, as Redis governs function state. When your function awakens, function state is efficiently (and automatically) restored.
|
|
97
|
+
- `random` | Generate a deterministic random number that can be used in a reentrant process workflow (replaces `Math.random()`).
|
|
98
|
+
- `executeChild` | Call another durable function and await the response. *Design sophisticated, multi-process solutions by leveraging this command.*
|
|
99
|
+
- `startChild` | Call another durable function, but do not await the response.
|
|
100
|
+
- `set` | Set a value (e.g, `set('name', 'value')`)
|
|
101
|
+
- `get` | Get a value (e.g, `get('name')`)
|
|
102
|
+
- `incr` | Increment (or decrement) a number (e.g, `incr('name', -99)`)
|
|
103
|
+
- `mult` | Multiply (or divide) a number (e.g, `mult('name', 12)`)
|
|
104
|
+
|
|
105
|
+
Refer to the [hotmeshio/samples-typescript](https://github.com/hotmeshio/samples-typescript) repo for usage examples.
|
|
106
|
+
|
|
100
107
|
## Advanced Design
|
|
101
108
|
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.
|
|
102
109
|
|
|
@@ -232,4 +239,4 @@ HotMesh is a distributed orchestration engine. Refer to the [Distributed Orchest
|
|
|
232
239
|
Gain insight into HotMesh's monitoring, exception handling, and alarm configurations via the [System Lifecycle Guide](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/system_lifecycle.md).
|
|
233
240
|
|
|
234
241
|
## Alpha Release
|
|
235
|
-
So what exacty is an [alpha release](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/alpha.md)
|
|
242
|
+
So what exacty is an [alpha release](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/alpha.md)?
|
package/build/modules/utils.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { RedisClient, RedisMulti } from "../types/redis";
|
|
|
5
5
|
import { StringAnyType } from "../types/serializer";
|
|
6
6
|
import { StreamCode, StreamStatus } from "../types/stream";
|
|
7
7
|
export declare function sleepFor(ms: number): Promise<unknown>;
|
|
8
|
+
export declare function deterministicRandom(seed: number): number;
|
|
8
9
|
export declare function identifyRedisType(redisInstance: any): 'redis' | 'ioredis' | null;
|
|
9
10
|
export declare const polyfill: {
|
|
10
11
|
resolveActivityType(activityType: string): string;
|
package/build/modules/utils.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.restoreHierarchy = exports.getValueByPath = exports.getIndexedHash = exports.getSymVal = exports.getSymKey = exports.formatISODate = exports.getTimeSeries = exports.getSubscriptionTopic = exports.findSubscriptionForTrigger = exports.findTopKey = exports.XSleepFor = exports.matchesStatus = exports.matchesStatusCode = exports.identifyRedisTypeFromClass = exports.polyfill = exports.identifyRedisType = exports.sleepFor = void 0;
|
|
3
|
+
exports.restoreHierarchy = exports.getValueByPath = exports.getIndexedHash = exports.getSymVal = exports.getSymKey = exports.formatISODate = exports.getTimeSeries = exports.getSubscriptionTopic = exports.findSubscriptionForTrigger = exports.findTopKey = exports.XSleepFor = exports.matchesStatus = exports.matchesStatusCode = exports.identifyRedisTypeFromClass = exports.polyfill = exports.identifyRedisType = exports.deterministicRandom = exports.sleepFor = void 0;
|
|
4
4
|
async function sleepFor(ms) {
|
|
5
5
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6
6
|
}
|
|
7
7
|
exports.sleepFor = sleepFor;
|
|
8
|
+
function deterministicRandom(seed) {
|
|
9
|
+
let x = Math.sin(seed) * 10000;
|
|
10
|
+
return x - Math.floor(x);
|
|
11
|
+
}
|
|
12
|
+
exports.deterministicRandom = deterministicRandom;
|
|
8
13
|
function identifyRedisType(redisInstance) {
|
|
9
14
|
const prototype = Object.getPrototypeOf(redisInstance);
|
|
10
15
|
if ('defineCommand' in prototype || Object.keys(prototype).includes('multi')) {
|
package/build/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { WorkflowHandleService } from './handle';
|
|
2
|
-
import { FindOptions, MeshOSActivityOptions, MeshOSOptions, WorkflowSearchOptions } from '../../types/durable';
|
|
3
|
-
import { RedisOptions, RedisClass } from '../../types/redis';
|
|
4
|
-
import { StringAnyType } from '../../types';
|
|
5
2
|
import { WorkflowService } from './workflow';
|
|
3
|
+
import { FindOptions, FindWhereOptions, FindWhereQuery, MeshOSActivityOptions, MeshOSConfig, MeshOSOptions, MeshOSWorkerOptions, WorkflowSearchOptions } from '../../types/durable';
|
|
4
|
+
import { RedisOptions, RedisClass } from '../../types/redis';
|
|
5
|
+
import { StringAnyType } from '../../types/serializer';
|
|
6
6
|
/**
|
|
7
7
|
* The base class for running MeshOS workflows.
|
|
8
|
-
* Extend
|
|
9
|
-
* execute as durable workflows
|
|
8
|
+
* Extend this class, add your Redis config, and add functions to
|
|
9
|
+
* execute as durable `hooks`, `workflows`, and `activities`.
|
|
10
10
|
*/
|
|
11
11
|
export declare class MeshOSService {
|
|
12
12
|
/**
|
|
@@ -78,14 +78,18 @@ export declare class MeshOSService {
|
|
|
78
78
|
*/
|
|
79
79
|
static createIndex(): Promise<void>;
|
|
80
80
|
/**
|
|
81
|
-
*
|
|
81
|
+
* stop the workers
|
|
82
|
+
* @returns {Promise<void>}
|
|
83
|
+
*/
|
|
84
|
+
static stopWorkers(): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Initializes the worker(s). This is a static
|
|
82
87
|
* method that allows for optional task Queue targeting.
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* @param {
|
|
86
|
-
* @param {string[]} allowList
|
|
88
|
+
* An `allowList` may be optionally provided to start
|
|
89
|
+
* specific `worker` and `hook` methods.
|
|
90
|
+
* @param {MeshOSWorkerOptions} [options]
|
|
87
91
|
*/
|
|
88
|
-
static startWorkers(
|
|
92
|
+
static startWorkers(options?: MeshOSWorkerOptions): Promise<void>;
|
|
89
93
|
/**
|
|
90
94
|
* executes the redis FT search query
|
|
91
95
|
* @example '@_quantity:[89 89]'
|
|
@@ -93,6 +97,14 @@ export declare class MeshOSService {
|
|
|
93
97
|
* @returns {string}
|
|
94
98
|
*/
|
|
95
99
|
static find(options: FindOptions, ...args: string[]): Promise<string[] | [number]>;
|
|
100
|
+
/**
|
|
101
|
+
* Provides a JSON abstraction for the Redis FT.search command
|
|
102
|
+
* (e.g, `count`, `query`, `return`, `limit`)
|
|
103
|
+
* @param {FindWhereOptions} options
|
|
104
|
+
* @returns {Promise<string[] | [number]>}
|
|
105
|
+
*/
|
|
106
|
+
static findWhere(options: FindWhereOptions): Promise<string[] | [number]>;
|
|
107
|
+
static generateSearchQuery(query: FindWhereQuery[]): string;
|
|
96
108
|
/**
|
|
97
109
|
* returns the workflow handle. The handle can then be
|
|
98
110
|
* used to query for status, state, custom state, etc.
|
|
@@ -104,5 +116,5 @@ export declare class MeshOSService {
|
|
|
104
116
|
* Optionally include a target taskQueue to exec the
|
|
105
117
|
* workflow's call on a specific worker queue.
|
|
106
118
|
*/
|
|
107
|
-
constructor(id?: string, options?:
|
|
119
|
+
constructor(id?: string | MeshOSConfig, options?: MeshOSConfig);
|
|
108
120
|
}
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MeshOSService = void 0;
|
|
4
4
|
const nanoid_1 = require("nanoid");
|
|
5
|
+
const _1 = require(".");
|
|
6
|
+
const asyncLocalStorage_1 = require("./asyncLocalStorage");
|
|
5
7
|
const client_1 = require("./client");
|
|
6
8
|
const search_1 = require("./search");
|
|
7
9
|
const worker_1 = require("./worker");
|
|
8
|
-
const _1 = require(".");
|
|
9
|
-
const asyncLocalStorage_1 = require("./asyncLocalStorage");
|
|
10
10
|
const workflow_1 = require("./workflow");
|
|
11
|
+
const stream_1 = require("../signaler/stream");
|
|
11
12
|
/**
|
|
12
13
|
* The base class for running MeshOS workflows.
|
|
13
|
-
* Extend
|
|
14
|
-
* execute as durable workflows
|
|
14
|
+
* Extend this class, add your Redis config, and add functions to
|
|
15
|
+
* execute as durable `hooks`, `workflows`, and `activities`.
|
|
15
16
|
*/
|
|
16
17
|
class MeshOSService {
|
|
17
18
|
static async getHotMeshClient(redisClass, redisOptions, namespace, taskQueue) {
|
|
@@ -42,14 +43,24 @@ class MeshOSService {
|
|
|
42
43
|
search_1.Search.configureSearchIndex(hmClient, my.search);
|
|
43
44
|
}
|
|
44
45
|
/**
|
|
45
|
-
*
|
|
46
|
+
* stop the workers
|
|
47
|
+
* @returns {Promise<void>}
|
|
48
|
+
*/
|
|
49
|
+
static async stopWorkers() {
|
|
50
|
+
await _1.Durable.Client.shutdown();
|
|
51
|
+
await _1.Durable.Worker.shutdown();
|
|
52
|
+
await stream_1.StreamSignaler.stopConsuming();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Initializes the worker(s). This is a static
|
|
46
56
|
* method that allows for optional task Queue targeting.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* @param {
|
|
50
|
-
* @param {string[]} allowList
|
|
57
|
+
* An `allowList` may be optionally provided to start
|
|
58
|
+
* specific `worker` and `hook` methods.
|
|
59
|
+
* @param {MeshOSWorkerOptions} [options]
|
|
51
60
|
*/
|
|
52
|
-
static async startWorkers(
|
|
61
|
+
static async startWorkers(options) {
|
|
62
|
+
const taskQueue = options && options.taskQueue;
|
|
63
|
+
const allowList = options && options.allowList || [];
|
|
53
64
|
const my = new this();
|
|
54
65
|
//helper functions
|
|
55
66
|
const resolveFunctionNames = (arr) => arr.map(item => typeof item === 'string' ? item : item.name);
|
|
@@ -74,8 +85,6 @@ class MeshOSService {
|
|
|
74
85
|
const proxiedActivities = _1.Durable.workflow.proxyActivities({
|
|
75
86
|
activities: proxyActivities
|
|
76
87
|
});
|
|
77
|
-
//WATCH!: unsure if this will pollute the scope; don't think
|
|
78
|
-
// so as activity functions are terminal in the chain.
|
|
79
88
|
Object.assign(my, proxiedActivities);
|
|
80
89
|
}
|
|
81
90
|
const functionsToIterate = allowList.length ? resolveFunctionNames(allowList) : resolveFunctionNames([...my.workflowFunctions, ...my.hookFunctions]);
|
|
@@ -142,9 +151,8 @@ class MeshOSService {
|
|
|
142
151
|
class: my.redisClass,
|
|
143
152
|
options: my.redisOptions
|
|
144
153
|
} });
|
|
145
|
-
//workflow name is the function name driving the workflow
|
|
146
154
|
let workflowName;
|
|
147
|
-
if (options
|
|
155
|
+
if (options.workflowName) {
|
|
148
156
|
workflowName = options?.workflowName;
|
|
149
157
|
}
|
|
150
158
|
else if (my.workflowFunctions?.length) {
|
|
@@ -156,7 +164,66 @@ class MeshOSService {
|
|
|
156
164
|
workflowName = target.name;
|
|
157
165
|
}
|
|
158
166
|
}
|
|
159
|
-
return await client.workflow.search(options
|
|
167
|
+
return await client.workflow.search(options.taskQueue ?? my.taskQueue, workflowName, options.namespace ?? my.namespace, options.index ?? my.search.index, ...args); //[count, [id, fields[]], [id, fields[]], [id, fields[]], ...]]
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Provides a JSON abstraction for the Redis FT.search command
|
|
171
|
+
* (e.g, `count`, `query`, `return`, `limit`)
|
|
172
|
+
* @param {FindWhereOptions} options
|
|
173
|
+
* @returns {Promise<string[] | [number]>}
|
|
174
|
+
*/
|
|
175
|
+
static async findWhere(options) {
|
|
176
|
+
const args = [this.generateSearchQuery(options.query)];
|
|
177
|
+
if (options.count) {
|
|
178
|
+
args.push('LIMIT', '0', '0');
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
//limit which hash fields to return
|
|
182
|
+
if (options.return?.length) {
|
|
183
|
+
args.push('RETURN');
|
|
184
|
+
args.push(options.return.length.toString());
|
|
185
|
+
options.return.forEach(returnField => {
|
|
186
|
+
args.push(`_${returnField}`);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
//paginate
|
|
190
|
+
if (options.limit) {
|
|
191
|
+
args.push('LIMIT', options.limit.start.toString(), options.limit.size.toString());
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return await this.find(options.options ?? {}, ...args);
|
|
195
|
+
}
|
|
196
|
+
static generateSearchQuery(query) {
|
|
197
|
+
const my = new this();
|
|
198
|
+
let queryString = query.map(q => {
|
|
199
|
+
const { field, is, value, type } = q;
|
|
200
|
+
const prefixedFieldName = my.search?.schema && field in my.search.schema ? `@_${field}` : `@${field}`;
|
|
201
|
+
const fieldType = my.search?.schema[field]?.type ?? type ?? 'TEXT';
|
|
202
|
+
switch (fieldType) {
|
|
203
|
+
case 'TAG':
|
|
204
|
+
return `${prefixedFieldName}:{${value}}`;
|
|
205
|
+
case 'TEXT':
|
|
206
|
+
return `${prefixedFieldName}:"${value}"`;
|
|
207
|
+
case 'NUMERIC':
|
|
208
|
+
let range = '';
|
|
209
|
+
if (is.startsWith('=')) {
|
|
210
|
+
range = `[${value} ${value}]`;
|
|
211
|
+
}
|
|
212
|
+
else if (is === '<') {
|
|
213
|
+
range = `[-inf ${value}]`;
|
|
214
|
+
}
|
|
215
|
+
else if (is === '>') {
|
|
216
|
+
range = `[${value} +inf]`;
|
|
217
|
+
}
|
|
218
|
+
else if (is === '[]') {
|
|
219
|
+
range = `[${value[0]} ${value[1]}]`;
|
|
220
|
+
}
|
|
221
|
+
return `${prefixedFieldName}:${range}`;
|
|
222
|
+
default:
|
|
223
|
+
return '';
|
|
224
|
+
}
|
|
225
|
+
}).join(' ');
|
|
226
|
+
return queryString;
|
|
160
227
|
}
|
|
161
228
|
/**
|
|
162
229
|
* returns the workflow handle. The handle can then be
|
|
@@ -224,7 +291,15 @@ class MeshOSService {
|
|
|
224
291
|
password: '',
|
|
225
292
|
db: 0,
|
|
226
293
|
};
|
|
227
|
-
|
|
294
|
+
if (typeof (id) === 'string') {
|
|
295
|
+
this.id = id;
|
|
296
|
+
}
|
|
297
|
+
else if (id?.id) {
|
|
298
|
+
this.id = id.id;
|
|
299
|
+
options = id;
|
|
300
|
+
id = undefined;
|
|
301
|
+
}
|
|
302
|
+
;
|
|
228
303
|
if (options?.taskQueue) {
|
|
229
304
|
this.taskQueue = options.taskQueue;
|
|
230
305
|
}
|
|
@@ -28,6 +28,13 @@ export declare class WorkflowService {
|
|
|
28
28
|
* process state and job state)
|
|
29
29
|
*/
|
|
30
30
|
static isSideEffectAllowed(hotMeshClient: HotMesh, prefix: string): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* returns a random number between 0 and 1. This number is deterministic
|
|
33
|
+
* and will never vary for a given seed. This is useful for randomizing
|
|
34
|
+
* pathways in a workflow that can be safely replayed.
|
|
35
|
+
* @returns {number}
|
|
36
|
+
*/
|
|
37
|
+
static random(): number;
|
|
31
38
|
/**
|
|
32
39
|
* send signal data into any other paused thread (which is paused and
|
|
33
40
|
* awaiting the signal) from within a hook-thread or the main-thread
|
|
@@ -14,6 +14,7 @@ const factory_1 = require("./factory");
|
|
|
14
14
|
const search_1 = require("./search");
|
|
15
15
|
const worker_1 = require("./worker");
|
|
16
16
|
const stream_1 = require("../../types/stream");
|
|
17
|
+
const utils_1 = require("../../modules/utils");
|
|
17
18
|
class WorkflowService {
|
|
18
19
|
/**
|
|
19
20
|
* Spawn a child workflow. await and return the result.
|
|
@@ -151,6 +152,18 @@ class WorkflowService {
|
|
|
151
152
|
const guidValue = Number(await hotMeshClient.engine.store.exec('HINCRBYFLOAT', workflowGuid, sessionId, '1'));
|
|
152
153
|
return guidValue === 1;
|
|
153
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* returns a random number between 0 and 1. This number is deterministic
|
|
157
|
+
* and will never vary for a given seed. This is useful for randomizing
|
|
158
|
+
* pathways in a workflow that can be safely replayed.
|
|
159
|
+
* @returns {number}
|
|
160
|
+
*/
|
|
161
|
+
static random() {
|
|
162
|
+
const store = asyncLocalStorage_1.asyncLocalStorage.getStore();
|
|
163
|
+
const COUNTER = store.get('counter');
|
|
164
|
+
const seed = COUNTER.counter = COUNTER.counter + 1;
|
|
165
|
+
return (0, utils_1.deterministicRandom)(seed);
|
|
166
|
+
}
|
|
154
167
|
/**
|
|
155
168
|
* send signal data into any other paused thread (which is paused and
|
|
156
169
|
* awaiting the signal) from within a hook-thread or the main-thread
|
|
@@ -33,8 +33,7 @@ class RedisStoreService extends index_1.StoreService {
|
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
getMulti() {
|
|
36
|
-
|
|
37
|
-
return multi;
|
|
36
|
+
return this.redisClient.multi();
|
|
38
37
|
}
|
|
39
38
|
async exec(...args) {
|
|
40
39
|
return await this.redisClient.sendCommand(args);
|
package/build/types/durable.d.ts
CHANGED
|
@@ -59,15 +59,9 @@ type MeshOSClassConfig = {
|
|
|
59
59
|
redisClass: RedisClass;
|
|
60
60
|
};
|
|
61
61
|
type MeshOSConfig = {
|
|
62
|
+
id?: string;
|
|
63
|
+
await?: boolean;
|
|
62
64
|
taskQueue?: string;
|
|
63
|
-
index?: {
|
|
64
|
-
index: string;
|
|
65
|
-
prefix: string[];
|
|
66
|
-
schema: Record<string, {
|
|
67
|
-
type: 'TEXT' | 'NUMERIC' | 'TAG';
|
|
68
|
-
sortable: boolean;
|
|
69
|
-
}>;
|
|
70
|
-
};
|
|
71
65
|
};
|
|
72
66
|
type ConnectionConfig = {
|
|
73
67
|
class: RedisClass;
|
|
@@ -88,12 +82,28 @@ type WorkerConfig = {
|
|
|
88
82
|
options?: WorkerOptions;
|
|
89
83
|
search?: WorkflowSearchOptions;
|
|
90
84
|
};
|
|
85
|
+
type FindWhereQuery = {
|
|
86
|
+
field: string;
|
|
87
|
+
is: string;
|
|
88
|
+
value: string | boolean | number | [number, number];
|
|
89
|
+
type?: string;
|
|
90
|
+
};
|
|
91
91
|
type FindOptions = {
|
|
92
92
|
workflowName?: string;
|
|
93
93
|
taskQueue?: string;
|
|
94
94
|
namespace?: string;
|
|
95
95
|
index?: string;
|
|
96
96
|
};
|
|
97
|
+
type FindWhereOptions = {
|
|
98
|
+
options?: FindOptions;
|
|
99
|
+
count?: boolean;
|
|
100
|
+
query: FindWhereQuery[];
|
|
101
|
+
return?: string[];
|
|
102
|
+
limit?: {
|
|
103
|
+
start: number;
|
|
104
|
+
size: number;
|
|
105
|
+
};
|
|
106
|
+
};
|
|
97
107
|
type MeshOSOptions = {
|
|
98
108
|
name: string;
|
|
99
109
|
options: WorkerOptions;
|
|
@@ -102,6 +112,13 @@ type MeshOSActivityOptions = {
|
|
|
102
112
|
name: string;
|
|
103
113
|
options: ActivityConfig;
|
|
104
114
|
};
|
|
115
|
+
type MeshOSWorkerOptions = {
|
|
116
|
+
taskQueue?: string;
|
|
117
|
+
allowList?: Array<MeshOSOptions | string>;
|
|
118
|
+
logLevel?: string;
|
|
119
|
+
maxSystemRetries?: number;
|
|
120
|
+
backoffCoefficient?: number;
|
|
121
|
+
};
|
|
105
122
|
type WorkerOptions = {
|
|
106
123
|
logLevel?: string;
|
|
107
124
|
maxSystemRetries?: number;
|
|
@@ -125,4 +142,4 @@ type ActivityConfig = {
|
|
|
125
142
|
maximumInterval: string;
|
|
126
143
|
};
|
|
127
144
|
};
|
|
128
|
-
export { ActivityConfig, ActivityWorkflowDataType, ClientConfig, ContextType, ConnectionConfig, Connection, ProxyType, Registry, SignalOptions, FindOptions, HookOptions, MeshOSActivityOptions, MeshOSClassConfig, MeshOSConfig, MeshOSOptions, WorkerConfig, WorkflowConfig, WorkerOptions, WorkflowSearchOptions, WorkflowDataType, WorkflowOptions, };
|
|
145
|
+
export { ActivityConfig, ActivityWorkflowDataType, ClientConfig, ContextType, ConnectionConfig, Connection, ProxyType, Registry, SignalOptions, FindOptions, FindWhereOptions, FindWhereQuery, HookOptions, MeshOSActivityOptions, MeshOSWorkerOptions, MeshOSClassConfig, MeshOSConfig, MeshOSOptions, WorkerConfig, WorkflowConfig, WorkerOptions, WorkflowSearchOptions, WorkflowDataType, WorkflowOptions, };
|
package/build/types/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { App, AppVID, AppTransitions, AppSubscriptions } from './app';
|
|
|
3
3
|
export { AsyncSignal } from './async';
|
|
4
4
|
export { CacheMode } from './cache';
|
|
5
5
|
export { CollationFaultType, CollationStage } from './collator';
|
|
6
|
-
export { ActivityConfig, ActivityWorkflowDataType, ClientConfig, ContextType, ConnectionConfig, Connection, ProxyType, Registry, SignalOptions, FindOptions, HookOptions, MeshOSActivityOptions, MeshOSClassConfig, MeshOSConfig, MeshOSOptions, WorkflowConfig, WorkerConfig, WorkerOptions, WorkflowSearchOptions, WorkflowDataType, WorkflowOptions, } from './durable';
|
|
6
|
+
export { ActivityConfig, ActivityWorkflowDataType, ClientConfig, ContextType, ConnectionConfig, Connection, ProxyType, Registry, SignalOptions, FindOptions, FindWhereOptions, FindWhereQuery, HookOptions, MeshOSActivityOptions, MeshOSWorkerOptions, MeshOSClassConfig, MeshOSConfig, MeshOSOptions, WorkflowConfig, WorkerConfig, WorkerOptions, WorkflowSearchOptions, WorkflowDataType, WorkflowOptions, } from './durable';
|
|
7
7
|
export { HookCondition, HookConditions, HookGate, HookInterface, HookRule, HookRules, HookSignal } from './hook';
|
|
8
8
|
export { RedisClientType as IORedisClientType, RedisMultiType as IORedisMultiType } from './ioredisclient';
|
|
9
9
|
export { ILogger } from './logger';
|
package/modules/utils.ts
CHANGED
|
@@ -8,6 +8,11 @@ export async function sleepFor(ms: number) {
|
|
|
8
8
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
export function deterministicRandom(seed: number): number {
|
|
12
|
+
let x = Math.sin(seed) * 10000;
|
|
13
|
+
return x - Math.floor(x);
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
export function identifyRedisType(redisInstance: any): 'redis' | 'ioredis' | null {
|
|
12
17
|
const prototype = Object.getPrototypeOf(redisInstance);
|
|
13
18
|
if ('defineCommand' in prototype || Object.keys(prototype).includes('multi')) {
|
package/package.json
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
import { nanoid } from 'nanoid';
|
|
2
2
|
|
|
3
|
+
import { Durable } from '.';
|
|
4
|
+
import { asyncLocalStorage } from './asyncLocalStorage';
|
|
3
5
|
import { ClientService as Client } from './client';
|
|
4
6
|
import { WorkflowHandleService } from './handle';
|
|
5
7
|
import { Search } from './search';
|
|
6
8
|
import { WorkerService as Worker } from './worker';
|
|
7
|
-
import { FindOptions, MeshOSActivityOptions, MeshOSOptions, WorkflowSearchOptions } from '../../types/durable';
|
|
8
|
-
import { RedisOptions, RedisClass } from '../../types/redis';
|
|
9
|
-
import { StringAnyType } from '../../types';
|
|
10
|
-
import { Durable } from '.';
|
|
11
|
-
import { asyncLocalStorage } from './asyncLocalStorage';
|
|
12
9
|
import { WorkflowService } from './workflow';
|
|
10
|
+
import { StreamSignaler } from '../signaler/stream';
|
|
11
|
+
import {
|
|
12
|
+
FindOptions,
|
|
13
|
+
FindWhereOptions,
|
|
14
|
+
FindWhereQuery,
|
|
15
|
+
MeshOSActivityOptions,
|
|
16
|
+
MeshOSConfig,
|
|
17
|
+
MeshOSOptions,
|
|
18
|
+
MeshOSWorkerOptions,
|
|
19
|
+
WorkflowSearchOptions } from '../../types/durable';
|
|
20
|
+
import { RedisOptions, RedisClass } from '../../types/redis';
|
|
21
|
+
import { StringAnyType } from '../../types/serializer';
|
|
13
22
|
|
|
14
23
|
/**
|
|
15
24
|
* The base class for running MeshOS workflows.
|
|
16
|
-
* Extend
|
|
17
|
-
* execute as durable workflows
|
|
25
|
+
* Extend this class, add your Redis config, and add functions to
|
|
26
|
+
* execute as durable `hooks`, `workflows`, and `activities`.
|
|
18
27
|
*/
|
|
19
28
|
|
|
20
29
|
export class MeshOSService {
|
|
@@ -122,14 +131,25 @@ export class MeshOSService {
|
|
|
122
131
|
}
|
|
123
132
|
|
|
124
133
|
/**
|
|
125
|
-
*
|
|
134
|
+
* stop the workers
|
|
135
|
+
* @returns {Promise<void>}
|
|
136
|
+
*/
|
|
137
|
+
static async stopWorkers(): Promise<void> {
|
|
138
|
+
await Durable.Client.shutdown();
|
|
139
|
+
await Durable.Worker.shutdown();
|
|
140
|
+
await StreamSignaler.stopConsuming();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Initializes the worker(s). This is a static
|
|
126
145
|
* method that allows for optional task Queue targeting.
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* @param {
|
|
130
|
-
* @param {string[]} allowList
|
|
146
|
+
* An `allowList` may be optionally provided to start
|
|
147
|
+
* specific `worker` and `hook` methods.
|
|
148
|
+
* @param {MeshOSWorkerOptions} [options]
|
|
131
149
|
*/
|
|
132
|
-
static async startWorkers(
|
|
150
|
+
static async startWorkers(options?: MeshOSWorkerOptions) {
|
|
151
|
+
const taskQueue = options && options.taskQueue;
|
|
152
|
+
const allowList = options && options.allowList || [];
|
|
133
153
|
const my = new this();
|
|
134
154
|
|
|
135
155
|
//helper functions
|
|
@@ -156,8 +176,6 @@ export class MeshOSService {
|
|
|
156
176
|
const proxiedActivities = Durable.workflow.proxyActivities({
|
|
157
177
|
activities: proxyActivities
|
|
158
178
|
});
|
|
159
|
-
//WATCH!: unsure if this will pollute the scope; don't think
|
|
160
|
-
// so as activity functions are terminal in the chain.
|
|
161
179
|
Object.assign(my, proxiedActivities);
|
|
162
180
|
}
|
|
163
181
|
|
|
@@ -229,9 +247,9 @@ export class MeshOSService {
|
|
|
229
247
|
class: my.redisClass,
|
|
230
248
|
options: my.redisOptions
|
|
231
249
|
}});
|
|
232
|
-
|
|
250
|
+
|
|
233
251
|
let workflowName: string;
|
|
234
|
-
if (options
|
|
252
|
+
if (options.workflowName) {
|
|
235
253
|
workflowName = options?.workflowName
|
|
236
254
|
} else if(my.workflowFunctions?.length) {
|
|
237
255
|
let target = my.workflowFunctions[0];
|
|
@@ -242,12 +260,70 @@ export class MeshOSService {
|
|
|
242
260
|
}
|
|
243
261
|
}
|
|
244
262
|
return await client.workflow.search(
|
|
245
|
-
options
|
|
263
|
+
options.taskQueue ?? my.taskQueue,
|
|
246
264
|
workflowName,
|
|
247
|
-
my.namespace,
|
|
248
|
-
my.search.index,
|
|
265
|
+
options.namespace ?? my.namespace,
|
|
266
|
+
options.index ?? my.search.index,
|
|
249
267
|
...args,
|
|
250
|
-
); //[count, [id, fields[], id, fields[], id, fields[], ...]]
|
|
268
|
+
); //[count, [id, fields[]], [id, fields[]], [id, fields[]], ...]]
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Provides a JSON abstraction for the Redis FT.search command
|
|
273
|
+
* (e.g, `count`, `query`, `return`, `limit`)
|
|
274
|
+
* @param {FindWhereOptions} options
|
|
275
|
+
* @returns {Promise<string[] | [number]>}
|
|
276
|
+
*/
|
|
277
|
+
static async findWhere(options: FindWhereOptions): Promise<string[] | [number]> {
|
|
278
|
+
const args: string[] = [this.generateSearchQuery(options.query)];
|
|
279
|
+
if (options.count) {
|
|
280
|
+
args.push('LIMIT', '0', '0');
|
|
281
|
+
} else {
|
|
282
|
+
//limit which hash fields to return
|
|
283
|
+
if (options.return?.length) {
|
|
284
|
+
args.push('RETURN');
|
|
285
|
+
args.push(options.return.length.toString());
|
|
286
|
+
options.return.forEach(returnField => {
|
|
287
|
+
args.push(`_${returnField}`);
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
//paginate
|
|
291
|
+
if (options.limit) {
|
|
292
|
+
args.push('LIMIT', options.limit.start.toString(), options.limit.size.toString());
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return await this.find(options.options ?? {}, ...args);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
static generateSearchQuery(query: FindWhereQuery[]) {
|
|
299
|
+
const my = new this();
|
|
300
|
+
let queryString = query.map(q => {
|
|
301
|
+
const { field, is, value, type } = q;
|
|
302
|
+
const prefixedFieldName = my.search?.schema && field in my.search.schema ? `@_${field}` : `@${field}`;
|
|
303
|
+
const fieldType = my.search?.schema[field]?.type ?? type ?? 'TEXT';
|
|
304
|
+
|
|
305
|
+
switch (fieldType) {
|
|
306
|
+
case 'TAG':
|
|
307
|
+
return `${prefixedFieldName}:{${value}}`;
|
|
308
|
+
case 'TEXT':
|
|
309
|
+
return `${prefixedFieldName}:"${value}"`;
|
|
310
|
+
case 'NUMERIC':
|
|
311
|
+
let range = '';
|
|
312
|
+
if (is.startsWith('=')) {
|
|
313
|
+
range = `[${value} ${value}]`;
|
|
314
|
+
} else if (is === '<') {
|
|
315
|
+
range = `[-inf ${value}]`;
|
|
316
|
+
} else if (is === '>') {
|
|
317
|
+
range = `[${value} +inf]`;
|
|
318
|
+
} else if (is === '[]') {
|
|
319
|
+
range = `[${value[0]} ${value[1]}]`
|
|
320
|
+
}
|
|
321
|
+
return `${prefixedFieldName}:${range}`;
|
|
322
|
+
default:
|
|
323
|
+
return '';
|
|
324
|
+
}
|
|
325
|
+
}).join(' ');
|
|
326
|
+
return queryString;
|
|
251
327
|
}
|
|
252
328
|
|
|
253
329
|
/**
|
|
@@ -281,8 +357,14 @@ export class MeshOSService {
|
|
|
281
357
|
* Optionally include a target taskQueue to exec the
|
|
282
358
|
* workflow's call on a specific worker queue.
|
|
283
359
|
*/
|
|
284
|
-
constructor(id?: string, options?:
|
|
285
|
-
|
|
360
|
+
constructor(id?: string | MeshOSConfig, options?: MeshOSConfig) {
|
|
361
|
+
if (typeof(id) === 'string') {
|
|
362
|
+
this.id = id;
|
|
363
|
+
} else if (id?.id) {
|
|
364
|
+
this.id = id.id;
|
|
365
|
+
options = id;
|
|
366
|
+
id = undefined;
|
|
367
|
+
};
|
|
286
368
|
if (options?.taskQueue) {
|
|
287
369
|
this.taskQueue = options.taskQueue;
|
|
288
370
|
} else if (!id && !options?.taskQueue) {
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
WorkflowOptions } from "../../types/durable";
|
|
20
20
|
import { JobOutput, JobState } from '../../types/job';
|
|
21
21
|
import { StreamStatus } from '../../types/stream';
|
|
22
|
+
import { deterministicRandom } from '../../modules/utils';
|
|
22
23
|
|
|
23
24
|
export class WorkflowService {
|
|
24
25
|
|
|
@@ -173,6 +174,19 @@ export class WorkflowService {
|
|
|
173
174
|
return guidValue === 1;
|
|
174
175
|
}
|
|
175
176
|
|
|
177
|
+
/**
|
|
178
|
+
* returns a random number between 0 and 1. This number is deterministic
|
|
179
|
+
* and will never vary for a given seed. This is useful for randomizing
|
|
180
|
+
* pathways in a workflow that can be safely replayed.
|
|
181
|
+
* @returns {number}
|
|
182
|
+
*/
|
|
183
|
+
static random(): number {
|
|
184
|
+
const store = asyncLocalStorage.getStore();
|
|
185
|
+
const COUNTER = store.get('counter');
|
|
186
|
+
const seed = COUNTER.counter = COUNTER.counter + 1;
|
|
187
|
+
return deterministicRandom(seed);
|
|
188
|
+
}
|
|
189
|
+
|
|
176
190
|
/**
|
|
177
191
|
* send signal data into any other paused thread (which is paused and
|
|
178
192
|
* awaiting the signal) from within a hook-thread or the main-thread
|
|
@@ -46,8 +46,7 @@ class RedisStoreService extends StoreService<RedisClientType, RedisMultiType> {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
getMulti(): RedisMultiType {
|
|
49
|
-
|
|
50
|
-
return multi as unknown as RedisMultiType;
|
|
49
|
+
return this.redisClient.multi() as unknown as RedisMultiType;
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
async exec(...args: any[]): Promise<string|string[]|string[][]> {
|
|
@@ -21,7 +21,7 @@ class RedisStreamService extends StreamService<RedisClientType, RedisMultiType>
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
getMulti(): RedisMultiType {
|
|
24
|
-
return this.redisClient.
|
|
24
|
+
return this.redisClient.multi() as unknown as RedisMultiType;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
mintKey(type: KeyType, params: KeyStoreParams): string {
|
package/types/durable.ts
CHANGED
|
@@ -65,12 +65,9 @@ type MeshOSClassConfig = {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
type MeshOSConfig = {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
prefix: string[];
|
|
72
|
-
schema: Record<string, {type: 'TEXT' | 'NUMERIC' | 'TAG', sortable: boolean}>;
|
|
73
|
-
};
|
|
68
|
+
id?: string; //guid for the workflow when instancing
|
|
69
|
+
await?: boolean; //default is false; must explicitly send true to await the final result
|
|
70
|
+
taskQueue?: string; //optional target queue isolate for the function
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
type ConnectionConfig = {
|
|
@@ -96,6 +93,13 @@ type WorkerConfig = {
|
|
|
96
93
|
search?: WorkflowSearchOptions;
|
|
97
94
|
}
|
|
98
95
|
|
|
96
|
+
type FindWhereQuery = {
|
|
97
|
+
field: string;
|
|
98
|
+
is: string;
|
|
99
|
+
value: string | boolean | number | [number, number];
|
|
100
|
+
type?: string; //default is TEXT
|
|
101
|
+
}
|
|
102
|
+
|
|
99
103
|
type FindOptions = {
|
|
100
104
|
workflowName?: string; //also the function name
|
|
101
105
|
taskQueue?: string;
|
|
@@ -103,6 +107,17 @@ type FindOptions = {
|
|
|
103
107
|
index?: string; //the FT search index name
|
|
104
108
|
}
|
|
105
109
|
|
|
110
|
+
type FindWhereOptions = {
|
|
111
|
+
options?: FindOptions;
|
|
112
|
+
count?: boolean;
|
|
113
|
+
query: FindWhereQuery[];
|
|
114
|
+
return?: string[];
|
|
115
|
+
limit?: {
|
|
116
|
+
start: number,
|
|
117
|
+
size: number
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
106
121
|
type MeshOSOptions = {
|
|
107
122
|
name: string;
|
|
108
123
|
options: WorkerOptions;
|
|
@@ -113,6 +128,14 @@ type MeshOSActivityOptions = {
|
|
|
113
128
|
options: ActivityConfig;
|
|
114
129
|
}
|
|
115
130
|
|
|
131
|
+
type MeshOSWorkerOptions = {
|
|
132
|
+
taskQueue?: string; //change the default task queue
|
|
133
|
+
allowList?: Array<MeshOSOptions | string>; //limit which `hook` and `workflow` workers start
|
|
134
|
+
logLevel?: string; //debug, info, warn, error
|
|
135
|
+
maxSystemRetries?: number; //1-3 (10ms, 100ms, 1_000ms)
|
|
136
|
+
backoffCoefficient?: number; //2-10ish
|
|
137
|
+
}
|
|
138
|
+
|
|
116
139
|
type WorkerOptions = {
|
|
117
140
|
logLevel?: string; //debug, info, warn, error
|
|
118
141
|
maxSystemRetries?: number; //1-3 (10ms, 100ms, 1_000ms)
|
|
@@ -151,8 +174,11 @@ export {
|
|
|
151
174
|
Registry,
|
|
152
175
|
SignalOptions,
|
|
153
176
|
FindOptions,
|
|
177
|
+
FindWhereOptions,
|
|
178
|
+
FindWhereQuery,
|
|
154
179
|
HookOptions,
|
|
155
180
|
MeshOSActivityOptions,
|
|
181
|
+
MeshOSWorkerOptions,
|
|
156
182
|
MeshOSClassConfig,
|
|
157
183
|
MeshOSConfig,
|
|
158
184
|
MeshOSOptions,
|
package/types/index.ts
CHANGED
|
@@ -38,8 +38,11 @@ export {
|
|
|
38
38
|
Registry,
|
|
39
39
|
SignalOptions,
|
|
40
40
|
FindOptions,
|
|
41
|
+
FindWhereOptions,
|
|
42
|
+
FindWhereQuery,
|
|
41
43
|
HookOptions,
|
|
42
44
|
MeshOSActivityOptions,
|
|
45
|
+
MeshOSWorkerOptions,
|
|
43
46
|
MeshOSClassConfig,
|
|
44
47
|
MeshOSConfig,
|
|
45
48
|
MeshOSOptions,
|
|
@@ -49,7 +52,7 @@ export {
|
|
|
49
52
|
WorkflowSearchOptions,
|
|
50
53
|
WorkflowDataType,
|
|
51
54
|
WorkflowOptions,
|
|
52
|
-
}from './durable'
|
|
55
|
+
} from './durable'
|
|
53
56
|
export {
|
|
54
57
|
HookCondition,
|
|
55
58
|
HookConditions,
|