@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 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 every day. 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`.*
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 = '1 day'): Promise<boolean> => {
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
- runMyCron('myDailyCron123');
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: 'myDailyCron123' }
73
+ options: { id: 'myNightlyCron123' }
73
74
  });
74
75
  ```
75
76
  </details>
@@ -35,3 +35,4 @@ export declare function getValueByPath(obj: {
35
35
  [key: string]: any;
36
36
  }, path: string): any;
37
37
  export declare function restoreHierarchy(obj: StringAnyType): StringAnyType;
38
+ export declare function isValidCron(cronExpression: string): boolean;
@@ -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;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.2.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",
@@ -92,6 +92,7 @@ class EngineService {
92
92
  reclaimDelay: config.engine.reclaimDelay,
93
93
  reclaimCount: config.engine.reclaimCount,
94
94
  throttle,
95
+ readonly: config.engine.readonly,
95
96
  }, this.stream, this.store, this.logger);
96
97
  }
97
98
  async fetchAndVerifyVID(vid, count = 0) {
@@ -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(workers: Map<string, HotMesh | Promise<HotMesh>>, namespace: string, config: RedisConfig): Promise<HotMesh | void>;
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<void>;
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(workers, namespace = key_1.HMNS, config) {
17
- for (const [id, hotMeshInstance] of workers) {
18
- if ((await hotMeshInstance).namespace === namespace) {
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
- return hotMeshInstance;
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 = '1') {
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 = await MeshCall.findFirstMatching(MeshCall.workers, namespace, redis);
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
- try {
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
- const TOPIC = `${params.namespace ?? key_1.HMNS}.cron`;
143
- const interval = (0, ms_1.default)(params.options.interval) / 1000;
144
- const delay = params.options.delay
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
- const maxCycles = params.options.maxCycles ?? 1000000;
148
- const hotMeshInstance = await MeshCall.getInstance(params.namespace, params.redis);
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
- await hotMeshInstance.interrupt(`${params.namespace ?? key_1.HMNS}.cron`, params.options.id, { throw: false, expire: 60 });
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";
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 = '1';
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: '{trigger_cron.output.data.interval}'
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,4 @@
1
+ declare class CronHandler {
2
+ nextDelay(cronExpression: string): number;
3
+ }
4
+ export { CronHandler };
@@ -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;
@@ -50,6 +50,7 @@ type HotMeshEngine = {
50
50
  redis?: RedisConfig;
51
51
  reclaimDelay?: number;
52
52
  reclaimCount?: number;
53
+ readonly?: boolean;
53
54
  };
54
55
  type HotMeshWorker = {
55
56
  topic: string;
@@ -46,13 +46,17 @@ interface MeshCallCronParams {
46
46
  topic: string;
47
47
  redis: RedisConfig;
48
48
  args: any[];
49
- callback: (...args: any[]) => any;
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, };
@@ -71,4 +71,5 @@ export type StreamConfig = {
71
71
  topic?: string;
72
72
  reclaimDelay?: number;
73
73
  reclaimCount?: number;
74
+ readonly?: boolean;
74
75
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.2.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
@@ -62,6 +62,7 @@ type HotMeshEngine = {
62
62
  redis?: RedisConfig;
63
63
  reclaimDelay?: number; //milliseconds
64
64
  reclaimCount?: number;
65
+ readonly?: boolean; //if true, the engine will not route stream messages
65
66
  };
66
67
 
67
68
  type HotMeshWorker = {
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: (...args: any[]) => any;
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
  };