@hotmeshio/hotmesh 0.2.2 → 0.2.4
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 +6 -5
- package/build/modules/utils.d.ts +1 -0
- package/build/modules/utils.js +6 -1
- package/build/package.json +2 -1
- package/build/services/engine/index.js +1 -0
- package/build/services/meshcall/index.d.ts +5 -5
- package/build/services/meshcall/index.js +47 -17
- package/build/services/meshcall/schemas/factory.d.ts +1 -1
- package/build/services/meshcall/schemas/factory.js +12 -5
- package/build/services/pipe/functions/cron.d.ts +4 -0
- package/build/services/pipe/functions/cron.js +32 -0
- package/build/services/pipe/functions/index.d.ts +2 -0
- package/build/services/pipe/functions/index.js +2 -0
- package/build/services/router/index.d.ts +1 -0
- package/build/services/router/index.js +5 -0
- package/build/types/hotmesh.d.ts +1 -0
- package/build/types/meshcall.d.ts +6 -2
- package/build/types/stream.d.ts +1 -0
- package/package.json +2 -1
- package/types/hotmesh.ts +1 -0
- package/types/meshcall.ts +21 -2
- package/types/stream.ts +2 -0
package/README.md
CHANGED
|
@@ -19,9 +19,9 @@ You have a Redis instance? Good. You're ready to go.
|
|
|
19
19
|
<summary style="font-size:1.25em;">Run an idempotent cron job</summary>
|
|
20
20
|
|
|
21
21
|
### Run a Cron
|
|
22
|
-
This example demonstrates an *idempotent* cron that runs
|
|
22
|
+
This example demonstrates an *idempotent* cron that runs daily at midnight. The `id` makes each cron job unique and ensures that only one instance runs, despite repeated invocations. *The `cron` method returns `false` if a workflow is already running with the same `id`.*
|
|
23
23
|
|
|
24
|
-
Optionally set a `delay` and/or set `maxCycles` to limit the number of cycles.
|
|
24
|
+
Optionally set a `delay` and/or set `maxCycles` to limit the number of cycles. The `interval` can be any human-readable time format (e.g., `1 day`, `2 hours`, `30 minutes`, etc) or a standard cron expression.
|
|
25
25
|
|
|
26
26
|
1. Define the cron function.
|
|
27
27
|
```typescript
|
|
@@ -29,7 +29,7 @@ You have a Redis instance? Good. You're ready to go.
|
|
|
29
29
|
import { MeshCall } from '@hotmeshio/hotmesh';
|
|
30
30
|
import * as Redis from 'redis';
|
|
31
31
|
|
|
32
|
-
export const runMyCron = async (id: string, interval = '
|
|
32
|
+
export const runMyCron = async (id: string, interval = '0 0 * * *'): Promise<boolean> => {
|
|
33
33
|
return await MeshCall.cron({
|
|
34
34
|
topic: 'my.cron.function',
|
|
35
35
|
redis: {
|
|
@@ -48,7 +48,8 @@ You have a Redis instance? Good. You're ready to go.
|
|
|
48
48
|
```typescript
|
|
49
49
|
//server.ts
|
|
50
50
|
import { runMyCron } from './cron';
|
|
51
|
-
|
|
51
|
+
|
|
52
|
+
runMyCron('myNightlyCron123');
|
|
52
53
|
```
|
|
53
54
|
</details>
|
|
54
55
|
|
|
@@ -69,7 +70,7 @@ You have a Redis instance? Good. You're ready to go.
|
|
|
69
70
|
class: Redis,
|
|
70
71
|
options: { url: 'redis://:key_admin@redis:6379' }
|
|
71
72
|
},
|
|
72
|
-
options: { id: '
|
|
73
|
+
options: { id: 'myNightlyCron123' }
|
|
73
74
|
});
|
|
74
75
|
```
|
|
75
76
|
</details>
|
package/build/modules/utils.d.ts
CHANGED
package/build/modules/utils.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.restoreHierarchy = exports.getValueByPath = exports.getIndexedHash = exports.getSymVal = exports.getSymKey = exports.formatISODate = exports.getTimeSeries = exports.getSubscriptionTopic = exports.findSubscriptionForTrigger = exports.findTopKey = exports.matchesStatus = exports.matchesStatusCode = exports.identifyRedisTypeFromClass = exports.polyfill = exports.identifyRedisType = exports.XSleepFor = exports.sleepImmediate = exports.sleepFor = exports.guid = exports.deterministicRandom = exports.deepCopy = exports.getSystemHealth = exports.hashOptions = void 0;
|
|
6
|
+
exports.isValidCron = exports.restoreHierarchy = exports.getValueByPath = exports.getIndexedHash = exports.getSymVal = exports.getSymKey = exports.formatISODate = exports.getTimeSeries = exports.getSubscriptionTopic = exports.findSubscriptionForTrigger = exports.findTopKey = exports.matchesStatus = exports.matchesStatusCode = exports.identifyRedisTypeFromClass = exports.polyfill = exports.identifyRedisType = exports.XSleepFor = exports.sleepImmediate = exports.sleepFor = exports.guid = exports.deterministicRandom = exports.deepCopy = exports.getSystemHealth = exports.hashOptions = void 0;
|
|
7
7
|
const os_1 = __importDefault(require("os"));
|
|
8
8
|
const crypto_1 = require("crypto");
|
|
9
9
|
const nanoid_1 = require("nanoid");
|
|
@@ -239,3 +239,8 @@ function restoreHierarchy(obj) {
|
|
|
239
239
|
return result;
|
|
240
240
|
}
|
|
241
241
|
exports.restoreHierarchy = restoreHierarchy;
|
|
242
|
+
function isValidCron(cronExpression) {
|
|
243
|
+
const cronRegex = /^(\*|([0-5]?\d)) (\*|([01]?\d|2[0-3])) (\*|([12]?\d|3[01])) (\*|([1-9]|1[0-2])) (\*|([0-6](?:-[0-6])?(?:,[0-6])?))$/;
|
|
244
|
+
return cronRegex.test(cronExpression);
|
|
245
|
+
}
|
|
246
|
+
exports.isValidCron = isValidCron;
|
package/build/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotmeshio/hotmesh",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Unbreakable Workflows",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
"dependencies": {
|
|
84
84
|
"@apidevtools/json-schema-ref-parser": "^10.1.0",
|
|
85
85
|
"@opentelemetry/api": "^1.4.1",
|
|
86
|
+
"cron-parser": "^4.9.0",
|
|
86
87
|
"js-yaml": "^4.1.0",
|
|
87
88
|
"ms": "^2.1.3",
|
|
88
89
|
"nanoid": "^3.3.6",
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { HotMesh } from '../hotmesh';
|
|
2
|
-
import { MeshCallConnectParams, MeshCallCronParams, MeshCallExecParams, MeshCallFlushParams, MeshCallInterruptParams } from '../../types/meshcall';
|
|
2
|
+
import { MeshCallConnectParams, MeshCallCronParams, MeshCallExecParams, MeshCallFlushParams, MeshCallInstanceOptions, MeshCallInterruptParams } from '../../types/meshcall';
|
|
3
3
|
import { RedisConfig } from '../../types';
|
|
4
4
|
declare class MeshCall {
|
|
5
5
|
static workers: Map<string, HotMesh | Promise<HotMesh>>;
|
|
6
6
|
static engines: Map<string, HotMesh | Promise<HotMesh>>;
|
|
7
7
|
static connections: Map<string, any>;
|
|
8
8
|
constructor();
|
|
9
|
-
static findFirstMatching(
|
|
10
|
-
static getHotMeshClient: (namespace: string, connection: RedisConfig) => Promise<HotMesh>;
|
|
9
|
+
static findFirstMatching(targets: Map<string, HotMesh | Promise<HotMesh>>, namespace: string, config: RedisConfig, options?: MeshCallInstanceOptions): Promise<HotMesh | void>;
|
|
10
|
+
static getHotMeshClient: (namespace: string, connection: RedisConfig, options?: MeshCallInstanceOptions) => Promise<HotMesh>;
|
|
11
11
|
static verifyWorkflowActive(hotMesh: HotMesh, appId?: string, count?: number): Promise<boolean>;
|
|
12
12
|
static activateWorkflow(hotMesh: HotMesh, appId?: string, version?: string): Promise<void>;
|
|
13
|
-
static getInstance(namespace: string, redis: RedisConfig): Promise<HotMesh>;
|
|
13
|
+
static getInstance(namespace: string, redis: RedisConfig, options?: MeshCallInstanceOptions): Promise<HotMesh>;
|
|
14
14
|
static connect(params: MeshCallConnectParams): Promise<HotMesh>;
|
|
15
15
|
static exec<U>(params: MeshCallExecParams): Promise<U>;
|
|
16
16
|
static flush(params: MeshCallFlushParams): Promise<void>;
|
|
17
17
|
static cron(params: MeshCallCronParams): Promise<boolean>;
|
|
18
|
-
static interrupt(params: MeshCallInterruptParams): Promise<
|
|
18
|
+
static interrupt(params: MeshCallInterruptParams): Promise<boolean>;
|
|
19
19
|
static shutdown(): Promise<void>;
|
|
20
20
|
}
|
|
21
21
|
export { MeshCall };
|
|
@@ -10,14 +10,19 @@ const hotmesh_1 = require("../hotmesh");
|
|
|
10
10
|
const enums_1 = require("../../modules/enums");
|
|
11
11
|
const utils_1 = require("../../modules/utils");
|
|
12
12
|
const key_1 = require("../../modules/key");
|
|
13
|
+
const cron_1 = require("../pipe/functions/cron");
|
|
13
14
|
const factory_1 = require("./schemas/factory");
|
|
14
15
|
class MeshCall {
|
|
15
16
|
constructor() { }
|
|
16
|
-
static async findFirstMatching(
|
|
17
|
-
for (const [id, hotMeshInstance] of
|
|
18
|
-
|
|
17
|
+
static async findFirstMatching(targets, namespace = key_1.HMNS, config, options = {}) {
|
|
18
|
+
for (const [id, hotMeshInstance] of targets) {
|
|
19
|
+
const hotMesh = await hotMeshInstance;
|
|
20
|
+
const appId = hotMesh.engine.appId;
|
|
21
|
+
if (appId === namespace) {
|
|
19
22
|
if (id.startsWith((0, utils_1.hashOptions)(config.options))) {
|
|
20
|
-
|
|
23
|
+
if (Boolean(options.readonly) == Boolean(hotMesh.engine.router.readonly)) {
|
|
24
|
+
return hotMeshInstance;
|
|
25
|
+
}
|
|
21
26
|
}
|
|
22
27
|
}
|
|
23
28
|
}
|
|
@@ -34,7 +39,7 @@ class MeshCall {
|
|
|
34
39
|
}
|
|
35
40
|
return true;
|
|
36
41
|
}
|
|
37
|
-
static async activateWorkflow(hotMesh, appId = key_1.HMNS, version =
|
|
42
|
+
static async activateWorkflow(hotMesh, appId = key_1.HMNS, version = factory_1.VERSION) {
|
|
38
43
|
const app = await hotMesh.engine.store.getApp(appId);
|
|
39
44
|
const appVersion = app?.version;
|
|
40
45
|
if (appVersion === version && !app.active) {
|
|
@@ -59,12 +64,15 @@ class MeshCall {
|
|
|
59
64
|
}
|
|
60
65
|
}
|
|
61
66
|
}
|
|
62
|
-
static async getInstance(namespace, redis) {
|
|
63
|
-
let hotMeshInstance
|
|
67
|
+
static async getInstance(namespace, redis, options = {}) {
|
|
68
|
+
let hotMeshInstance;
|
|
69
|
+
if (!options.readonly) {
|
|
70
|
+
hotMeshInstance = await MeshCall.findFirstMatching(MeshCall.workers, namespace, redis, options);
|
|
71
|
+
}
|
|
64
72
|
if (!hotMeshInstance) {
|
|
65
|
-
hotMeshInstance = await MeshCall.findFirstMatching(MeshCall.engines, namespace, redis);
|
|
73
|
+
hotMeshInstance = await MeshCall.findFirstMatching(MeshCall.engines, namespace, redis, options);
|
|
66
74
|
if (!hotMeshInstance) {
|
|
67
|
-
hotMeshInstance = (await MeshCall.getHotMeshClient(namespace, redis));
|
|
75
|
+
hotMeshInstance = (await MeshCall.getHotMeshClient(namespace, redis, options));
|
|
68
76
|
}
|
|
69
77
|
}
|
|
70
78
|
return hotMeshInstance;
|
|
@@ -130,7 +138,7 @@ class MeshCall {
|
|
|
130
138
|
await hotMeshInstance.scrub(params.id ?? params?.options?.id);
|
|
131
139
|
}
|
|
132
140
|
static async cron(params) {
|
|
133
|
-
|
|
141
|
+
if (params.callback) {
|
|
134
142
|
await MeshCall.connect({
|
|
135
143
|
logLevel: params.logLevel,
|
|
136
144
|
guid: params.guid,
|
|
@@ -139,18 +147,32 @@ class MeshCall {
|
|
|
139
147
|
callback: params.callback,
|
|
140
148
|
namespace: params.namespace,
|
|
141
149
|
});
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
150
|
+
}
|
|
151
|
+
const TOPIC = `${params.namespace ?? key_1.HMNS}.cron`;
|
|
152
|
+
const maxCycles = params.options.maxCycles ?? 100000;
|
|
153
|
+
let interval = enums_1.HMSH_FIDELITY_SECONDS;
|
|
154
|
+
let delay;
|
|
155
|
+
let cron;
|
|
156
|
+
if ((0, utils_1.isValidCron)(params.options.interval)) {
|
|
157
|
+
cron = params.options.interval;
|
|
158
|
+
const nextDelay = new cron_1.CronHandler().nextDelay(cron);
|
|
159
|
+
delay = nextDelay > 0 ? nextDelay : undefined;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
const seconds = (0, ms_1.default)(params.options.interval) / 1000;
|
|
163
|
+
interval = Math.max(seconds, enums_1.HMSH_FIDELITY_SECONDS);
|
|
164
|
+
delay = params.options.delay
|
|
145
165
|
? (0, ms_1.default)(params.options.delay) / 1000
|
|
146
166
|
: undefined;
|
|
147
|
-
|
|
148
|
-
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
const hotMeshInstance = await MeshCall.getInstance(params.namespace, params.redis, { readonly: params.callback ? false : true, guid: params.guid });
|
|
149
170
|
await hotMeshInstance.pub(TOPIC, {
|
|
150
171
|
id: params.options.id,
|
|
151
172
|
topic: params.topic,
|
|
152
173
|
args: params.args,
|
|
153
174
|
interval,
|
|
175
|
+
cron,
|
|
154
176
|
maxCycles,
|
|
155
177
|
delay,
|
|
156
178
|
});
|
|
@@ -165,7 +187,13 @@ class MeshCall {
|
|
|
165
187
|
}
|
|
166
188
|
static async interrupt(params) {
|
|
167
189
|
const hotMeshInstance = await MeshCall.getInstance(params.namespace, params.redis);
|
|
168
|
-
|
|
190
|
+
try {
|
|
191
|
+
await hotMeshInstance.interrupt(`${params.namespace ?? key_1.HMNS}.cron`, params.options.id, { throw: false, expire: 1 });
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
return true;
|
|
169
197
|
}
|
|
170
198
|
static async shutdown() {
|
|
171
199
|
for (const [_, hotMeshInstance] of MeshCall.workers) {
|
|
@@ -182,7 +210,7 @@ _a = MeshCall;
|
|
|
182
210
|
MeshCall.workers = new Map();
|
|
183
211
|
MeshCall.engines = new Map();
|
|
184
212
|
MeshCall.connections = new Map();
|
|
185
|
-
MeshCall.getHotMeshClient = async (namespace, connection) => {
|
|
213
|
+
MeshCall.getHotMeshClient = async (namespace, connection, options = {}) => {
|
|
186
214
|
const optionsHash = (0, utils_1.hashOptions)(connection.options);
|
|
187
215
|
const targetNS = namespace ?? key_1.HMNS;
|
|
188
216
|
const connectionNS = `${optionsHash}.${targetNS}`;
|
|
@@ -192,6 +220,7 @@ MeshCall.getHotMeshClient = async (namespace, connection) => {
|
|
|
192
220
|
return hotMeshClient;
|
|
193
221
|
}
|
|
194
222
|
const hotMeshClient = hotmesh_1.HotMesh.init({
|
|
223
|
+
guid: options.guid,
|
|
195
224
|
appId: targetNS,
|
|
196
225
|
logLevel: enums_1.HMSH_LOGLEVEL,
|
|
197
226
|
engine: {
|
|
@@ -199,6 +228,7 @@ MeshCall.getHotMeshClient = async (namespace, connection) => {
|
|
|
199
228
|
class: connection.class,
|
|
200
229
|
options: connection.options,
|
|
201
230
|
},
|
|
231
|
+
readonly: options.readonly,
|
|
202
232
|
},
|
|
203
233
|
});
|
|
204
234
|
MeshCall.engines.set(connectionNS, hotMeshClient);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "
|
|
1
|
+
export declare const VERSION = "2";
|
|
2
2
|
export declare const getWorkflowYAML: (appId?: string, version?: string) => string;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getWorkflowYAML = exports.VERSION = void 0;
|
|
4
4
|
const key_1 = require("../../../modules/key");
|
|
5
|
-
exports.VERSION = '
|
|
5
|
+
exports.VERSION = '2';
|
|
6
6
|
const getWorkflowYAML = (appId = key_1.HMNS, version = exports.VERSION) => {
|
|
7
7
|
return `app:
|
|
8
8
|
id: ${appId}
|
|
@@ -72,8 +72,6 @@ const getWorkflowYAML = (appId = key_1.HMNS, version = exports.VERSION) => {
|
|
|
72
72
|
|
|
73
73
|
- subscribes: ${appId}.cron
|
|
74
74
|
|
|
75
|
-
expire: 120
|
|
76
|
-
|
|
77
75
|
input:
|
|
78
76
|
schema:
|
|
79
77
|
type: object
|
|
@@ -86,7 +84,10 @@ const getWorkflowYAML = (appId = key_1.HMNS, version = exports.VERSION) => {
|
|
|
86
84
|
description: time in seconds to sleep before invoking the first cycle
|
|
87
85
|
interval:
|
|
88
86
|
type: number
|
|
89
|
-
description: time in seconds to sleep before the next cycle
|
|
87
|
+
description: time in seconds to sleep before the next cycle (also min interval in seconds if cron is provided)
|
|
88
|
+
cron:
|
|
89
|
+
type: string
|
|
90
|
+
description: cron expression to determine the next cycle (takes precedence over interval)
|
|
90
91
|
topic:
|
|
91
92
|
type: string
|
|
92
93
|
description: topic assigned to locate the worker
|
|
@@ -120,6 +121,8 @@ const getWorkflowYAML = (appId = key_1.HMNS, version = exports.VERSION) => {
|
|
|
120
121
|
properties:
|
|
121
122
|
sleepSeconds:
|
|
122
123
|
type: number
|
|
124
|
+
iterationCount:
|
|
125
|
+
type: number
|
|
123
126
|
maps:
|
|
124
127
|
sleepSeconds:
|
|
125
128
|
'@pipe':
|
|
@@ -148,7 +151,11 @@ const getWorkflowYAML = (appId = key_1.HMNS, version = exports.VERSION) => {
|
|
|
148
151
|
ancestor: cycle_hook_cron
|
|
149
152
|
input:
|
|
150
153
|
maps:
|
|
151
|
-
sleepSeconds:
|
|
154
|
+
sleepSeconds:
|
|
155
|
+
'@pipe':
|
|
156
|
+
- ['{trigger_cron.output.data.cron}']
|
|
157
|
+
- ['{@cron.nextDelay}', '{trigger_cron.output.data.interval}']
|
|
158
|
+
- ['{@math.max}']
|
|
152
159
|
iterationCount:
|
|
153
160
|
'@pipe':
|
|
154
161
|
- ['{cycle_hook_cron.output.data.iterationCount}', 1]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CronHandler = void 0;
|
|
4
|
+
const cron_parser_1 = require("cron-parser");
|
|
5
|
+
const enums_1 = require("../../../modules/enums");
|
|
6
|
+
const utils_1 = require("../../../modules/utils");
|
|
7
|
+
class CronHandler {
|
|
8
|
+
nextDelay(cronExpression) {
|
|
9
|
+
try {
|
|
10
|
+
if (!(0, utils_1.isValidCron)(cronExpression)) {
|
|
11
|
+
return -1;
|
|
12
|
+
}
|
|
13
|
+
const interval = (0, cron_parser_1.parseExpression)(cronExpression, { utc: true });
|
|
14
|
+
const nextDate = interval.next().toDate();
|
|
15
|
+
const now = new Date();
|
|
16
|
+
const delay = (nextDate.getTime() - now.getTime()) / 1000;
|
|
17
|
+
if (delay <= 0) {
|
|
18
|
+
return -1;
|
|
19
|
+
}
|
|
20
|
+
if (delay < enums_1.HMSH_FIDELITY_SECONDS) {
|
|
21
|
+
return enums_1.HMSH_FIDELITY_SECONDS;
|
|
22
|
+
}
|
|
23
|
+
const iDelay = Math.round(delay);
|
|
24
|
+
return iDelay;
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
console.error('Error calculating next cron job execution delay:', error);
|
|
28
|
+
return -1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.CronHandler = CronHandler;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ArrayHandler } from './array';
|
|
2
2
|
import { BitwiseHandler } from './bitwise';
|
|
3
3
|
import { ConditionalHandler } from './conditional';
|
|
4
|
+
import { CronHandler } from './cron';
|
|
4
5
|
import { DateHandler } from './date';
|
|
5
6
|
import { JsonHandler } from './json';
|
|
6
7
|
import { LogicalHandler } from './logical';
|
|
@@ -14,6 +15,7 @@ declare const _default: {
|
|
|
14
15
|
array: ArrayHandler;
|
|
15
16
|
bitwise: BitwiseHandler;
|
|
16
17
|
conditional: ConditionalHandler;
|
|
18
|
+
cron: CronHandler;
|
|
17
19
|
date: DateHandler;
|
|
18
20
|
json: JsonHandler;
|
|
19
21
|
logical: LogicalHandler;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const array_1 = require("./array");
|
|
4
4
|
const bitwise_1 = require("./bitwise");
|
|
5
5
|
const conditional_1 = require("./conditional");
|
|
6
|
+
const cron_1 = require("./cron");
|
|
6
7
|
const date_1 = require("./date");
|
|
7
8
|
const json_1 = require("./json");
|
|
8
9
|
const logical_1 = require("./logical");
|
|
@@ -16,6 +17,7 @@ exports.default = {
|
|
|
16
17
|
array: new array_1.ArrayHandler(),
|
|
17
18
|
bitwise: new bitwise_1.BitwiseHandler(),
|
|
18
19
|
conditional: new conditional_1.ConditionalHandler(),
|
|
20
|
+
cron: new cron_1.CronHandler(),
|
|
19
21
|
date: new date_1.DateHandler(),
|
|
20
22
|
json: new json_1.JsonHandler(),
|
|
21
23
|
logical: new logical_1.LogicalHandler(),
|
|
@@ -26,6 +26,7 @@ declare class Router {
|
|
|
26
26
|
innerPromiseResolve: (() => void) | null;
|
|
27
27
|
isSleeping: boolean;
|
|
28
28
|
sleepTimout: NodeJS.Timeout | null;
|
|
29
|
+
readonly: boolean;
|
|
29
30
|
constructor(config: StreamConfig, stream: StreamService<RedisClient, RedisMulti>, store: StoreService<RedisClient, RedisMulti>, logger: ILogger);
|
|
30
31
|
private resetThrottleState;
|
|
31
32
|
createGroup(stream: string, group: string): Promise<void>;
|
|
@@ -26,6 +26,7 @@ class Router {
|
|
|
26
26
|
this.reclaimDelay = config.reclaimDelay || enums_1.HMSH_XCLAIM_DELAY_MS;
|
|
27
27
|
this.reclaimCount = config.reclaimCount || enums_1.HMSH_XCLAIM_COUNT;
|
|
28
28
|
this.logger = logger;
|
|
29
|
+
this.readonly = config.readonly || false;
|
|
29
30
|
this.resetThrottleState();
|
|
30
31
|
}
|
|
31
32
|
resetThrottleState() {
|
|
@@ -73,6 +74,10 @@ class Router {
|
|
|
73
74
|
});
|
|
74
75
|
}
|
|
75
76
|
async consumeMessages(stream, group, consumer, callback) {
|
|
77
|
+
if (this.readonly) {
|
|
78
|
+
this.logger.info(`router-stream-readonly`, { group, consumer, stream });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
76
81
|
this.logger.info(`router-stream-starting`, { group, consumer, stream });
|
|
77
82
|
Router.instances.add(this);
|
|
78
83
|
this.shouldConsume = true;
|
package/build/types/hotmesh.d.ts
CHANGED
|
@@ -46,13 +46,17 @@ interface MeshCallCronParams {
|
|
|
46
46
|
topic: string;
|
|
47
47
|
redis: RedisConfig;
|
|
48
48
|
args: any[];
|
|
49
|
-
callback
|
|
49
|
+
callback?: (...args: any[]) => any;
|
|
50
50
|
options: MeshCallCronOptions;
|
|
51
51
|
}
|
|
52
|
+
interface MeshCallInstanceOptions {
|
|
53
|
+
readonly?: boolean;
|
|
54
|
+
guid?: string;
|
|
55
|
+
}
|
|
52
56
|
interface MeshCallInterruptParams {
|
|
53
57
|
namespace?: string;
|
|
54
58
|
topic: string;
|
|
55
59
|
redis: RedisConfig;
|
|
56
60
|
options: MeshCallInterruptOptions;
|
|
57
61
|
}
|
|
58
|
-
export { MeshCallConnectParams, MeshCallExecParams, MeshCallCronParams, MeshCallExecOptions, MeshCallCronOptions, MeshCallInterruptOptions, MeshCallInterruptParams, MeshCallFlushOptions, MeshCallFlushParams, };
|
|
62
|
+
export { MeshCallConnectParams, MeshCallExecParams, MeshCallCronParams, MeshCallExecOptions, MeshCallCronOptions, MeshCallInterruptOptions, MeshCallInterruptParams, MeshCallFlushOptions, MeshCallFlushParams, MeshCallInstanceOptions, };
|
package/build/types/stream.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotmeshio/hotmesh",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Unbreakable Workflows",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
"dependencies": {
|
|
84
84
|
"@apidevtools/json-schema-ref-parser": "^10.1.0",
|
|
85
85
|
"@opentelemetry/api": "^1.4.1",
|
|
86
|
+
"cron-parser": "^4.9.0",
|
|
86
87
|
"js-yaml": "^4.1.0",
|
|
87
88
|
"ms": "^2.1.3",
|
|
88
89
|
"nanoid": "^3.3.6",
|
package/types/hotmesh.ts
CHANGED
package/types/meshcall.ts
CHANGED
|
@@ -102,6 +102,7 @@ interface MeshCallCronOptions {
|
|
|
102
102
|
/**
|
|
103
103
|
* For example, `1 day`, `1 hour`. Fidelity is generally
|
|
104
104
|
* within 5 seconds. Refer to the syntax for the `ms` NPM package.
|
|
105
|
+
* Standard cron syntax is also supported. (e.g. `0 0 * * *`)
|
|
105
106
|
*/
|
|
106
107
|
interval: string;
|
|
107
108
|
/**
|
|
@@ -112,6 +113,7 @@ interface MeshCallCronOptions {
|
|
|
112
113
|
* Time in seconds to sleep before invoking the first cycle.
|
|
113
114
|
* For example, `1 day`, `1 hour`. Fidelity is generally
|
|
114
115
|
* within 5 seconds. Refer to the syntax for the `ms` NPM package.
|
|
116
|
+
* If the interval field uses standard cron syntax, this field is ignored.
|
|
115
117
|
*/
|
|
116
118
|
delay?: string;
|
|
117
119
|
}
|
|
@@ -151,15 +153,31 @@ interface MeshCallCronParams {
|
|
|
151
153
|
*/
|
|
152
154
|
args: any[];
|
|
153
155
|
/**
|
|
154
|
-
* linked worker function to run
|
|
156
|
+
* linked worker function to run; if not provided, the system will
|
|
157
|
+
* attempt to start the cron job using the topic, but a new
|
|
158
|
+
* worker will not be created. This is useful for spawning a cron job
|
|
159
|
+
* from an ephemeral node process.
|
|
155
160
|
*/
|
|
156
|
-
callback
|
|
161
|
+
callback?: (...args: any[]) => any;
|
|
157
162
|
/**
|
|
158
163
|
* Options for the cron job
|
|
159
164
|
*/
|
|
160
165
|
options: MeshCallCronOptions;
|
|
161
166
|
}
|
|
162
167
|
|
|
168
|
+
interface MeshCallInstanceOptions {
|
|
169
|
+
/**
|
|
170
|
+
* if true, the connection to HotMesh will be in readonly mode
|
|
171
|
+
* and the instantiated client will not route messages
|
|
172
|
+
* @default false
|
|
173
|
+
*/
|
|
174
|
+
readonly?: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Idempotent GUID for the worker and engine
|
|
177
|
+
*/
|
|
178
|
+
guid?: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
163
181
|
interface MeshCallInterruptParams {
|
|
164
182
|
/**
|
|
165
183
|
* namespace for grouping common functions
|
|
@@ -189,4 +207,5 @@ export {
|
|
|
189
207
|
MeshCallInterruptParams,
|
|
190
208
|
MeshCallFlushOptions,
|
|
191
209
|
MeshCallFlushParams,
|
|
210
|
+
MeshCallInstanceOptions,
|
|
192
211
|
};
|
package/types/stream.ts
CHANGED
|
@@ -138,4 +138,6 @@ export type StreamConfig = {
|
|
|
138
138
|
reclaimDelay?: number;
|
|
139
139
|
/** Maximum number of reclaims allowed, defaults to 3. Values greater throw an error */
|
|
140
140
|
reclaimCount?: number;
|
|
141
|
+
/** if true, will not process stream messages; default true */
|
|
142
|
+
readonly?: boolean;
|
|
141
143
|
};
|