@hotmeshio/hotmesh 0.0.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/LICENSE +214 -0
- package/README.md +241 -0
- package/build/index.d.ts +4 -0
- package/build/index.js +7 -0
- package/build/modules/errors.d.ts +28 -0
- package/build/modules/errors.js +50 -0
- package/build/modules/key.d.ts +75 -0
- package/build/modules/key.js +116 -0
- package/build/modules/utils.d.ts +34 -0
- package/build/modules/utils.js +173 -0
- package/build/package.json +73 -0
- package/build/services/activities/activity.d.ts +59 -0
- package/build/services/activities/activity.js +396 -0
- package/build/services/activities/await.d.ts +16 -0
- package/build/services/activities/await.js +143 -0
- package/build/services/activities/emit.d.ts +9 -0
- package/build/services/activities/emit.js +13 -0
- package/build/services/activities/index.d.ts +15 -0
- package/build/services/activities/index.js +16 -0
- package/build/services/activities/iterate.d.ts +9 -0
- package/build/services/activities/iterate.js +13 -0
- package/build/services/activities/trigger.d.ts +22 -0
- package/build/services/activities/trigger.js +161 -0
- package/build/services/activities/worker.d.ts +17 -0
- package/build/services/activities/worker.js +164 -0
- package/build/services/collator/index.d.ts +54 -0
- package/build/services/collator/index.js +171 -0
- package/build/services/compiler/deployer.d.ts +35 -0
- package/build/services/compiler/deployer.js +412 -0
- package/build/services/compiler/index.d.ts +30 -0
- package/build/services/compiler/index.js +111 -0
- package/build/services/compiler/validator.d.ts +32 -0
- package/build/services/compiler/validator.js +134 -0
- package/build/services/connector/clients/ioredis.d.ts +13 -0
- package/build/services/connector/clients/ioredis.js +50 -0
- package/build/services/connector/clients/redis.d.ts +13 -0
- package/build/services/connector/clients/redis.js +62 -0
- package/build/services/connector/index.d.ts +5 -0
- package/build/services/connector/index.js +31 -0
- package/build/services/dimension/index.d.ts +29 -0
- package/build/services/dimension/index.js +35 -0
- package/build/services/durable/asyncLocalStorage.d.ts +3 -0
- package/build/services/durable/asyncLocalStorage.js +5 -0
- package/build/services/durable/client.d.ts +15 -0
- package/build/services/durable/client.js +108 -0
- package/build/services/durable/connection.d.ts +4 -0
- package/build/services/durable/connection.js +51 -0
- package/build/services/durable/factory.d.ts +3 -0
- package/build/services/durable/factory.js +123 -0
- package/build/services/durable/handle.d.ts +8 -0
- package/build/services/durable/handle.js +38 -0
- package/build/services/durable/index.d.ts +57 -0
- package/build/services/durable/index.js +58 -0
- package/build/services/durable/native.d.ts +4 -0
- package/build/services/durable/native.js +47 -0
- package/build/services/durable/worker.d.ts +36 -0
- package/build/services/durable/worker.js +266 -0
- package/build/services/durable/workflow.d.ts +6 -0
- package/build/services/durable/workflow.js +135 -0
- package/build/services/engine/index.d.ts +82 -0
- package/build/services/engine/index.js +511 -0
- package/build/services/hotmesh/index.d.ts +45 -0
- package/build/services/hotmesh/index.js +134 -0
- package/build/services/logger/index.d.ts +17 -0
- package/build/services/logger/index.js +73 -0
- package/build/services/mapper/index.d.ts +24 -0
- package/build/services/mapper/index.js +72 -0
- package/build/services/pipe/functions/array.d.ts +24 -0
- package/build/services/pipe/functions/array.js +69 -0
- package/build/services/pipe/functions/bitwise.d.ts +9 -0
- package/build/services/pipe/functions/bitwise.js +24 -0
- package/build/services/pipe/functions/conditional.d.ts +10 -0
- package/build/services/pipe/functions/conditional.js +27 -0
- package/build/services/pipe/functions/date.d.ts +57 -0
- package/build/services/pipe/functions/date.js +167 -0
- package/build/services/pipe/functions/index.d.ts +25 -0
- package/build/services/pipe/functions/index.js +26 -0
- package/build/services/pipe/functions/json.d.ts +5 -0
- package/build/services/pipe/functions/json.js +12 -0
- package/build/services/pipe/functions/math.d.ts +38 -0
- package/build/services/pipe/functions/math.js +111 -0
- package/build/services/pipe/functions/number.d.ts +25 -0
- package/build/services/pipe/functions/number.js +133 -0
- package/build/services/pipe/functions/object.d.ts +22 -0
- package/build/services/pipe/functions/object.js +63 -0
- package/build/services/pipe/functions/string.d.ts +23 -0
- package/build/services/pipe/functions/string.js +69 -0
- package/build/services/pipe/functions/symbol.d.ts +12 -0
- package/build/services/pipe/functions/symbol.js +33 -0
- package/build/services/pipe/functions/unary.d.ts +7 -0
- package/build/services/pipe/functions/unary.js +18 -0
- package/build/services/pipe/index.d.ts +30 -0
- package/build/services/pipe/index.js +128 -0
- package/build/services/quorum/index.d.ts +34 -0
- package/build/services/quorum/index.js +147 -0
- package/build/services/reporter/index.d.ts +47 -0
- package/build/services/reporter/index.js +330 -0
- package/build/services/serializer/index.d.ts +36 -0
- package/build/services/serializer/index.js +222 -0
- package/build/services/signaler/store.d.ts +15 -0
- package/build/services/signaler/store.js +53 -0
- package/build/services/signaler/stream.d.ts +43 -0
- package/build/services/signaler/stream.js +317 -0
- package/build/services/store/cache.d.ts +66 -0
- package/build/services/store/cache.js +127 -0
- package/build/services/store/clients/ioredis.d.ts +27 -0
- package/build/services/store/clients/ioredis.js +96 -0
- package/build/services/store/clients/redis.d.ts +29 -0
- package/build/services/store/clients/redis.js +143 -0
- package/build/services/store/index.d.ts +88 -0
- package/build/services/store/index.js +657 -0
- package/build/services/stream/clients/ioredis.d.ts +23 -0
- package/build/services/stream/clients/ioredis.js +115 -0
- package/build/services/stream/clients/redis.d.ts +23 -0
- package/build/services/stream/clients/redis.js +119 -0
- package/build/services/stream/index.d.ts +21 -0
- package/build/services/stream/index.js +9 -0
- package/build/services/sub/clients/ioredis.d.ts +20 -0
- package/build/services/sub/clients/ioredis.js +72 -0
- package/build/services/sub/clients/redis.d.ts +20 -0
- package/build/services/sub/clients/redis.js +63 -0
- package/build/services/sub/index.d.ts +18 -0
- package/build/services/sub/index.js +9 -0
- package/build/services/task/index.d.ts +18 -0
- package/build/services/task/index.js +73 -0
- package/build/services/telemetry/index.d.ts +49 -0
- package/build/services/telemetry/index.js +223 -0
- package/build/services/worker/index.d.ts +30 -0
- package/build/services/worker/index.js +105 -0
- package/build/types/activity.d.ts +86 -0
- package/build/types/activity.js +2 -0
- package/build/types/app.d.ts +16 -0
- package/build/types/app.js +2 -0
- package/build/types/async.d.ts +5 -0
- package/build/types/async.js +2 -0
- package/build/types/cache.d.ts +1 -0
- package/build/types/cache.js +2 -0
- package/build/types/collator.d.ts +8 -0
- package/build/types/collator.js +11 -0
- package/build/types/durable.d.ts +59 -0
- package/build/types/durable.js +2 -0
- package/build/types/hook.d.ts +31 -0
- package/build/types/hook.js +9 -0
- package/build/types/hotmesh.d.ts +82 -0
- package/build/types/hotmesh.js +2 -0
- package/build/types/index.d.ts +20 -0
- package/build/types/index.js +21 -0
- package/build/types/ioredisclient.d.ts +5 -0
- package/build/types/ioredisclient.js +5 -0
- package/build/types/job.d.ts +50 -0
- package/build/types/job.js +2 -0
- package/build/types/logger.d.ts +6 -0
- package/build/types/logger.js +2 -0
- package/build/types/map.d.ts +4 -0
- package/build/types/map.js +2 -0
- package/build/types/pipe.d.ts +4 -0
- package/build/types/pipe.js +2 -0
- package/build/types/quorum.d.ts +46 -0
- package/build/types/quorum.js +2 -0
- package/build/types/redis.d.ts +8 -0
- package/build/types/redis.js +2 -0
- package/build/types/redisclient.d.ts +25 -0
- package/build/types/redisclient.js +2 -0
- package/build/types/serializer.d.ts +33 -0
- package/build/types/serializer.js +2 -0
- package/build/types/stats.d.ts +83 -0
- package/build/types/stats.js +2 -0
- package/build/types/stream.d.ts +67 -0
- package/build/types/stream.js +25 -0
- package/build/types/telemetry.d.ts +1 -0
- package/build/types/telemetry.js +11 -0
- package/build/types/transition.d.ts +17 -0
- package/build/types/transition.js +2 -0
- package/index.ts +5 -0
- package/modules/errors.ts +55 -0
- package/modules/key.ts +129 -0
- package/modules/utils.ts +170 -0
- package/package.json +73 -0
- package/services/activities/activity.ts +473 -0
- package/services/activities/await.ts +172 -0
- package/services/activities/emit.ts +25 -0
- package/services/activities/index.ts +15 -0
- package/services/activities/iterate.ts +26 -0
- package/services/activities/trigger.ts +196 -0
- package/services/activities/worker.ts +190 -0
- package/services/collator/README.md +102 -0
- package/services/collator/index.ts +182 -0
- package/services/compiler/deployer.ts +432 -0
- package/services/compiler/index.ts +98 -0
- package/services/compiler/validator.ts +154 -0
- package/services/connector/clients/ioredis.ts +57 -0
- package/services/connector/clients/redis.ts +72 -0
- package/services/connector/index.ts +44 -0
- package/services/dimension/README.md +73 -0
- package/services/dimension/index.ts +39 -0
- package/services/durable/asyncLocalStorage.ts +3 -0
- package/services/durable/client.ts +116 -0
- package/services/durable/connection.ts +50 -0
- package/services/durable/factory.ts +124 -0
- package/services/durable/handle.ts +43 -0
- package/services/durable/index.ts +60 -0
- package/services/durable/native.ts +46 -0
- package/services/durable/worker.ts +254 -0
- package/services/durable/workflow.ts +136 -0
- package/services/engine/index.ts +615 -0
- package/services/hotmesh/index.ts +182 -0
- package/services/logger/index.ts +79 -0
- package/services/mapper/index.ts +84 -0
- package/services/pipe/functions/array.ts +87 -0
- package/services/pipe/functions/bitwise.ts +27 -0
- package/services/pipe/functions/conditional.ts +31 -0
- package/services/pipe/functions/date.ts +214 -0
- package/services/pipe/functions/index.ts +25 -0
- package/services/pipe/functions/json.ts +11 -0
- package/services/pipe/functions/math.ts +143 -0
- package/services/pipe/functions/number.ts +150 -0
- package/services/pipe/functions/object.ts +79 -0
- package/services/pipe/functions/string.ts +86 -0
- package/services/pipe/functions/symbol.ts +39 -0
- package/services/pipe/functions/unary.ts +19 -0
- package/services/pipe/index.ts +138 -0
- package/services/quorum/index.ts +200 -0
- package/services/reporter/index.ts +379 -0
- package/services/serializer/README.md +10 -0
- package/services/serializer/index.ts +243 -0
- package/services/signaler/store.ts +61 -0
- package/services/signaler/stream.ts +354 -0
- package/services/store/cache.ts +172 -0
- package/services/store/clients/ioredis.ts +123 -0
- package/services/store/clients/redis.ts +169 -0
- package/services/store/index.ts +757 -0
- package/services/stream/clients/ioredis.ts +148 -0
- package/services/stream/clients/redis.ts +144 -0
- package/services/stream/index.ts +57 -0
- package/services/sub/clients/ioredis.ts +83 -0
- package/services/sub/clients/redis.ts +74 -0
- package/services/sub/index.ts +25 -0
- package/services/task/index.ts +86 -0
- package/services/telemetry/index.ts +267 -0
- package/services/worker/index.ts +165 -0
- package/types/activity.ts +115 -0
- package/types/app.ts +20 -0
- package/types/async.ts +7 -0
- package/types/cache.ts +1 -0
- package/types/collator.ts +9 -0
- package/types/durable.ts +81 -0
- package/types/hook.ts +32 -0
- package/types/hotmesh.ts +102 -0
- package/types/index.ts +138 -0
- package/types/ioredisclient.ts +10 -0
- package/types/job.ts +59 -0
- package/types/logger.ts +6 -0
- package/types/map.ts +5 -0
- package/types/ms.d.ts +7 -0
- package/types/pipe.ts +7 -0
- package/types/quorum.ts +59 -0
- package/types/redis.ts +27 -0
- package/types/redisclient.ts +29 -0
- package/types/serializer.ts +38 -0
- package/types/stats.ts +100 -0
- package/types/stream.ts +75 -0
- package/types/telemetry.ts +15 -0
- package/types/transition.ts +20 -0
package/modules/key.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keys
|
|
3
|
+
*
|
|
4
|
+
* hmsh -> {hash} hotmesh config {version: "0.0.1", namespace: "hmsh"}
|
|
5
|
+
* hmsh:a:<appid> -> {hash} app profile { "id": "appid", "version": "2", "versions/1": "GMT", "versions/2": "GMT"}
|
|
6
|
+
* hmsh:<appid>:e:<engineId> -> {string} setnx to ensure only one engine of given id
|
|
7
|
+
* hmsh:<appid>:w: -> {zset} work items/tasks an engine must do like garbage collect or hook a set of matching records (hookAll)
|
|
8
|
+
* hmsh:<appid>:t: -> {zset} an ordered set of list (work lists) ids
|
|
9
|
+
* hmsh:<appid>:t:<timeValue?> -> {list} a worklist of `jobId+activityId` items that should be awakened
|
|
10
|
+
* hmsh:<appid>:q: -> {hash} quorum-wide messages
|
|
11
|
+
* hmsh:<appid>:q:<ngnid> -> {hash} engine-targeted messages (targeted quorum-oriented message)
|
|
12
|
+
* hmsh:<appid>:j:<jobid> -> {hash} job data
|
|
13
|
+
* hmsh:<appid>:j:<jobid>:<activityid> -> {hash} job activity data (a1)
|
|
14
|
+
* hmsh:<appid>:s:<jobkey>:<dateTime> -> {hash} job stats (general)
|
|
15
|
+
* hmsh:<appid>:s:<jobkey>:<dateTime>:mdn:<field/path>:<fieldvalue> -> {zset} job stats (median)
|
|
16
|
+
* hmsh:<appid>:s:<jobkey>:<dateTime>:index:<field/path>:<fieldvalue> -> {list} job stats (index of jobid[])
|
|
17
|
+
* hmsh:<appid>:v:<version>:activities -> {hash} schemas [cache]
|
|
18
|
+
* hmsh:<appid>:v:<version>:transitions -> {hash} transitions [cache]
|
|
19
|
+
* hmsh:<appid>:v:<version>:subscriptions -> {hash} subscriptions [cache]
|
|
20
|
+
* hmsh:<appid>:x: -> {xstream} when an engine is sent or reads a buffered task (engines read from their custom topic)
|
|
21
|
+
* hmsh:<appid>:x:<topic> -> {xstream} when a worker is sent or reads a buffered task (workers read from their custom topic)
|
|
22
|
+
* hmsh:<appid>:hooks -> {hash} hook patterns/rules; set at compile time
|
|
23
|
+
* hmsh:<appid>:signals -> {hash} dynamic hook signals (hget/hdel) when resolving (always self-clean); added/removed at runtime
|
|
24
|
+
* hmsh:<appid>:sym:keys: -> {hash} list of symbol ranges and :cursor assigned at version deploy time for job keys
|
|
25
|
+
* hmsh:<appid>:sym:keys:<activityid|$subscribes> -> {hash} list of symbols based upon schema enums (initially) and adaptively optimized (later) during runtime; if '$subscribes' is used as the activityid, it is a top-level `job` symbol set (for job keys)
|
|
26
|
+
* hmsh:<appid>:sym:vals: -> {hash} list of symbols for job values across all app versions
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
//default namespace for hotmesh
|
|
30
|
+
const PSNS = "hmsh";
|
|
31
|
+
|
|
32
|
+
//these are the entity types that are stored in the key/value store
|
|
33
|
+
enum KeyType {
|
|
34
|
+
APP,
|
|
35
|
+
ENGINE_ID,
|
|
36
|
+
HOOKS,
|
|
37
|
+
JOB_STATE,
|
|
38
|
+
JOB_STATS_GENERAL,
|
|
39
|
+
JOB_STATS_MEDIAN,
|
|
40
|
+
JOB_STATS_INDEX,
|
|
41
|
+
HOTMESH,
|
|
42
|
+
QUORUM,
|
|
43
|
+
SCHEMAS,
|
|
44
|
+
SIGNALS,
|
|
45
|
+
STREAMS,
|
|
46
|
+
SUBSCRIPTIONS,
|
|
47
|
+
SUBSCRIPTION_PATTERNS,
|
|
48
|
+
SYMKEYS,
|
|
49
|
+
SYMVALS,
|
|
50
|
+
TIME_RANGE,
|
|
51
|
+
WORK_ITEMS,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//when minting a key, the following parameters are used to create a unique key per entity
|
|
55
|
+
type KeyStoreParams = {
|
|
56
|
+
appId?: string; //app id is a uuid for a hotmesh app
|
|
57
|
+
engineId?: string; //unique auto-generated guid for an ephemeral engine instance
|
|
58
|
+
appVersion?: string; //(e.g. "1.0.0", "1", "1.0")
|
|
59
|
+
jobId?: string; //a customer-defined id for job; must be unique for the entire app
|
|
60
|
+
activityId?: string; //activity id is a uuid for a given hotmesh app
|
|
61
|
+
jobKey?: string; //a customer-defined label for a job that serves to categorize events
|
|
62
|
+
dateTime?: string; //UTC date time: YYYY-MM-DDTHH:MM (20203-04-12T00:00); serves as a time-series bucket for the job_key
|
|
63
|
+
facet?: string; //data path starting at root with values separated by colons (e.g. "object/type:bar")
|
|
64
|
+
topic?: string; //topic name (e.g., "foo" or "" for top-level)
|
|
65
|
+
timeValue?: number; //time value (rounded to minute) (for delete range)
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
class KeyService {
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* returns a key that can be used to access a value in the key/value store
|
|
72
|
+
* appropriate for the given key type; the keys have an implicit hierarchy
|
|
73
|
+
* and are used to organize data in the store in a tree-like structure
|
|
74
|
+
* via the use of colons as separators. The top-level entity is the hmsh manifest.
|
|
75
|
+
* This file will reveal the full scope of what is on the server (apps, versions, etc)
|
|
76
|
+
* @param namespace
|
|
77
|
+
* @param keyType
|
|
78
|
+
* @param params
|
|
79
|
+
* @returns {string}
|
|
80
|
+
*/
|
|
81
|
+
static mintKey(namespace: string, keyType: KeyType, params: KeyStoreParams): string {
|
|
82
|
+
switch (keyType) {
|
|
83
|
+
case KeyType.HOTMESH:
|
|
84
|
+
return namespace;
|
|
85
|
+
case KeyType.ENGINE_ID:
|
|
86
|
+
return `${namespace}:${params.appId}:e:${params.engineId}`;
|
|
87
|
+
case KeyType.WORK_ITEMS:
|
|
88
|
+
return `${namespace}:${params.appId}:w:`;
|
|
89
|
+
case KeyType.TIME_RANGE:
|
|
90
|
+
return `${namespace}:${params.appId}:t:${params.timeValue || ''}`;
|
|
91
|
+
case KeyType.APP:
|
|
92
|
+
return `${namespace}:a:${params.appId || ''}`;
|
|
93
|
+
case KeyType.QUORUM:
|
|
94
|
+
return `${namespace}:${params.appId}:q:${params.engineId || ''}`;
|
|
95
|
+
case KeyType.JOB_STATE:
|
|
96
|
+
return `${namespace}:${params.appId}:j:${params.jobId}`;
|
|
97
|
+
case KeyType.JOB_STATS_GENERAL:
|
|
98
|
+
return `${namespace}:${params.appId}:s:${params.jobKey}:${params.dateTime}`;
|
|
99
|
+
case KeyType.JOB_STATS_MEDIAN:
|
|
100
|
+
return `${namespace}:${params.appId}:s:${params.jobKey}:${params.dateTime}:${params.facet}`;
|
|
101
|
+
case KeyType.JOB_STATS_INDEX:
|
|
102
|
+
return `${namespace}:${params.appId}:s:${params.jobKey}:${params.dateTime}:${params.facet}`;
|
|
103
|
+
case KeyType.SCHEMAS:
|
|
104
|
+
return `${namespace}:${params.appId}:v:${params.appVersion}:schemas`;
|
|
105
|
+
case KeyType.SUBSCRIPTIONS:
|
|
106
|
+
return `${namespace}:${params.appId}:v:${params.appVersion}:subscriptions`;
|
|
107
|
+
case KeyType.SUBSCRIPTION_PATTERNS:
|
|
108
|
+
return `${namespace}:${params.appId}:v:${params.appVersion}:transitions`;
|
|
109
|
+
case KeyType.HOOKS:
|
|
110
|
+
//`hooks` provide the pattern to resolve a value
|
|
111
|
+
return `${namespace}:${params.appId}:hooks`;
|
|
112
|
+
case KeyType.SIGNALS:
|
|
113
|
+
//`signals` provide the registry of resolved values that link back to paused jobs
|
|
114
|
+
return `${namespace}:${params.appId}:signals`;
|
|
115
|
+
case KeyType.SYMKEYS:
|
|
116
|
+
//`symbol keys` provide the registry of replacement values for job keys
|
|
117
|
+
return `${namespace}:${params.appId}:sym:keys:${params.activityId || ''}`;
|
|
118
|
+
case KeyType.SYMVALS:
|
|
119
|
+
//`symbol vals` provide the registry of replacement values for job vals
|
|
120
|
+
return `${namespace}:${params.appId}:sym:vals:`;
|
|
121
|
+
case KeyType.STREAMS:
|
|
122
|
+
return `${namespace}:${params.appId || ''}:x:${params.topic || ''}`;
|
|
123
|
+
default:
|
|
124
|
+
throw new Error("Invalid key type.");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { KeyService, KeyType, KeyStoreParams, PSNS };
|
package/modules/utils.ts
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { StoreService } from "../services/store";
|
|
2
|
+
import { AppSubscriptions, AppTransitions, AppVID } from "../types/app";
|
|
3
|
+
import { RedisClient, RedisMulti } from "../types/redis";
|
|
4
|
+
import { StringAnyType } from "../types/serializer";
|
|
5
|
+
import { StreamCode, StreamStatus } from "../types/stream";
|
|
6
|
+
|
|
7
|
+
export async function sleepFor(ms: number) {
|
|
8
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function identifyRedisType(redisInstance: any): 'redis' | 'ioredis' | null {
|
|
12
|
+
if (redisInstance.constructor) {
|
|
13
|
+
if (redisInstance.constructor.name === 'Redis' || redisInstance.constructor.name === 'EventEmitter') {
|
|
14
|
+
if ('hset' in redisInstance) {
|
|
15
|
+
return 'ioredis';
|
|
16
|
+
}
|
|
17
|
+
} else if (redisInstance.constructor.name === 'RedisClient' || redisInstance.constructor.name === 'Commander') {
|
|
18
|
+
if ('HSET' in redisInstance) {
|
|
19
|
+
return 'redis';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function identifyRedisTypeFromClass(redisClass: any): 'redis' | 'ioredis' | null {
|
|
27
|
+
if (redisClass && redisClass.name === 'Redis' || redisClass.name === 'EventEmitter') {
|
|
28
|
+
return 'ioredis';
|
|
29
|
+
} else if (redisClass && 'createClient' in redisClass) {
|
|
30
|
+
return 'redis';
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function matchesStatusCode(code: StreamCode, pattern: string | RegExp): boolean {
|
|
36
|
+
if (typeof pattern === 'string') {
|
|
37
|
+
// Convert '*' wildcard to its regex equivalent (\d)
|
|
38
|
+
const regexPattern = `^${pattern.replace(/\*/g, "\\d")}$`;
|
|
39
|
+
return new RegExp(regexPattern).test(code.toString());
|
|
40
|
+
}
|
|
41
|
+
return pattern.test(code.toString());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function matchesStatus(status: StreamStatus, targetStatus: StreamStatus): boolean {
|
|
45
|
+
return status === targetStatus;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function XSleepFor(ms: number): { promise: Promise<unknown>, timerId: NodeJS.Timeout } {
|
|
49
|
+
//can be interrupted with `clearTimeout`
|
|
50
|
+
let timerId: NodeJS.Timeout;
|
|
51
|
+
let promise = new Promise((resolve) => {
|
|
52
|
+
timerId = setTimeout(resolve, ms);
|
|
53
|
+
});
|
|
54
|
+
return { promise, timerId };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function findTopKey(obj: AppTransitions, input: string): string | null {
|
|
58
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
59
|
+
if (value.hasOwnProperty(input)) {
|
|
60
|
+
const parentKey = findTopKey(obj, key.replace(/^\./, ''));
|
|
61
|
+
return (parentKey || key).replace(/^\./, '');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function findSubscriptionForTrigger(obj: AppSubscriptions, value: string): string | null {
|
|
68
|
+
for (const [key, itemValue] of Object.entries(obj)) {
|
|
69
|
+
if (itemValue === value) {
|
|
70
|
+
return key;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get the subscription topic for the flow to which @activityId belongs.
|
|
78
|
+
* TODO: resolve this value in the compiler...do not call this at runtime
|
|
79
|
+
*/
|
|
80
|
+
export async function getSubscriptionTopic(activityId: string, store: StoreService<RedisClient, RedisMulti>, appVID: AppVID): Promise<string | undefined> {
|
|
81
|
+
const appTransitions = await store.getTransitions(appVID);
|
|
82
|
+
const appSubscriptions = await store.getSubscriptions(appVID);
|
|
83
|
+
const triggerId = findTopKey(appTransitions, activityId);
|
|
84
|
+
const topic = findSubscriptionForTrigger(appSubscriptions, triggerId);
|
|
85
|
+
return topic;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* returns the 12-digit format of the iso timestamp (e.g, 202101010000)
|
|
90
|
+
*/
|
|
91
|
+
export function getTimeSeries(granularity: string): string {
|
|
92
|
+
const now = new Date();
|
|
93
|
+
const granularityUnit = granularity.slice(-1);
|
|
94
|
+
const granularityValue = parseInt(granularity.slice(0, -1), 10);
|
|
95
|
+
if (granularityUnit === 'm') {
|
|
96
|
+
const minute = Math.floor(now.getMinutes() / granularityValue) * granularityValue;
|
|
97
|
+
now.setUTCMinutes(minute, 0, 0);
|
|
98
|
+
} else if (granularityUnit === 'h') {
|
|
99
|
+
now.setUTCMinutes(0, 0, 0);
|
|
100
|
+
}
|
|
101
|
+
return now.toISOString().replace(/:\d\d\..+|-|T/g, '').replace(':','');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function formatISODate(input: Date | string): string {
|
|
105
|
+
const date = input instanceof Date ? input : new Date(input);
|
|
106
|
+
return date.toISOString().replace(/[:TZ-]/g, '');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function getSymKey(number: number): string {
|
|
110
|
+
const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
111
|
+
const base = alphabet.length;
|
|
112
|
+
if (number < 0 || number >= Math.pow(base, 3)) {
|
|
113
|
+
throw new Error('Number out of range');
|
|
114
|
+
}
|
|
115
|
+
let [q1, r1] = divmod(number, base);
|
|
116
|
+
let [q2, r2] = divmod(q1, base);
|
|
117
|
+
return alphabet[q2] + alphabet[r1] + alphabet[r2];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function getSymVal(number: number): string {
|
|
121
|
+
const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
122
|
+
const base = alphabet.length;
|
|
123
|
+
if (number < 0 || number >= Math.pow(base, 2)) {
|
|
124
|
+
throw new Error('Number out of range');
|
|
125
|
+
}
|
|
126
|
+
let [q, r] = divmod(number, base);
|
|
127
|
+
return alphabet[q] + alphabet[r];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function divmod(m: number, n: number): number[] {
|
|
131
|
+
return [Math.floor(m / n), m % n];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function getIndexedHash<T>(hash: T, target: string): [number, T] {
|
|
135
|
+
const index = hash[target] as number || 0;
|
|
136
|
+
const newHash = { ...hash };
|
|
137
|
+
delete newHash[target];
|
|
138
|
+
return [index, newHash as T];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function getValueByPath(obj: { [key: string]: any }, path: string): any {
|
|
142
|
+
const pathParts = path.split('/');
|
|
143
|
+
let currentValue = obj;
|
|
144
|
+
for (const part of pathParts) {
|
|
145
|
+
if (currentValue[part] !== undefined) {
|
|
146
|
+
currentValue = currentValue[part];
|
|
147
|
+
} else {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return currentValue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function restoreHierarchy(obj: StringAnyType): StringAnyType {
|
|
155
|
+
const result: StringAnyType = {};
|
|
156
|
+
for (const key in obj) {
|
|
157
|
+
if (obj[key] === undefined) continue;
|
|
158
|
+
const keys = key.split('/');
|
|
159
|
+
let current = result;
|
|
160
|
+
for (let i = 0; i < keys.length; i++) {
|
|
161
|
+
if (i === keys.length - 1) {
|
|
162
|
+
current[keys[i]] = obj[key];
|
|
163
|
+
} else {
|
|
164
|
+
current[keys[i]] = current[keys[i]] || {};
|
|
165
|
+
current = current[keys[i]];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hotmeshio/hotmesh",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Durable Workflows",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"types": "build/index.d.ts",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/hotmeshio/hotmesh.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/hotmeshio/hotmesh#readme",
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"clean": "rimraf ./build",
|
|
17
|
+
"build": "tsc --build tsconfig.json",
|
|
18
|
+
"clean-build": "npm run clean && npm run build",
|
|
19
|
+
"lint": "eslint . --ext .ts",
|
|
20
|
+
"lint:fix": "eslint . --fix --ext .ts",
|
|
21
|
+
"start": "ts-node src/index.ts",
|
|
22
|
+
"test": "NODE_ENV=test jest --detectOpenHandles --forceExit --verbose",
|
|
23
|
+
"test:hmsh": "NODE_ENV=test jest ./tests/functional/index.test.ts --detectOpenHandles --verbose",
|
|
24
|
+
"test:compile": "NODE_ENV=test jest ./tests/functional/compile/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
25
|
+
"test:connect": "NODE_ENV=test jest ./tests/unit/services/connector/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
26
|
+
"test:connect:redis": "NODE_ENV=test jest ./tests/unit/services/connector/clients/redis.test.ts --detectOpenHandles --forceExit --verbose",
|
|
27
|
+
"test:connect:ioredis": "NODE_ENV=test jest ./tests/unit/services/connector/clients/ioredis.test.ts --detectOpenHandles --forceExit --verbose",
|
|
28
|
+
"test:parallel": "NODE_ENV=test jest ./tests/functional/parallel/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
29
|
+
"test:sequence": "NODE_ENV=test jest ./tests/functional/sequence/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
30
|
+
"test:quorum": "NODE_ENV=test jest ./tests/functional/quorum/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
31
|
+
"test:reclaim": "NODE_ENV=test jest ./tests/functional/reclaim/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
32
|
+
"test:redeploy": "NODE_ENV=test jest ./tests/functional/redeploy/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
33
|
+
"test:retry": "NODE_ENV=test jest ./tests/functional/retry/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
34
|
+
"test:status": "NODE_ENV=test jest ./tests/functional/status/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
35
|
+
"test:store:redis": "NODE_ENV=test jest ./tests/functional/store/clients/redis.test.ts --detectOpenHandles --forceExit --verbose",
|
|
36
|
+
"test:store:ioredis": "NODE_ENV=test jest ./tests/functional/store/clients/ioredis.test.ts --detectOpenHandles --forceExit --verbose",
|
|
37
|
+
"test:stream:redis": "NODE_ENV=test jest ./tests/functional/stream/clients/redis.test.ts --detectOpenHandles --forceExit --verbose",
|
|
38
|
+
"test:stream:ioredis": "NODE_ENV=test jest ./tests/functional/stream/clients/ioredis.test.ts --detectOpenHandles --forceExit --verbose",
|
|
39
|
+
"test:sub:redis": "NODE_ENV=test jest ./tests/functional/sub/clients/redis.test.ts --detectOpenHandles --forceExit --verbose",
|
|
40
|
+
"test:sub:ioredis": "NODE_ENV=test jest ./tests/functional/sub/clients/ioredis.test.ts --detectOpenHandles --forceExit --verbose",
|
|
41
|
+
"test:durable": "NODE_ENV=test jest ./tests/durable/*/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
42
|
+
"test:durable:hello": "NODE_ENV=test jest ./tests/durable/helloworld/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
43
|
+
"test:durable:goodbye": "NODE_ENV=test jest ./tests/durable/goodbye/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
44
|
+
"test:durable:loopactivity": "NODE_ENV=test jest ./tests/durable/loopactivity/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
45
|
+
"test:durable:nested": "NODE_ENV=test jest ./tests/durable/nested/index.test.ts --detectOpenHandles --forceExit --verbose"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [],
|
|
48
|
+
"author": "luke.birdeau@gmail.com",
|
|
49
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@apidevtools/json-schema-ref-parser": "^10.1.0",
|
|
52
|
+
"@opentelemetry/api": "^1.4.1",
|
|
53
|
+
"js-yaml": "^4.1.0",
|
|
54
|
+
"ms": "^2.1.3",
|
|
55
|
+
"nanoid": "^3.3.6",
|
|
56
|
+
"winston": "^3.8.2"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/jest": "^29.5.0",
|
|
60
|
+
"@types/node": "^18.15.11",
|
|
61
|
+
"@typescript-eslint/eslint-plugin": "^5.56.0",
|
|
62
|
+
"@typescript-eslint/parser": "^5.56.0",
|
|
63
|
+
"eslint": "^8.36.0",
|
|
64
|
+
"ioredis": "^5.3.2",
|
|
65
|
+
"jest": "^29.5.0",
|
|
66
|
+
"redis": "^4.6.6",
|
|
67
|
+
"rimraf": "^4.4.1",
|
|
68
|
+
"ts-jest": "^29.0.5",
|
|
69
|
+
"ts-node": "^10.9.1",
|
|
70
|
+
"ts-node-dev": "^2.0.0",
|
|
71
|
+
"typescript": "^5.0.4"
|
|
72
|
+
}
|
|
73
|
+
}
|