@hotmeshio/hotmesh 0.0.23 → 0.0.24
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 +66 -67
- package/build/index.d.ts +2 -1
- package/build/index.js +3 -1
- package/build/package.json +2 -2
- package/build/services/durable/factory.js +6 -6
- package/build/services/durable/handle.js +2 -4
- package/build/services/durable/index.d.ts +2 -2
- package/build/services/durable/index.js +2 -2
- package/build/services/durable/meshos.d.ts +108 -0
- package/build/services/durable/meshos.js +289 -0
- package/build/services/durable/search.js +0 -1
- package/build/services/durable/worker.d.ts +1 -1
- package/build/services/durable/worker.js +8 -4
- package/build/services/durable/workflow.d.ts +4 -0
- package/build/services/durable/workflow.js +21 -9
- package/build/services/signaler/stream.js +1 -2
- package/build/services/store/clients/ioredis.js +2 -2
- package/build/services/store/clients/redis.js +1 -1
- package/build/types/durable.d.ts +19 -5
- package/build/types/index.d.ts +1 -1
- package/index.ts +2 -1
- package/package.json +2 -2
- package/services/durable/factory.ts +6 -6
- package/services/durable/handle.ts +2 -4
- package/services/durable/index.ts +2 -2
- package/services/durable/meshos.ts +344 -0
- package/services/durable/search.ts +0 -1
- package/services/durable/worker.ts +8 -5
- package/services/durable/workflow.ts +23 -10
- package/services/signaler/stream.ts +1 -2
- package/services/store/clients/ioredis.ts +2 -3
- package/services/store/clients/redis.ts +1 -1
- package/types/durable.ts +26 -6
- package/types/index.ts +6 -2
- package/build/services/durable/meshdb.d.ts +0 -113
- package/build/services/durable/meshdb.js +0 -211
- package/services/durable/meshdb.ts +0 -254
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { WorkflowHandleService } from './handle';
|
|
2
|
-
import { WorkflowSearchOptions } from '../../types/durable';
|
|
3
|
-
import { RedisOptions, RedisClass } from '../../types/redis';
|
|
4
|
-
/**
|
|
5
|
-
* A base class for configuration and setup of
|
|
6
|
-
* a reentrant process database. Entities modeled as
|
|
7
|
-
* subclasses of this class will execute as reentrant
|
|
8
|
-
* processes with a 'main' execution thread and 'n'
|
|
9
|
-
* parallel hook threads.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* //RUN (start a workflow)
|
|
13
|
-
* const myInstance = new MeshDB('someIdempotentGuid');
|
|
14
|
-
* const handle = await myInstance.create(100);
|
|
15
|
-
* await handle.result(); //100
|
|
16
|
-
*
|
|
17
|
-
* //UPDATE (update a workflow)
|
|
18
|
-
* const result = await myInstance.decrement(11);
|
|
19
|
-
*/
|
|
20
|
-
export declare class MeshDBService {
|
|
21
|
-
/**
|
|
22
|
-
* The name of the main method. When this method
|
|
23
|
-
* is invoked/proxied, it is assumed that a new
|
|
24
|
-
* workflow instance is being created. In all other
|
|
25
|
-
* cases, the call is assumed to be a hook/update
|
|
26
|
-
*/
|
|
27
|
-
main: string;
|
|
28
|
-
/**
|
|
29
|
-
* The GUID for the workflow (assigned when created). This
|
|
30
|
-
* value should be idempotent and will be rejected if an
|
|
31
|
-
* instance is already running with the same id.
|
|
32
|
-
*/
|
|
33
|
-
id: string;
|
|
34
|
-
/**
|
|
35
|
-
* test value
|
|
36
|
-
*/
|
|
37
|
-
value: number;
|
|
38
|
-
/**
|
|
39
|
-
* The top-level Redis isolation. All workflow data is
|
|
40
|
-
* isolated within this namespace. Values should be
|
|
41
|
-
* lower-case with no spaces (e.g, 'staging', 'prod', 'test',
|
|
42
|
-
* 'routing-stagig', 'reporting-prod', etc.).
|
|
43
|
-
* 1) only url-safe values are allowed;
|
|
44
|
-
* 2) the 'a' symbol is reserved by HotMesh for indexing apps
|
|
45
|
-
*/
|
|
46
|
-
namespace: string;
|
|
47
|
-
/**
|
|
48
|
-
* The second-level isolation. Data is routed to workers
|
|
49
|
-
* that specify this task queue. Setting the task queue
|
|
50
|
-
* when the worker is created will ensure that the worker
|
|
51
|
-
* only receives messages destined for the queue. This
|
|
52
|
-
* allows callers to specify specific workers/containers
|
|
53
|
-
* for specific tasks. Only url-safe values are allowed.
|
|
54
|
-
*/
|
|
55
|
-
taskQueue: string;
|
|
56
|
-
/**
|
|
57
|
-
* The Redis connection options. NOTE: Redis and IORedis
|
|
58
|
-
* use different formats for their connection config.
|
|
59
|
-
*/
|
|
60
|
-
redisOptions: RedisOptions;
|
|
61
|
-
/**
|
|
62
|
-
* The Redis connection class. Import as follows
|
|
63
|
-
* within the base subclass as follows:
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* import Redis from 'ioredis';
|
|
67
|
-
* import * as Redis from 'redis';
|
|
68
|
-
*/
|
|
69
|
-
redisClass: RedisClass | null;
|
|
70
|
-
/**
|
|
71
|
-
* Configuration for the the Redis FT search index.
|
|
72
|
-
*/
|
|
73
|
-
search: WorkflowSearchOptions;
|
|
74
|
-
static getHotMeshClient(redisClass: RedisClass, redisOptions: RedisOptions, namespace: string, taskQueue: string): Promise<import("../hotmesh").HotMeshService>;
|
|
75
|
-
/**
|
|
76
|
-
* mints a new key, using the provided search prefix, ensuring
|
|
77
|
-
* new workflows are properly indexed
|
|
78
|
-
* @returns {string}
|
|
79
|
-
*/
|
|
80
|
-
static mintGuid(): string;
|
|
81
|
-
/**
|
|
82
|
-
* Creates an FT search index
|
|
83
|
-
*/
|
|
84
|
-
static createIndex(): Promise<void>;
|
|
85
|
-
/**
|
|
86
|
-
* Initialize the worker(s) for the entity. This is a static
|
|
87
|
-
* method that allows for optional task Queue targeting.
|
|
88
|
-
* NOTE: Allow List may be optionally used
|
|
89
|
-
* @param {string} taskQueue
|
|
90
|
-
* @param {string[]} allowList
|
|
91
|
-
*/
|
|
92
|
-
static doWork(taskQueue?: string, allowList?: string[]): Promise<void>;
|
|
93
|
-
/**
|
|
94
|
-
* executes the redis FT search query
|
|
95
|
-
* @example '@_quantity:[89 89]'
|
|
96
|
-
* @param {any[]} args
|
|
97
|
-
* @returns {string}
|
|
98
|
-
*/
|
|
99
|
-
static find(...args: string[]): Promise<string[] | [number]>;
|
|
100
|
-
/**
|
|
101
|
-
* returns the workflow handle (use the handle to call:
|
|
102
|
-
* `state`, `status`, `queryStatus`, and `result`)
|
|
103
|
-
* @param {string} id
|
|
104
|
-
* @returns {Promise<WorkflowHandleService>}
|
|
105
|
-
*/
|
|
106
|
-
static get(id: string): Promise<WorkflowHandleService>;
|
|
107
|
-
/**
|
|
108
|
-
* Initialize with an idempotent workflow identifier.
|
|
109
|
-
* Optionally include a target taskQueue to send
|
|
110
|
-
* events to a specific worker.
|
|
111
|
-
*/
|
|
112
|
-
constructor(id?: string, taskQueue?: string);
|
|
113
|
-
}
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MeshDBService = void 0;
|
|
4
|
-
const nanoid_1 = require("nanoid");
|
|
5
|
-
const client_1 = require("./client");
|
|
6
|
-
const search_1 = require("./search");
|
|
7
|
-
const worker_1 = require("./worker");
|
|
8
|
-
/**
|
|
9
|
-
* A base class for configuration and setup of
|
|
10
|
-
* a reentrant process database. Entities modeled as
|
|
11
|
-
* subclasses of this class will execute as reentrant
|
|
12
|
-
* processes with a 'main' execution thread and 'n'
|
|
13
|
-
* parallel hook threads.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* //RUN (start a workflow)
|
|
17
|
-
* const myInstance = new MeshDB('someIdempotentGuid');
|
|
18
|
-
* const handle = await myInstance.create(100);
|
|
19
|
-
* await handle.result(); //100
|
|
20
|
-
*
|
|
21
|
-
* //UPDATE (update a workflow)
|
|
22
|
-
* const result = await myInstance.decrement(11);
|
|
23
|
-
*/
|
|
24
|
-
class MeshDBService {
|
|
25
|
-
static async getHotMeshClient(redisClass, redisOptions, namespace, taskQueue) {
|
|
26
|
-
const client = new client_1.ClientService({
|
|
27
|
-
connection: {
|
|
28
|
-
class: redisClass,
|
|
29
|
-
options: redisOptions,
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
return await client.getHotMeshClient(taskQueue, namespace);
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* mints a new key, using the provided search prefix, ensuring
|
|
36
|
-
* new workflows are properly indexed
|
|
37
|
-
* @returns {string}
|
|
38
|
-
*/
|
|
39
|
-
static mintGuid() {
|
|
40
|
-
const my = new this();
|
|
41
|
-
return `${my.search?.prefix?.[0]}${(0, nanoid_1.nanoid)()}}`;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Creates an FT search index
|
|
45
|
-
*/
|
|
46
|
-
static async createIndex() {
|
|
47
|
-
const my = new this();
|
|
48
|
-
const hmClient = await MeshDBService.getHotMeshClient(my.redisClass, my.redisOptions, my.namespace, my.taskQueue);
|
|
49
|
-
search_1.Search.configureSearchIndex(hmClient, my.search);
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Initialize the worker(s) for the entity. This is a static
|
|
53
|
-
* method that allows for optional task Queue targeting.
|
|
54
|
-
* NOTE: Allow List may be optionally used
|
|
55
|
-
* @param {string} taskQueue
|
|
56
|
-
* @param {string[]} allowList
|
|
57
|
-
*/
|
|
58
|
-
static async doWork(taskQueue, allowList) {
|
|
59
|
-
const my = new this();
|
|
60
|
-
let prototype = Object.getPrototypeOf(my);
|
|
61
|
-
const durablePromises = [];
|
|
62
|
-
const found = [];
|
|
63
|
-
while (prototype !== null && !Object.getOwnPropertyNames(prototype).includes('__proto__')) {
|
|
64
|
-
const promises = Object.getOwnPropertyNames(prototype).map((prop) => {
|
|
65
|
-
if (found.includes(prop) || ['constructor'].includes(prop) || (allowList && !allowList.includes(prop))) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
const originalMethod = my[prop];
|
|
69
|
-
if (typeof originalMethod === 'function') {
|
|
70
|
-
found.push(prop);
|
|
71
|
-
return worker_1.WorkerService.create({
|
|
72
|
-
namespace: my.namespace,
|
|
73
|
-
connection: {
|
|
74
|
-
class: my.redisClass,
|
|
75
|
-
options: my.redisOptions,
|
|
76
|
-
},
|
|
77
|
-
taskQueue: taskQueue ?? my.taskQueue,
|
|
78
|
-
workflow: originalMethod,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
}).filter(p => p !== undefined); // filter out undefined values
|
|
82
|
-
durablePromises.push(...promises);
|
|
83
|
-
prototype = Object.getPrototypeOf(prototype);
|
|
84
|
-
}
|
|
85
|
-
await Promise.all(durablePromises);
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* executes the redis FT search query
|
|
89
|
-
* @example '@_quantity:[89 89]'
|
|
90
|
-
* @param {any[]} args
|
|
91
|
-
* @returns {string}
|
|
92
|
-
*/
|
|
93
|
-
static async find(...args) {
|
|
94
|
-
const my = new this();
|
|
95
|
-
const client = new client_1.ClientService({ connection: {
|
|
96
|
-
class: my.redisClass,
|
|
97
|
-
options: my.redisOptions
|
|
98
|
-
} });
|
|
99
|
-
return await client.workflow.search(my.taskQueue, my.main, my.namespace, my.search.index, ...args);
|
|
100
|
-
//[count, [id, fields[], id, fields[], id, fields[], ...]]
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* returns the workflow handle (use the handle to call:
|
|
104
|
-
* `state`, `status`, `queryStatus`, and `result`)
|
|
105
|
-
* @param {string} id
|
|
106
|
-
* @returns {Promise<WorkflowHandleService>}
|
|
107
|
-
*/
|
|
108
|
-
static async get(id) {
|
|
109
|
-
const my = new this();
|
|
110
|
-
const client = new client_1.ClientService({ connection: {
|
|
111
|
-
class: my.redisClass,
|
|
112
|
-
options: my.redisOptions
|
|
113
|
-
} });
|
|
114
|
-
return await client.workflow.getHandle(my.taskQueue, my.main, id, my.namespace);
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Initialize with an idempotent workflow identifier.
|
|
118
|
-
* Optionally include a target taskQueue to send
|
|
119
|
-
* events to a specific worker.
|
|
120
|
-
*/
|
|
121
|
-
constructor(id, taskQueue) {
|
|
122
|
-
/**
|
|
123
|
-
* The name of the main method. When this method
|
|
124
|
-
* is invoked/proxied, it is assumed that a new
|
|
125
|
-
* workflow instance is being created. In all other
|
|
126
|
-
* cases, the call is assumed to be a hook/update
|
|
127
|
-
*/
|
|
128
|
-
this.main = 'create';
|
|
129
|
-
/**
|
|
130
|
-
* The top-level Redis isolation. All workflow data is
|
|
131
|
-
* isolated within this namespace. Values should be
|
|
132
|
-
* lower-case with no spaces (e.g, 'staging', 'prod', 'test',
|
|
133
|
-
* 'routing-stagig', 'reporting-prod', etc.).
|
|
134
|
-
* 1) only url-safe values are allowed;
|
|
135
|
-
* 2) the 'a' symbol is reserved by HotMesh for indexing apps
|
|
136
|
-
*/
|
|
137
|
-
this.namespace = 'durable';
|
|
138
|
-
/**
|
|
139
|
-
* The second-level isolation. Data is routed to workers
|
|
140
|
-
* that specify this task queue. Setting the task queue
|
|
141
|
-
* when the worker is created will ensure that the worker
|
|
142
|
-
* only receives messages destined for the queue. This
|
|
143
|
-
* allows callers to specify specific workers/containers
|
|
144
|
-
* for specific tasks. Only url-safe values are allowed.
|
|
145
|
-
*/
|
|
146
|
-
this.taskQueue = 'default';
|
|
147
|
-
/**
|
|
148
|
-
* The Redis connection options. NOTE: Redis and IORedis
|
|
149
|
-
* use different formats for their connection config.
|
|
150
|
-
*/
|
|
151
|
-
this.redisOptions = {
|
|
152
|
-
host: 'localhost',
|
|
153
|
-
port: 6379,
|
|
154
|
-
password: '',
|
|
155
|
-
db: 0,
|
|
156
|
-
};
|
|
157
|
-
/**
|
|
158
|
-
* The Redis connection class. Import as follows
|
|
159
|
-
* within the base subclass as follows:
|
|
160
|
-
*
|
|
161
|
-
* @example
|
|
162
|
-
* import Redis from 'ioredis';
|
|
163
|
-
* import * as Redis from 'redis';
|
|
164
|
-
*/
|
|
165
|
-
this.redisClass = null;
|
|
166
|
-
this.id = id;
|
|
167
|
-
if (taskQueue) {
|
|
168
|
-
this.taskQueue = taskQueue;
|
|
169
|
-
}
|
|
170
|
-
else if (!id && !taskQueue) {
|
|
171
|
-
return this;
|
|
172
|
-
}
|
|
173
|
-
return new Proxy(this, {
|
|
174
|
-
get: (target, prop, receiver) => {
|
|
175
|
-
if (typeof target[prop] === 'function') {
|
|
176
|
-
return (...args) => {
|
|
177
|
-
return new Promise(async (resolve, reject) => {
|
|
178
|
-
const client = new client_1.ClientService({ connection: {
|
|
179
|
-
class: this.redisClass,
|
|
180
|
-
options: this.redisOptions
|
|
181
|
-
} });
|
|
182
|
-
if (prop === this.main) {
|
|
183
|
-
//start a new workflow (main method was called)
|
|
184
|
-
return client.workflow.start({
|
|
185
|
-
namespace: this.namespace,
|
|
186
|
-
args,
|
|
187
|
-
taskQueue: this.taskQueue,
|
|
188
|
-
workflowName: prop,
|
|
189
|
-
workflowId: this.id,
|
|
190
|
-
}).then(resolve).catch(reject);
|
|
191
|
-
}
|
|
192
|
-
else if (prop !== 'constructor') {
|
|
193
|
-
//update an existing workflow (hook/signal-in)
|
|
194
|
-
return client.workflow.hook({
|
|
195
|
-
namespace: this.namespace,
|
|
196
|
-
taskQueue: this.taskQueue,
|
|
197
|
-
workflowName: prop,
|
|
198
|
-
workflowId: this.id,
|
|
199
|
-
args,
|
|
200
|
-
}).then(resolve).catch(reject);
|
|
201
|
-
}
|
|
202
|
-
target[prop].apply(this, args).then(resolve).catch(reject);
|
|
203
|
-
});
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
return Reflect.get(target, prop, receiver);
|
|
207
|
-
},
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
exports.MeshDBService = MeshDBService;
|
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
import { nanoid } from 'nanoid';
|
|
2
|
-
|
|
3
|
-
import { ClientService as Client } from './client';
|
|
4
|
-
import { WorkflowHandleService } from './handle';
|
|
5
|
-
import { Search } from './search';
|
|
6
|
-
import { WorkerService as Worker } from './worker';
|
|
7
|
-
import { WorkflowSearchOptions } from '../../types/durable';
|
|
8
|
-
import { RedisOptions, RedisClass } from '../../types/redis';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* A base class for configuration and setup of
|
|
12
|
-
* a reentrant process database. Entities modeled as
|
|
13
|
-
* subclasses of this class will execute as reentrant
|
|
14
|
-
* processes with a 'main' execution thread and 'n'
|
|
15
|
-
* parallel hook threads.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* //RUN (start a workflow)
|
|
19
|
-
* const myInstance = new MeshDB('someIdempotentGuid');
|
|
20
|
-
* const handle = await myInstance.create(100);
|
|
21
|
-
* await handle.result(); //100
|
|
22
|
-
*
|
|
23
|
-
* //UPDATE (update a workflow)
|
|
24
|
-
* const result = await myInstance.decrement(11);
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
export class MeshDBService {
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The name of the main method. When this method
|
|
31
|
-
* is invoked/proxied, it is assumed that a new
|
|
32
|
-
* workflow instance is being created. In all other
|
|
33
|
-
* cases, the call is assumed to be a hook/update
|
|
34
|
-
*/
|
|
35
|
-
main = 'create';
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* The GUID for the workflow (assigned when created). This
|
|
39
|
-
* value should be idempotent and will be rejected if an
|
|
40
|
-
* instance is already running with the same id.
|
|
41
|
-
*/
|
|
42
|
-
id: string;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* test value
|
|
46
|
-
*/
|
|
47
|
-
value: number;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* The top-level Redis isolation. All workflow data is
|
|
51
|
-
* isolated within this namespace. Values should be
|
|
52
|
-
* lower-case with no spaces (e.g, 'staging', 'prod', 'test',
|
|
53
|
-
* 'routing-stagig', 'reporting-prod', etc.).
|
|
54
|
-
* 1) only url-safe values are allowed;
|
|
55
|
-
* 2) the 'a' symbol is reserved by HotMesh for indexing apps
|
|
56
|
-
*/
|
|
57
|
-
namespace = 'durable';
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* The second-level isolation. Data is routed to workers
|
|
61
|
-
* that specify this task queue. Setting the task queue
|
|
62
|
-
* when the worker is created will ensure that the worker
|
|
63
|
-
* only receives messages destined for the queue. This
|
|
64
|
-
* allows callers to specify specific workers/containers
|
|
65
|
-
* for specific tasks. Only url-safe values are allowed.
|
|
66
|
-
*/
|
|
67
|
-
taskQueue = 'default';
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* The Redis connection options. NOTE: Redis and IORedis
|
|
71
|
-
* use different formats for their connection config.
|
|
72
|
-
*/
|
|
73
|
-
redisOptions: RedisOptions = {
|
|
74
|
-
host: 'localhost',
|
|
75
|
-
port: 6379,
|
|
76
|
-
password: '',
|
|
77
|
-
db: 0,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* The Redis connection class. Import as follows
|
|
82
|
-
* within the base subclass as follows:
|
|
83
|
-
*
|
|
84
|
-
* @example
|
|
85
|
-
* import Redis from 'ioredis';
|
|
86
|
-
* import * as Redis from 'redis';
|
|
87
|
-
*/
|
|
88
|
-
redisClass: RedisClass | null = null;
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Configuration for the the Redis FT search index.
|
|
92
|
-
*/
|
|
93
|
-
search: WorkflowSearchOptions;
|
|
94
|
-
|
|
95
|
-
static async getHotMeshClient (redisClass: RedisClass, redisOptions: RedisOptions, namespace: string, taskQueue: string) {
|
|
96
|
-
const client = new Client({
|
|
97
|
-
connection: {
|
|
98
|
-
class: redisClass,
|
|
99
|
-
options: redisOptions,
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
return await client.getHotMeshClient(taskQueue, namespace);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* mints a new key, using the provided search prefix, ensuring
|
|
107
|
-
* new workflows are properly indexed
|
|
108
|
-
* @returns {string}
|
|
109
|
-
*/
|
|
110
|
-
static mintGuid(): string {
|
|
111
|
-
const my = new this();
|
|
112
|
-
return `${my.search?.prefix?.[0]}${nanoid()}}`;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Creates an FT search index
|
|
117
|
-
*/
|
|
118
|
-
static async createIndex() {
|
|
119
|
-
const my = new this();
|
|
120
|
-
const hmClient = await MeshDBService.getHotMeshClient(my.redisClass, my.redisOptions, my.namespace, my.taskQueue);
|
|
121
|
-
Search.configureSearchIndex(hmClient, my.search)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Initialize the worker(s) for the entity. This is a static
|
|
126
|
-
* method that allows for optional task Queue targeting.
|
|
127
|
-
* NOTE: Allow List may be optionally used
|
|
128
|
-
* @param {string} taskQueue
|
|
129
|
-
* @param {string[]} allowList
|
|
130
|
-
*/
|
|
131
|
-
static async doWork(taskQueue?: string, allowList?: string[]) {
|
|
132
|
-
const my = new this();
|
|
133
|
-
let prototype = Object.getPrototypeOf(my);
|
|
134
|
-
const durablePromises = [];
|
|
135
|
-
const found = [];
|
|
136
|
-
|
|
137
|
-
while (prototype !== null && !Object.getOwnPropertyNames(prototype).includes('__proto__')) {
|
|
138
|
-
const promises = Object.getOwnPropertyNames(prototype).map((prop) => {
|
|
139
|
-
if (found.includes(prop) || ['constructor'].includes(prop) || (allowList && !allowList.includes(prop))) {
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
const originalMethod = my[prop];
|
|
143
|
-
if (typeof originalMethod === 'function') {
|
|
144
|
-
found.push(prop);
|
|
145
|
-
return Worker.create({
|
|
146
|
-
namespace: my.namespace,
|
|
147
|
-
connection: {
|
|
148
|
-
class: my.redisClass,
|
|
149
|
-
options: my.redisOptions,
|
|
150
|
-
},
|
|
151
|
-
taskQueue: taskQueue ?? my.taskQueue,
|
|
152
|
-
workflow: originalMethod,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
}).filter(p => p !== undefined); // filter out undefined values
|
|
156
|
-
durablePromises.push(...promises);
|
|
157
|
-
prototype = Object.getPrototypeOf(prototype);
|
|
158
|
-
}
|
|
159
|
-
await Promise.all(durablePromises);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* executes the redis FT search query
|
|
164
|
-
* @example '@_quantity:[89 89]'
|
|
165
|
-
* @param {any[]} args
|
|
166
|
-
* @returns {string}
|
|
167
|
-
*/
|
|
168
|
-
static async find(...args: string[]): Promise<string[] | [number]> {
|
|
169
|
-
const my = new this();
|
|
170
|
-
const client = new Client({ connection: {
|
|
171
|
-
class: my.redisClass,
|
|
172
|
-
options: my.redisOptions
|
|
173
|
-
}});
|
|
174
|
-
return await client.workflow.search(
|
|
175
|
-
my.taskQueue,
|
|
176
|
-
my.main,
|
|
177
|
-
my.namespace,
|
|
178
|
-
my.search.index,
|
|
179
|
-
...args,
|
|
180
|
-
);
|
|
181
|
-
//[count, [id, fields[], id, fields[], id, fields[], ...]]
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* returns the workflow handle (use the handle to call:
|
|
186
|
-
* `state`, `status`, `queryStatus`, and `result`)
|
|
187
|
-
* @param {string} id
|
|
188
|
-
* @returns {Promise<WorkflowHandleService>}
|
|
189
|
-
*/
|
|
190
|
-
static async get(id: string): Promise<WorkflowHandleService> {
|
|
191
|
-
const my = new this();
|
|
192
|
-
const client = new Client({ connection: {
|
|
193
|
-
class: my.redisClass,
|
|
194
|
-
options: my.redisOptions
|
|
195
|
-
}});
|
|
196
|
-
return await client.workflow.getHandle(
|
|
197
|
-
my.taskQueue,
|
|
198
|
-
my.main,
|
|
199
|
-
id,
|
|
200
|
-
my.namespace,
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Initialize with an idempotent workflow identifier.
|
|
206
|
-
* Optionally include a target taskQueue to send
|
|
207
|
-
* events to a specific worker.
|
|
208
|
-
*/
|
|
209
|
-
constructor(id?: string, taskQueue?: string) {
|
|
210
|
-
this.id = id;
|
|
211
|
-
if (taskQueue) {
|
|
212
|
-
this.taskQueue = taskQueue;
|
|
213
|
-
} else if (!id && !taskQueue) {
|
|
214
|
-
return this;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return new Proxy(this, {
|
|
218
|
-
get: (target, prop, receiver) => {
|
|
219
|
-
if (typeof target[prop] === 'function') {
|
|
220
|
-
return (...args: any[]) => {
|
|
221
|
-
|
|
222
|
-
return new Promise(async (resolve, reject) => {
|
|
223
|
-
const client = new Client({ connection: {
|
|
224
|
-
class: this.redisClass,
|
|
225
|
-
options: this.redisOptions
|
|
226
|
-
}});
|
|
227
|
-
if (prop === this.main) {
|
|
228
|
-
//start a new workflow (main method was called)
|
|
229
|
-
return client.workflow.start({
|
|
230
|
-
namespace: this.namespace,
|
|
231
|
-
args,
|
|
232
|
-
taskQueue: this.taskQueue,
|
|
233
|
-
workflowName: prop,
|
|
234
|
-
workflowId: this.id,
|
|
235
|
-
}).then(resolve).catch(reject);
|
|
236
|
-
} else if (prop !== 'constructor') {
|
|
237
|
-
//update an existing workflow (hook/signal-in)
|
|
238
|
-
return client.workflow.hook({
|
|
239
|
-
namespace: this.namespace,
|
|
240
|
-
taskQueue: this.taskQueue,
|
|
241
|
-
workflowName: prop as string,
|
|
242
|
-
workflowId: this.id,
|
|
243
|
-
args,
|
|
244
|
-
}).then(resolve).catch(reject);
|
|
245
|
-
}
|
|
246
|
-
target[prop].apply(this, args).then(resolve).catch(reject);
|
|
247
|
-
});
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
return Reflect.get(target, prop, receiver);
|
|
251
|
-
},
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
}
|