@hotmeshio/hotmesh 0.3.32 → 0.4.1
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 +128 -823
- package/build/index.d.ts +9 -9
- package/build/index.js +10 -10
- package/build/modules/enums.d.ts +23 -23
- package/build/modules/enums.js +26 -26
- package/build/modules/errors.d.ts +16 -16
- package/build/modules/errors.js +28 -28
- package/build/modules/key.js +190 -1
- package/build/modules/utils.js +374 -1
- package/build/package.json +22 -21
- package/build/services/activities/activity.js +549 -1
- package/build/services/activities/await.js +114 -1
- package/build/services/activities/cycle.js +112 -1
- package/build/services/activities/hook.js +168 -1
- package/build/services/activities/index.js +20 -1
- package/build/services/activities/interrupt.js +158 -1
- package/build/services/activities/signal.js +134 -1
- package/build/services/activities/trigger.js +246 -1
- package/build/services/activities/worker.js +106 -1
- package/build/services/collator/index.js +293 -1
- package/build/services/compiler/deployer.js +488 -1
- package/build/services/compiler/index.js +112 -1
- package/build/services/compiler/validator.js +147 -1
- package/build/services/engine/index.js +761 -1
- package/build/services/exporter/index.js +126 -1
- package/build/services/hotmesh/index.d.ts +160 -17
- package/build/services/hotmesh/index.js +160 -17
- package/build/services/mapper/index.js +81 -1
- package/build/services/{meshflow → memflow}/client.d.ts +3 -3
- package/build/services/{meshflow → memflow}/client.js +17 -16
- package/build/services/{meshflow → memflow}/connection.d.ts +2 -2
- package/build/services/{meshflow → memflow}/connection.js +1 -1
- package/build/services/memflow/context.d.ts +143 -0
- package/build/services/memflow/context.js +299 -0
- package/build/services/{meshflow → memflow}/exporter.d.ts +6 -6
- package/build/services/memflow/exporter.js +215 -0
- package/build/services/{meshflow → memflow}/handle.d.ts +4 -4
- package/build/services/{meshflow → memflow}/handle.js +2 -2
- package/build/services/{meshflow → memflow}/index.d.ts +18 -13
- package/build/services/{meshflow → memflow}/index.js +26 -21
- package/build/services/{meshflow → memflow}/schemas/factory.d.ts +4 -4
- package/build/services/{meshflow → memflow}/schemas/factory.js +5 -5
- package/build/services/{meshflow → memflow}/search.d.ts +1 -1
- package/build/services/{meshflow → memflow}/search.js +4 -4
- package/build/services/{meshflow → memflow}/worker.d.ts +5 -5
- package/build/services/{meshflow → memflow}/worker.js +24 -24
- package/build/services/memflow/workflow/common.d.ts +20 -0
- package/build/services/memflow/workflow/common.js +47 -0
- package/build/services/memflow/workflow/contextMethods.d.ts +14 -0
- package/build/services/memflow/workflow/contextMethods.js +33 -0
- package/build/services/{meshflow → memflow}/workflow/execChild.js +12 -12
- package/build/services/memflow/workflow/execHook.d.ts +65 -0
- package/build/services/memflow/workflow/execHook.js +73 -0
- package/build/services/{meshflow → memflow}/workflow/hook.js +19 -3
- package/build/services/{meshflow → memflow}/workflow/index.d.ts +7 -3
- package/build/services/{meshflow → memflow}/workflow/index.js +7 -3
- package/build/services/{meshflow → memflow}/workflow/proxyActivities.d.ts +2 -2
- package/build/services/{meshflow → memflow}/workflow/proxyActivities.js +8 -8
- package/build/services/{meshflow → memflow}/workflow/sleepFor.js +2 -2
- package/build/services/{meshflow → memflow}/workflow/waitFor.js +2 -2
- package/build/services/meshdata/index.d.ts +24 -24
- package/build/services/meshdata/index.js +40 -40
- package/build/services/pipe/functions/array.js +74 -1
- package/build/services/pipe/functions/bitwise.js +24 -1
- package/build/services/pipe/functions/conditional.js +36 -1
- package/build/services/pipe/functions/cron.js +40 -1
- package/build/services/pipe/functions/date.js +171 -1
- package/build/services/pipe/functions/index.js +30 -1
- package/build/services/pipe/functions/json.js +12 -1
- package/build/services/pipe/functions/logical.js +12 -1
- package/build/services/pipe/functions/math.js +184 -1
- package/build/services/pipe/functions/number.js +60 -1
- package/build/services/pipe/functions/object.js +81 -1
- package/build/services/pipe/functions/string.js +69 -1
- package/build/services/pipe/functions/symbol.js +33 -1
- package/build/services/pipe/functions/unary.js +18 -1
- package/build/services/pipe/index.js +242 -1
- package/build/services/quorum/index.js +263 -1
- package/build/services/reporter/index.js +348 -1
- package/build/services/router/config/index.d.ts +11 -0
- package/build/services/router/config/index.js +36 -0
- package/build/services/router/consumption/index.d.ts +34 -0
- package/build/services/router/consumption/index.js +395 -0
- package/build/services/router/error-handling/index.d.ts +8 -0
- package/build/services/router/error-handling/index.js +98 -0
- package/build/services/router/index.d.ts +13 -16
- package/build/services/router/index.js +121 -1
- package/build/services/router/lifecycle/index.d.ts +27 -0
- package/build/services/router/lifecycle/index.js +80 -0
- package/build/services/router/telemetry/index.d.ts +11 -0
- package/build/services/router/telemetry/index.js +32 -0
- package/build/services/router/throttling/index.d.ts +23 -0
- package/build/services/router/throttling/index.js +76 -0
- package/build/services/search/index.d.ts +2 -1
- package/build/services/search/providers/postgres/postgres.d.ts +2 -1
- package/build/services/search/providers/postgres/postgres.js +149 -1
- package/build/services/search/providers/redis/ioredis.d.ts +1 -0
- package/build/services/search/providers/redis/ioredis.js +121 -1
- package/build/services/search/providers/redis/redis.d.ts +1 -0
- package/build/services/search/providers/redis/redis.js +134 -1
- package/build/services/serializer/index.js +282 -1
- package/build/services/store/providers/postgres/kvsql.d.ts +1 -1
- package/build/services/store/providers/postgres/kvsql.js +198 -1
- package/build/services/store/providers/postgres/kvtables.js +441 -1
- package/build/services/store/providers/postgres/kvtransaction.js +248 -1
- package/build/services/store/providers/postgres/kvtypes/hash.d.ts +1 -1
- package/build/services/store/providers/postgres/kvtypes/hash.js +1287 -1
- package/build/services/store/providers/postgres/kvtypes/list.js +194 -1
- package/build/services/store/providers/postgres/kvtypes/string.js +115 -1
- package/build/services/store/providers/postgres/kvtypes/zset.js +214 -1
- package/build/services/store/providers/postgres/postgres.js +1036 -1
- package/build/services/store/providers/redis/_base.js +980 -1
- package/build/services/store/providers/redis/ioredis.js +180 -1
- package/build/services/store/providers/redis/redis.js +199 -1
- package/build/services/store/providers/store-initializable.js +2 -1
- package/build/services/stream/index.d.ts +5 -0
- package/build/services/stream/providers/nats/nats.d.ts +1 -0
- package/build/services/stream/providers/nats/nats.js +225 -1
- package/build/services/stream/providers/postgres/kvtables.d.ts +1 -0
- package/build/services/stream/providers/postgres/kvtables.js +146 -1
- package/build/services/stream/providers/postgres/postgres.d.ts +19 -0
- package/build/services/stream/providers/postgres/postgres.js +519 -1
- package/build/services/stream/providers/redis/ioredis.d.ts +1 -0
- package/build/services/stream/providers/redis/ioredis.js +272 -1
- package/build/services/stream/providers/redis/redis.d.ts +1 -0
- package/build/services/stream/providers/redis/redis.js +305 -1
- package/build/services/stream/providers/stream-initializable.js +2 -1
- package/build/services/sub/providers/nats/nats.js +105 -1
- package/build/services/sub/providers/postgres/postgres.js +92 -1
- package/build/services/sub/providers/redis/ioredis.js +81 -1
- package/build/services/sub/providers/redis/redis.js +72 -1
- package/build/services/task/index.js +206 -1
- package/build/services/telemetry/index.js +306 -1
- package/build/services/worker/index.js +197 -1
- package/build/types/error.d.ts +5 -5
- package/build/types/exporter.d.ts +1 -1
- package/build/types/index.d.ts +3 -3
- package/build/types/manifest.d.ts +2 -2
- package/build/types/{meshflow.d.ts → memflow.d.ts} +15 -15
- package/build/types/meshdata.d.ts +3 -3
- package/build/types/postgres.d.ts +7 -0
- package/build/types/stream.d.ts +3 -0
- package/index.ts +11 -11
- package/package.json +22 -21
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- package/build/services/meshflow/exporter.js +0 -1
- package/build/services/meshflow/workflow/common.d.ts +0 -18
- package/build/services/meshflow/workflow/common.js +0 -45
- package/typedoc.json +0 -46
- package/types/activity.ts +0 -268
- package/types/app.ts +0 -20
- package/types/async.ts +0 -6
- package/types/cache.ts +0 -1
- package/types/collator.ts +0 -9
- package/types/error.ts +0 -56
- package/types/exporter.ts +0 -102
- package/types/hook.ts +0 -44
- package/types/hotmesh.ts +0 -314
- package/types/index.ts +0 -306
- package/types/job.ts +0 -233
- package/types/logger.ts +0 -8
- package/types/manifest.ts +0 -70
- package/types/map.ts +0 -5
- package/types/meshcall.ts +0 -235
- package/types/meshdata.ts +0 -278
- package/types/meshflow.ts +0 -645
- package/types/ms.d.ts +0 -7
- package/types/nats.ts +0 -270
- package/types/pipe.ts +0 -90
- package/types/postgres.ts +0 -105
- package/types/provider.ts +0 -161
- package/types/quorum.ts +0 -167
- package/types/redis.ts +0 -404
- package/types/serializer.ts +0 -40
- package/types/stats.ts +0 -117
- package/types/stream.ts +0 -227
- package/types/task.ts +0 -7
- package/types/telemetry.ts +0 -16
- package/types/transition.ts +0 -20
- /package/build/services/{meshflow → memflow}/workflow/all.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/all.js +0 -0
- /package/build/services/{meshflow → memflow}/workflow/context.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/context.js +0 -0
- /package/build/services/{meshflow → memflow}/workflow/didRun.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/didRun.js +0 -0
- /package/build/services/{meshflow → memflow}/workflow/emit.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/emit.js +0 -0
- /package/build/services/{meshflow → memflow}/workflow/enrich.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/enrich.js +0 -0
- /package/build/services/{meshflow → memflow}/workflow/execChild.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/hook.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/interrupt.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/interrupt.js +0 -0
- /package/build/services/{meshflow → memflow}/workflow/isSideEffectAllowed.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/isSideEffectAllowed.js +0 -0
- /package/build/services/{meshflow → memflow}/workflow/random.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/random.js +0 -0
- /package/build/services/{meshflow → memflow}/workflow/searchMethods.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/searchMethods.js +0 -0
- /package/build/services/{meshflow → memflow}/workflow/signal.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/signal.js +0 -0
- /package/build/services/{meshflow → memflow}/workflow/sleepFor.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/trace.d.ts +0 -0
- /package/build/services/{meshflow → memflow}/workflow/trace.js +0 -0
- /package/build/services/{meshflow → memflow}/workflow/waitFor.d.ts +0 -0
- /package/build/types/{meshflow.js → memflow.js} +0 -0
|
@@ -1 +1,105 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NatsSubService = void 0;
|
|
4
|
+
const key_1 = require("../../../../modules/key");
|
|
5
|
+
const index_1 = require("../../index");
|
|
6
|
+
class NatsSubService extends index_1.SubService {
|
|
7
|
+
constructor(eventClient, storeClient) {
|
|
8
|
+
super(eventClient, storeClient);
|
|
9
|
+
this.subscriptions = new Map();
|
|
10
|
+
}
|
|
11
|
+
async init(namespace = key_1.HMNS, appId, engineId, logger) {
|
|
12
|
+
this.namespace = namespace;
|
|
13
|
+
this.logger = logger;
|
|
14
|
+
this.appId = appId;
|
|
15
|
+
this.engineId = engineId;
|
|
16
|
+
}
|
|
17
|
+
transact() {
|
|
18
|
+
// NATS does not support transactions like Redis.
|
|
19
|
+
// Return an empty object or throw an error if not supported.
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
mintKey(type, params) {
|
|
23
|
+
if (!this.namespace)
|
|
24
|
+
throw new Error('namespace not set');
|
|
25
|
+
return key_1.KeyService.mintKey(this.namespace, type, params)
|
|
26
|
+
.replace(/:/g, '.')
|
|
27
|
+
.replace(/\.$/g, '.X');
|
|
28
|
+
}
|
|
29
|
+
async subscribe(keyType, callback, appId, topic) {
|
|
30
|
+
const subject = this.mintKey(keyType, { appId, engineId: topic });
|
|
31
|
+
const subscription = this.eventClient.subscribe(subject);
|
|
32
|
+
this.subscriptions.set(subject, subscription);
|
|
33
|
+
this.logger.debug(`nats-subscribe ${subject}`);
|
|
34
|
+
(async () => {
|
|
35
|
+
for await (const msg of subscription) {
|
|
36
|
+
try {
|
|
37
|
+
const payload = JSON.parse(msg.data.toString());
|
|
38
|
+
callback(subject, payload);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
this.logger.error(`nats-subscribe-message-parse-error: ${msg.data.toString()}`, e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
})().catch((err) => {
|
|
45
|
+
this.logger.error(`nats-subscribe-error ${subject}`, err);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async unsubscribe(keyType, appId, engineId) {
|
|
49
|
+
const subject = this.mintKey(keyType, { appId, engineId });
|
|
50
|
+
const subscription = this.subscriptions.get(subject);
|
|
51
|
+
if (subscription) {
|
|
52
|
+
subscription.unsubscribe();
|
|
53
|
+
this.subscriptions.delete(subject);
|
|
54
|
+
this.logger.debug(`nats-unsubscribe ${subject}`);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
this.logger.warn(`nats-unsubscribe-error ${subject}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async psubscribe(keyType, callback, appId, topic) {
|
|
61
|
+
const subject = this.mintKey(keyType, { appId, engineId: topic });
|
|
62
|
+
const subscription = this.eventClient.subscribe(subject);
|
|
63
|
+
this.subscriptions.set(subject, subscription);
|
|
64
|
+
this.logger.debug(`nats-psubscribe ${subject}`);
|
|
65
|
+
(async () => {
|
|
66
|
+
for await (const msg of subscription) {
|
|
67
|
+
try {
|
|
68
|
+
const payload = JSON.parse(msg.data.toString());
|
|
69
|
+
callback(msg.subject, payload);
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
this.logger.error(`nats-parse-psubscription-message-error ${msg.data.toString()}`, e);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
})().catch((err) => {
|
|
76
|
+
this.logger.error(`nats-pattern-psubscription-error ${subject}`, err);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async punsubscribe(keyType, appId, topic) {
|
|
80
|
+
const subject = this.mintKey(keyType, { appId, engineId: topic });
|
|
81
|
+
const subscription = this.subscriptions.get(subject);
|
|
82
|
+
if (subscription) {
|
|
83
|
+
subscription.unsubscribe();
|
|
84
|
+
this.subscriptions.delete(subject);
|
|
85
|
+
this.logger.debug(`nats-punsubscribe ${subject}`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.logger.warn(`nats-punsubscribe-error ${subject}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async publish(keyType, message, appId, topic) {
|
|
92
|
+
const subject = this.mintKey(keyType, { appId, engineId: topic });
|
|
93
|
+
try {
|
|
94
|
+
// Use the storeClient for publishing if necessary
|
|
95
|
+
this.storeClient.publish(subject, JSON.stringify(message));
|
|
96
|
+
this.logger.debug(`nats-publish ${subject}`);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
this.logger.error(`nats-publish-error ${subject}`, err);
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.NatsSubService = NatsSubService;
|
|
@@ -1 +1,92 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PostgresSubService = void 0;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
const key_1 = require("../../../../modules/key");
|
|
9
|
+
const index_1 = require("../../index");
|
|
10
|
+
class PostgresSubService extends index_1.SubService {
|
|
11
|
+
constructor(eventClient, storeClient) {
|
|
12
|
+
super(eventClient, storeClient);
|
|
13
|
+
}
|
|
14
|
+
async init(namespace = key_1.HMNS, appId, engineId, logger) {
|
|
15
|
+
this.namespace = namespace;
|
|
16
|
+
this.logger = logger;
|
|
17
|
+
this.appId = appId;
|
|
18
|
+
this.engineId = engineId;
|
|
19
|
+
}
|
|
20
|
+
transact() {
|
|
21
|
+
throw new Error('Transactions are not supported in lightweight pub/sub');
|
|
22
|
+
}
|
|
23
|
+
mintKey(type, params) {
|
|
24
|
+
if (!this.namespace)
|
|
25
|
+
throw new Error('Namespace not set');
|
|
26
|
+
return key_1.KeyService.mintKey(this.namespace, type, params);
|
|
27
|
+
}
|
|
28
|
+
mintSafeKey(type, params) {
|
|
29
|
+
const originalKey = this.mintKey(type, params);
|
|
30
|
+
if (originalKey.length <= 63) {
|
|
31
|
+
return [originalKey, originalKey];
|
|
32
|
+
}
|
|
33
|
+
const { appId = '', engineId = '' } = params;
|
|
34
|
+
const baseKey = `${this.namespace}:${appId}:${type}`;
|
|
35
|
+
const maxHashLength = 63 - baseKey.length - 1; // Reserve space for `:` delimiter
|
|
36
|
+
const engineIdHash = crypto_1.default
|
|
37
|
+
.createHash('sha256')
|
|
38
|
+
.update(engineId)
|
|
39
|
+
.digest('hex')
|
|
40
|
+
.substring(0, maxHashLength);
|
|
41
|
+
const safeKey = `${baseKey}:${engineIdHash}`;
|
|
42
|
+
return [originalKey, safeKey];
|
|
43
|
+
}
|
|
44
|
+
async subscribe(keyType, callback, appId, topic) {
|
|
45
|
+
const [originalKey, safeKey] = this.mintSafeKey(keyType, {
|
|
46
|
+
appId,
|
|
47
|
+
engineId: topic,
|
|
48
|
+
});
|
|
49
|
+
// Start listening to the safe topic
|
|
50
|
+
await this.eventClient.query(`LISTEN "${safeKey}"`);
|
|
51
|
+
this.logger.debug(`postgres-subscribe`, { originalKey, safeKey });
|
|
52
|
+
// Set up the notification handler
|
|
53
|
+
this.eventClient.on('notification', (msg) => {
|
|
54
|
+
if (msg.channel === safeKey) {
|
|
55
|
+
try {
|
|
56
|
+
const payload = JSON.parse(msg.payload || '{}');
|
|
57
|
+
callback(safeKey, payload);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
this.logger.error(`Error parsing message for topic ${safeKey}:`, err);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async unsubscribe(keyType, appId, topic) {
|
|
66
|
+
const [originalKey, safeKey] = this.mintSafeKey(keyType, {
|
|
67
|
+
appId,
|
|
68
|
+
engineId: topic,
|
|
69
|
+
});
|
|
70
|
+
// Stop listening to the safe topic
|
|
71
|
+
await this.eventClient.query(`UNLISTEN "${safeKey}"`);
|
|
72
|
+
this.logger.debug(`postgres-subscribe`, { originalKey, safeKey });
|
|
73
|
+
}
|
|
74
|
+
async publish(keyType, message, appId, topic) {
|
|
75
|
+
const [originalKey, safeKey] = this.mintSafeKey(keyType, {
|
|
76
|
+
appId,
|
|
77
|
+
engineId: topic,
|
|
78
|
+
});
|
|
79
|
+
// Publish the message using the safe topic
|
|
80
|
+
const payload = JSON.stringify(message).replace(/'/g, "''");
|
|
81
|
+
await this.storeClient.query(`NOTIFY "${safeKey}", '${payload}'`);
|
|
82
|
+
this.logger.debug(`postgres-publish`, { originalKey, safeKey });
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
async psubscribe() {
|
|
86
|
+
throw new Error('Pattern subscriptions are not supported in PostgreSQL');
|
|
87
|
+
}
|
|
88
|
+
async punsubscribe() {
|
|
89
|
+
throw new Error('Pattern subscriptions are not supported in PostgreSQL');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.PostgresSubService = PostgresSubService;
|
|
@@ -1 +1,81 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IORedisSubService = void 0;
|
|
4
|
+
const key_1 = require("../../../../modules/key");
|
|
5
|
+
const index_1 = require("../../index");
|
|
6
|
+
class IORedisSubService extends index_1.SubService {
|
|
7
|
+
constructor(eventClient, storeClient) {
|
|
8
|
+
super(eventClient, storeClient);
|
|
9
|
+
}
|
|
10
|
+
async init(namespace = key_1.HMNS, appId, engineId, logger) {
|
|
11
|
+
this.namespace = namespace;
|
|
12
|
+
this.logger = logger;
|
|
13
|
+
this.appId = appId;
|
|
14
|
+
this.engineId = engineId;
|
|
15
|
+
}
|
|
16
|
+
transact() {
|
|
17
|
+
return this.eventClient.multi();
|
|
18
|
+
}
|
|
19
|
+
mintKey(type, params) {
|
|
20
|
+
if (!this.namespace)
|
|
21
|
+
throw new Error('namespace not set');
|
|
22
|
+
return key_1.KeyService.mintKey(this.namespace, type, params);
|
|
23
|
+
}
|
|
24
|
+
async subscribe(keyType, callback, appId, engineId) {
|
|
25
|
+
const self = this;
|
|
26
|
+
const topic = this.mintKey(keyType, { appId, engineId });
|
|
27
|
+
await this.eventClient.subscribe(topic, (err) => {
|
|
28
|
+
if (err) {
|
|
29
|
+
self.logger.error(`Error subscribing to: ${topic}`, err);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
this.eventClient.on('message', (channel, message) => {
|
|
33
|
+
if (channel === topic) {
|
|
34
|
+
try {
|
|
35
|
+
const payload = JSON.parse(message);
|
|
36
|
+
callback(topic, payload);
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
self.logger.error(`Error parsing message: ${message}`, e);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async unsubscribe(keyType, appId, engineId) {
|
|
45
|
+
const topic = this.mintKey(keyType, { appId, engineId });
|
|
46
|
+
await this.eventClient.unsubscribe(topic);
|
|
47
|
+
}
|
|
48
|
+
async psubscribe(keyType, callback, appId, engineId) {
|
|
49
|
+
const self = this;
|
|
50
|
+
const topic = this.mintKey(keyType, { appId, engineId });
|
|
51
|
+
await this.eventClient.psubscribe(topic, (err) => {
|
|
52
|
+
if (err) {
|
|
53
|
+
self.logger.error(`Error subscribing to: ${topic}`, err);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
this.eventClient.on('pmessage', (pattern, channel, message) => {
|
|
57
|
+
if (pattern === topic) {
|
|
58
|
+
try {
|
|
59
|
+
const payload = JSON.parse(message);
|
|
60
|
+
callback(channel, payload);
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
self.logger.error(`Error parsing message: ${message}`, e);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async punsubscribe(keyType, appId, engineId) {
|
|
69
|
+
const topic = this.mintKey(keyType, { appId, engineId });
|
|
70
|
+
await this.eventClient.punsubscribe(topic);
|
|
71
|
+
}
|
|
72
|
+
async publish(keyType, message, appId, engineId) {
|
|
73
|
+
const topic = this.mintKey(keyType, { appId, engineId });
|
|
74
|
+
//NOTE: `storeClient.publish` is used,
|
|
75
|
+
// because a Redis connection with subscriptions
|
|
76
|
+
// may not publish (is read only).
|
|
77
|
+
const status = await this.storeClient.publish(topic, JSON.stringify(message));
|
|
78
|
+
return status === 1;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.IORedisSubService = IORedisSubService;
|
|
@@ -1 +1,72 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RedisSubService = void 0;
|
|
4
|
+
const key_1 = require("../../../../modules/key");
|
|
5
|
+
const index_1 = require("../../index");
|
|
6
|
+
class RedisSubService extends index_1.SubService {
|
|
7
|
+
constructor(eventClient, storeClient) {
|
|
8
|
+
super(eventClient, storeClient);
|
|
9
|
+
}
|
|
10
|
+
async init(namespace = key_1.HMNS, appId, engineId, logger) {
|
|
11
|
+
this.namespace = namespace;
|
|
12
|
+
this.logger = logger;
|
|
13
|
+
this.appId = appId;
|
|
14
|
+
this.engineId = engineId;
|
|
15
|
+
}
|
|
16
|
+
transact() {
|
|
17
|
+
const multi = this.eventClient.multi();
|
|
18
|
+
return multi;
|
|
19
|
+
}
|
|
20
|
+
mintKey(type, params) {
|
|
21
|
+
if (!this.namespace)
|
|
22
|
+
throw new Error('namespace not set');
|
|
23
|
+
return key_1.KeyService.mintKey(this.namespace, type, params);
|
|
24
|
+
}
|
|
25
|
+
async subscribe(keyType, callback, appId, engineId) {
|
|
26
|
+
if (this.eventClient) {
|
|
27
|
+
const self = this;
|
|
28
|
+
const topic = this.mintKey(keyType, { appId, engineId });
|
|
29
|
+
await this.eventClient.subscribe(topic, (message) => {
|
|
30
|
+
try {
|
|
31
|
+
const payload = JSON.parse(message);
|
|
32
|
+
callback(topic, payload);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
self.logger.error(`Error parsing message: ${message}`, e);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async unsubscribe(keyType, appId, engineId) {
|
|
41
|
+
const topic = this.mintKey(keyType, { appId, engineId });
|
|
42
|
+
await this.eventClient.unsubscribe(topic);
|
|
43
|
+
}
|
|
44
|
+
async psubscribe(keyType, callback, appId, engineId) {
|
|
45
|
+
if (this.eventClient) {
|
|
46
|
+
const self = this;
|
|
47
|
+
const topic = this.mintKey(keyType, { appId, engineId });
|
|
48
|
+
await this.eventClient.pSubscribe(topic, (message, channel) => {
|
|
49
|
+
try {
|
|
50
|
+
const payload = JSON.parse(message);
|
|
51
|
+
callback(channel, payload);
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
self.logger.error(`Error parsing message: ${message}`, e);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async punsubscribe(keyType, appId, engineId) {
|
|
60
|
+
const topic = this.mintKey(keyType, { appId, engineId });
|
|
61
|
+
await this.eventClient.pUnsubscribe(topic);
|
|
62
|
+
}
|
|
63
|
+
async publish(keyType, message, appId, engineId) {
|
|
64
|
+
const topic = this.mintKey(keyType, { appId, engineId });
|
|
65
|
+
//NOTE: `storeClient.publish` is used,
|
|
66
|
+
// because a Redis connection with subscriptions
|
|
67
|
+
// may not publish (is read only).
|
|
68
|
+
const status = await this.storeClient.publish(topic, JSON.stringify(message));
|
|
69
|
+
return status > 0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.RedisSubService = RedisSubService;
|
|
@@ -1 +1,206 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TaskService = void 0;
|
|
4
|
+
const enums_1 = require("../../modules/enums");
|
|
5
|
+
const utils_1 = require("../../modules/utils");
|
|
6
|
+
const pipe_1 = require("../pipe");
|
|
7
|
+
const hotmesh_1 = require("../../types/hotmesh");
|
|
8
|
+
const key_1 = require("../../modules/key");
|
|
9
|
+
class TaskService {
|
|
10
|
+
constructor(store, logger) {
|
|
11
|
+
this.cleanupTimeout = null;
|
|
12
|
+
this.isScout = false;
|
|
13
|
+
this.errorCount = 0;
|
|
14
|
+
this.logger = logger;
|
|
15
|
+
this.store = store;
|
|
16
|
+
}
|
|
17
|
+
async processWebHooks(hookEventCallback) {
|
|
18
|
+
const workItemKey = await this.store.getActiveTaskQueue();
|
|
19
|
+
if (workItemKey) {
|
|
20
|
+
const [topic, sourceKey, scrub, ...sdata] = workItemKey.split(key_1.WEBSEP);
|
|
21
|
+
const data = JSON.parse(sdata.join(key_1.WEBSEP));
|
|
22
|
+
const destinationKey = `${sourceKey}:processed`;
|
|
23
|
+
const jobId = await this.store.processTaskQueue(sourceKey, destinationKey);
|
|
24
|
+
if (jobId) {
|
|
25
|
+
//todo: don't use 'id', make configurable using hook rule
|
|
26
|
+
await hookEventCallback(topic, { ...data, id: jobId });
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
await this.store.deleteProcessedTaskQueue(workItemKey, sourceKey, destinationKey, scrub === 'true');
|
|
30
|
+
}
|
|
31
|
+
setImmediate(() => this.processWebHooks(hookEventCallback));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async enqueueWorkItems(keys) {
|
|
35
|
+
await this.store.addTaskQueues(keys);
|
|
36
|
+
}
|
|
37
|
+
async registerJobForCleanup(jobId, inSeconds = enums_1.HMSH_EXPIRE_DURATION, options) {
|
|
38
|
+
if (inSeconds > 0) {
|
|
39
|
+
await this.store.expireJob(jobId, inSeconds);
|
|
40
|
+
// const fromNow = Date.now() + inSeconds * 1000;
|
|
41
|
+
// const fidelityMS = HMSH_FIDELITY_SECONDS * 1000;
|
|
42
|
+
// const timeSlot = Math.floor(fromNow / fidelityMS) * fidelityMS;
|
|
43
|
+
// await this.store.registerDependenciesForCleanup(jobId, timeSlot, options);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async registerTimeHook(jobId, gId, activityId, type, inSeconds = enums_1.HMSH_FIDELITY_SECONDS, dad, transaction) {
|
|
47
|
+
const fromNow = Date.now() + inSeconds * 1000;
|
|
48
|
+
const fidelityMS = enums_1.HMSH_FIDELITY_SECONDS * 1000;
|
|
49
|
+
const awakenTimeSlot = Math.floor(fromNow / fidelityMS) * fidelityMS;
|
|
50
|
+
await this.store.registerTimeHook(jobId, gId, activityId, type, awakenTimeSlot, dad, transaction);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Should this engine instance play the role of 'scout' on behalf
|
|
54
|
+
* of the entire quorum? The scout role is responsible for processing
|
|
55
|
+
* task lists on behalf of the collective.
|
|
56
|
+
*/
|
|
57
|
+
async shouldScout() {
|
|
58
|
+
const wasScout = this.isScout;
|
|
59
|
+
const isScout = wasScout || (this.isScout = await this.store.reserveScoutRole('time'));
|
|
60
|
+
if (isScout) {
|
|
61
|
+
if (!wasScout) {
|
|
62
|
+
setTimeout(() => {
|
|
63
|
+
this.isScout = false;
|
|
64
|
+
}, enums_1.HMSH_SCOUT_INTERVAL_SECONDS * 1000);
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Callback handler that takes an item from a work list and
|
|
72
|
+
* processes according to its type
|
|
73
|
+
*/
|
|
74
|
+
async processTimeHooks(timeEventCallback, listKey) {
|
|
75
|
+
if (await this.shouldScout()) {
|
|
76
|
+
try {
|
|
77
|
+
const workListTask = await this.store.getNextTask(listKey);
|
|
78
|
+
if (Array.isArray(workListTask)) {
|
|
79
|
+
const [listKey, target, gId, activityId, type] = workListTask;
|
|
80
|
+
if (type === 'child') {
|
|
81
|
+
//continue; this child is listed here for convenience, but
|
|
82
|
+
// will be expired by an origin ancestor and is listed there
|
|
83
|
+
}
|
|
84
|
+
else if (type === 'delist') {
|
|
85
|
+
//delist the signalKey (target)
|
|
86
|
+
const key = this.store.mintKey(hotmesh_1.KeyType.SIGNALS, {
|
|
87
|
+
appId: this.store.appId,
|
|
88
|
+
});
|
|
89
|
+
await this.store.delistSignalKey(key, target);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
//awaken/expire/interrupt
|
|
93
|
+
await timeEventCallback(target, gId, activityId, type);
|
|
94
|
+
}
|
|
95
|
+
await (0, utils_1.sleepFor)(0);
|
|
96
|
+
this.errorCount = 0;
|
|
97
|
+
this.processTimeHooks(timeEventCallback, listKey);
|
|
98
|
+
}
|
|
99
|
+
else if (workListTask) {
|
|
100
|
+
//a worklist was just emptied; try again immediately
|
|
101
|
+
await (0, utils_1.sleepFor)(0);
|
|
102
|
+
this.errorCount = 0;
|
|
103
|
+
this.processTimeHooks(timeEventCallback);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
//no worklists exist; sleep before checking
|
|
107
|
+
const sleep = (0, utils_1.XSleepFor)(enums_1.HMSH_FIDELITY_SECONDS * 1000);
|
|
108
|
+
this.cleanupTimeout = sleep.timerId;
|
|
109
|
+
await sleep.promise;
|
|
110
|
+
this.errorCount = 0;
|
|
111
|
+
this.processTimeHooks(timeEventCallback);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
//most common reasons: deleted job not found; container stopping; test stopping
|
|
116
|
+
//less common: redis/cluster down; retry with fallback (5s max main reassignment)
|
|
117
|
+
this.logger.warn('task-process-timehooks-error', err);
|
|
118
|
+
await (0, utils_1.sleepFor)(1000 * this.errorCount++);
|
|
119
|
+
if (this.errorCount < 5) {
|
|
120
|
+
this.processTimeHooks(timeEventCallback);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
//didn't get the scout role; try again in 'one-ish' minutes
|
|
126
|
+
const sleep = (0, utils_1.XSleepFor)(enums_1.HMSH_SCOUT_INTERVAL_SECONDS * 1000 * 2 * Math.random());
|
|
127
|
+
this.cleanupTimeout = sleep.timerId;
|
|
128
|
+
await sleep.promise;
|
|
129
|
+
this.processTimeHooks(timeEventCallback);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
cancelCleanup() {
|
|
133
|
+
if (this.cleanupTimeout !== undefined) {
|
|
134
|
+
clearTimeout(this.cleanupTimeout);
|
|
135
|
+
this.cleanupTimeout = undefined;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async getHookRule(topic) {
|
|
139
|
+
const rules = await this.store.getHookRules();
|
|
140
|
+
return rules?.[topic]?.[0];
|
|
141
|
+
}
|
|
142
|
+
async registerWebHook(topic, context, dad, expire, transaction) {
|
|
143
|
+
const hookRule = await this.getHookRule(topic);
|
|
144
|
+
if (hookRule) {
|
|
145
|
+
const mapExpression = hookRule.conditions.match[0].expected;
|
|
146
|
+
const resolved = pipe_1.Pipe.resolve(mapExpression, context);
|
|
147
|
+
const jobId = context.metadata.jid;
|
|
148
|
+
const gId = context.metadata.gid;
|
|
149
|
+
const activityId = hookRule.to;
|
|
150
|
+
//composite keys are used to fully describe the task target
|
|
151
|
+
const compositeJobKey = [activityId, dad, gId, jobId].join(key_1.WEBSEP);
|
|
152
|
+
const hook = {
|
|
153
|
+
topic,
|
|
154
|
+
resolved,
|
|
155
|
+
jobId: compositeJobKey,
|
|
156
|
+
expire,
|
|
157
|
+
};
|
|
158
|
+
await this.store.setHookSignal(hook, transaction);
|
|
159
|
+
return jobId;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
throw new Error('signaler.registerWebHook:error: hook rule not found');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async processWebHookSignal(topic, data) {
|
|
166
|
+
const hookRule = await this.getHookRule(topic);
|
|
167
|
+
if (hookRule) {
|
|
168
|
+
//NOTE: both formats are supported by the mapping engine:
|
|
169
|
+
// `$self.hook.data` OR `$hook.data`
|
|
170
|
+
const context = { $self: { hook: { data } }, $hook: { data } };
|
|
171
|
+
const mapExpression = hookRule.conditions.match[0].actual;
|
|
172
|
+
const resolved = pipe_1.Pipe.resolve(mapExpression, context);
|
|
173
|
+
const hookSignalId = await this.store.getHookSignal(topic, resolved);
|
|
174
|
+
if (!hookSignalId) {
|
|
175
|
+
//messages can be double-processed; not an issue; return `undefined`
|
|
176
|
+
//users can also provide a bogus topic; not an issue; return `undefined`
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
//`aid` is part of composite key, but the hook `topic` is its public interface;
|
|
180
|
+
// this means that a new version of the graph can be deployed and the
|
|
181
|
+
// topic can be re-mapped to a different activity id. Outside callers
|
|
182
|
+
// can adhere to the unchanged contract (calling the same topic),
|
|
183
|
+
// while the internal system can be updated in real-time as necessary.
|
|
184
|
+
const [_aid, dad, gid, ...jid] = hookSignalId.split(key_1.WEBSEP);
|
|
185
|
+
return [jid.join(key_1.WEBSEP), hookRule.to, dad, gid];
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
throw new Error('signal-not-found');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async deleteWebHookSignal(topic, data) {
|
|
192
|
+
const hookRule = await this.getHookRule(topic);
|
|
193
|
+
if (hookRule) {
|
|
194
|
+
//NOTE: both formats are supported by the mapping engine:
|
|
195
|
+
// `$self.hook.data` OR `$hook.data`
|
|
196
|
+
const context = { $self: { hook: { data } }, $hook: { data } };
|
|
197
|
+
const mapExpression = hookRule.conditions.match[0].actual;
|
|
198
|
+
const resolved = pipe_1.Pipe.resolve(mapExpression, context);
|
|
199
|
+
return await this.store.deleteHookSignal(topic, resolved);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
throw new Error('signaler.process:error: hook rule not found');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
exports.TaskService = TaskService;
|