@hotmeshio/hotmesh 0.4.0 → 0.4.2

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.
Files changed (284) hide show
  1. package/README.md +39 -14
  2. package/build/modules/enums.d.ts +110 -0
  3. package/build/modules/enums.js +134 -0
  4. package/build/modules/errors.d.ts +124 -0
  5. package/build/modules/errors.js +191 -0
  6. package/build/modules/key.d.ts +66 -0
  7. package/build/modules/key.js +190 -0
  8. package/build/modules/storage.d.ts +3 -0
  9. package/build/modules/storage.js +5 -0
  10. package/build/modules/utils.d.ts +119 -0
  11. package/build/modules/utils.js +374 -0
  12. package/build/package.json +1 -1
  13. package/build/services/activities/activity.d.ts +104 -0
  14. package/build/services/activities/activity.js +549 -0
  15. package/build/services/activities/await.d.ts +12 -0
  16. package/build/services/activities/await.js +114 -0
  17. package/build/services/activities/cycle.d.ts +19 -0
  18. package/build/services/activities/cycle.js +112 -0
  19. package/build/services/activities/hook.d.ts +27 -0
  20. package/build/services/activities/hook.js +168 -0
  21. package/build/services/activities/index.d.ts +19 -0
  22. package/build/services/activities/index.js +20 -0
  23. package/build/services/activities/interrupt.d.ts +16 -0
  24. package/build/services/activities/interrupt.js +158 -0
  25. package/build/services/activities/signal.d.ts +20 -0
  26. package/build/services/activities/signal.js +134 -0
  27. package/build/services/activities/trigger.d.ts +37 -0
  28. package/build/services/activities/trigger.js +246 -0
  29. package/build/services/activities/worker.d.ts +12 -0
  30. package/build/services/activities/worker.js +106 -0
  31. package/build/services/collator/index.d.ts +111 -0
  32. package/build/services/collator/index.js +293 -0
  33. package/build/services/compiler/deployer.d.ts +40 -0
  34. package/build/services/compiler/deployer.js +488 -0
  35. package/build/services/compiler/index.d.ts +32 -0
  36. package/build/services/compiler/index.js +112 -0
  37. package/build/services/compiler/validator.d.ts +34 -0
  38. package/build/services/compiler/validator.js +147 -0
  39. package/build/services/connector/factory.d.ts +22 -0
  40. package/build/services/connector/factory.js +99 -0
  41. package/build/services/connector/index.d.ts +30 -0
  42. package/build/services/connector/index.js +54 -0
  43. package/build/services/connector/providers/ioredis.d.ts +9 -0
  44. package/build/services/connector/providers/ioredis.js +26 -0
  45. package/build/services/connector/providers/nats.d.ts +9 -0
  46. package/build/services/connector/providers/nats.js +34 -0
  47. package/build/services/connector/providers/postgres.d.ts +20 -0
  48. package/build/services/connector/providers/postgres.js +102 -0
  49. package/build/services/connector/providers/redis.d.ts +9 -0
  50. package/build/services/connector/providers/redis.js +38 -0
  51. package/build/services/engine/index.d.ts +264 -0
  52. package/build/services/engine/index.js +761 -0
  53. package/build/services/exporter/index.d.ts +44 -0
  54. package/build/services/exporter/index.js +126 -0
  55. package/build/services/hotmesh/index.d.ts +483 -0
  56. package/build/services/hotmesh/index.js +622 -0
  57. package/build/services/logger/index.d.ts +16 -0
  58. package/build/services/logger/index.js +54 -0
  59. package/build/services/mapper/index.d.ts +28 -0
  60. package/build/services/mapper/index.js +81 -0
  61. package/build/services/memflow/client.d.ts +108 -0
  62. package/build/services/memflow/client.js +372 -0
  63. package/build/services/memflow/connection.d.ts +23 -0
  64. package/build/services/memflow/connection.js +33 -0
  65. package/build/services/memflow/context.d.ts +143 -0
  66. package/build/services/memflow/context.js +299 -0
  67. package/build/services/memflow/exporter.d.ts +51 -0
  68. package/build/services/memflow/exporter.js +215 -0
  69. package/build/services/memflow/handle.d.ts +90 -0
  70. package/build/services/memflow/handle.js +176 -0
  71. package/build/services/memflow/index.d.ts +116 -0
  72. package/build/services/memflow/index.js +122 -0
  73. package/build/services/memflow/schemas/factory.d.ts +29 -0
  74. package/build/services/memflow/schemas/factory.js +2492 -0
  75. package/build/services/memflow/search.d.ts +142 -0
  76. package/build/services/memflow/search.js +320 -0
  77. package/build/services/memflow/worker.d.ts +124 -0
  78. package/build/services/memflow/worker.js +514 -0
  79. package/build/services/memflow/workflow/all.d.ts +7 -0
  80. package/build/services/memflow/workflow/all.js +15 -0
  81. package/build/services/memflow/workflow/common.d.ts +20 -0
  82. package/build/services/memflow/workflow/common.js +47 -0
  83. package/build/services/memflow/workflow/context.d.ts +6 -0
  84. package/build/services/memflow/workflow/context.js +45 -0
  85. package/build/services/memflow/workflow/contextMethods.d.ts +14 -0
  86. package/build/services/memflow/workflow/contextMethods.js +33 -0
  87. package/build/services/memflow/workflow/didRun.d.ts +7 -0
  88. package/build/services/memflow/workflow/didRun.js +22 -0
  89. package/build/services/memflow/workflow/emit.d.ts +11 -0
  90. package/build/services/memflow/workflow/emit.js +29 -0
  91. package/build/services/memflow/workflow/enrich.d.ts +9 -0
  92. package/build/services/memflow/workflow/enrich.js +17 -0
  93. package/build/services/memflow/workflow/execChild.d.ts +18 -0
  94. package/build/services/memflow/workflow/execChild.js +102 -0
  95. package/build/services/memflow/workflow/execHook.d.ts +65 -0
  96. package/build/services/memflow/workflow/execHook.js +73 -0
  97. package/build/services/memflow/workflow/hook.d.ts +9 -0
  98. package/build/services/memflow/workflow/hook.js +56 -0
  99. package/build/services/memflow/workflow/index.d.ts +74 -0
  100. package/build/services/memflow/workflow/index.js +87 -0
  101. package/build/services/memflow/workflow/interrupt.d.ts +9 -0
  102. package/build/services/memflow/workflow/interrupt.js +24 -0
  103. package/build/services/memflow/workflow/isSideEffectAllowed.d.ts +10 -0
  104. package/build/services/memflow/workflow/isSideEffectAllowed.js +33 -0
  105. package/build/services/memflow/workflow/proxyActivities.d.ts +20 -0
  106. package/build/services/memflow/workflow/proxyActivities.js +97 -0
  107. package/build/services/memflow/workflow/random.d.ts +6 -0
  108. package/build/services/memflow/workflow/random.js +16 -0
  109. package/build/services/memflow/workflow/searchMethods.d.ts +6 -0
  110. package/build/services/memflow/workflow/searchMethods.js +25 -0
  111. package/build/services/memflow/workflow/signal.d.ts +29 -0
  112. package/build/services/memflow/workflow/signal.js +50 -0
  113. package/build/services/memflow/workflow/sleepFor.d.ts +24 -0
  114. package/build/services/memflow/workflow/sleepFor.js +51 -0
  115. package/build/services/memflow/workflow/trace.d.ts +14 -0
  116. package/build/services/memflow/workflow/trace.js +33 -0
  117. package/build/services/memflow/workflow/waitFor.d.ts +29 -0
  118. package/build/services/memflow/workflow/waitFor.js +56 -0
  119. package/build/services/meshcall/index.d.ts +194 -0
  120. package/build/services/meshcall/index.js +452 -0
  121. package/build/services/meshcall/schemas/factory.d.ts +9 -0
  122. package/build/services/meshcall/schemas/factory.js +189 -0
  123. package/build/services/meshdata/index.d.ts +795 -0
  124. package/build/services/meshdata/index.js +1235 -0
  125. package/build/services/meshos/index.d.ts +293 -0
  126. package/build/services/meshos/index.js +547 -0
  127. package/build/services/pipe/functions/array.d.ts +17 -0
  128. package/build/services/pipe/functions/array.js +74 -0
  129. package/build/services/pipe/functions/bitwise.d.ts +9 -0
  130. package/build/services/pipe/functions/bitwise.js +24 -0
  131. package/build/services/pipe/functions/conditional.d.ts +13 -0
  132. package/build/services/pipe/functions/conditional.js +36 -0
  133. package/build/services/pipe/functions/cron.d.ts +12 -0
  134. package/build/services/pipe/functions/cron.js +40 -0
  135. package/build/services/pipe/functions/date.d.ts +58 -0
  136. package/build/services/pipe/functions/date.js +171 -0
  137. package/build/services/pipe/functions/index.d.ts +29 -0
  138. package/build/services/pipe/functions/index.js +30 -0
  139. package/build/services/pipe/functions/json.d.ts +5 -0
  140. package/build/services/pipe/functions/json.js +12 -0
  141. package/build/services/pipe/functions/logical.d.ts +5 -0
  142. package/build/services/pipe/functions/logical.js +12 -0
  143. package/build/services/pipe/functions/math.d.ts +42 -0
  144. package/build/services/pipe/functions/math.js +184 -0
  145. package/build/services/pipe/functions/number.d.ts +21 -0
  146. package/build/services/pipe/functions/number.js +60 -0
  147. package/build/services/pipe/functions/object.d.ts +25 -0
  148. package/build/services/pipe/functions/object.js +81 -0
  149. package/build/services/pipe/functions/string.d.ts +23 -0
  150. package/build/services/pipe/functions/string.js +69 -0
  151. package/build/services/pipe/functions/symbol.d.ts +12 -0
  152. package/build/services/pipe/functions/symbol.js +33 -0
  153. package/build/services/pipe/functions/unary.d.ts +7 -0
  154. package/build/services/pipe/functions/unary.js +18 -0
  155. package/build/services/pipe/index.d.ts +48 -0
  156. package/build/services/pipe/index.js +242 -0
  157. package/build/services/quorum/index.d.ts +90 -0
  158. package/build/services/quorum/index.js +263 -0
  159. package/build/services/reporter/index.d.ts +50 -0
  160. package/build/services/reporter/index.js +348 -0
  161. package/build/services/router/config/index.d.ts +11 -0
  162. package/build/services/router/config/index.js +36 -0
  163. package/build/services/router/consumption/index.d.ts +34 -0
  164. package/build/services/router/consumption/index.js +395 -0
  165. package/build/services/router/error-handling/index.d.ts +8 -0
  166. package/build/services/router/error-handling/index.js +98 -0
  167. package/build/services/router/index.d.ts +57 -0
  168. package/build/services/router/index.js +121 -0
  169. package/build/services/router/lifecycle/index.d.ts +27 -0
  170. package/build/services/router/lifecycle/index.js +80 -0
  171. package/build/services/router/telemetry/index.d.ts +11 -0
  172. package/build/services/router/telemetry/index.js +32 -0
  173. package/build/services/router/throttling/index.d.ts +23 -0
  174. package/build/services/router/throttling/index.js +76 -0
  175. package/build/services/search/factory.d.ts +7 -0
  176. package/build/services/search/factory.js +24 -0
  177. package/build/services/search/index.d.ts +23 -0
  178. package/build/services/search/index.js +10 -0
  179. package/build/services/search/providers/postgres/postgres.d.ts +25 -0
  180. package/build/services/search/providers/postgres/postgres.js +149 -0
  181. package/build/services/search/providers/redis/ioredis.d.ts +19 -0
  182. package/build/services/search/providers/redis/ioredis.js +121 -0
  183. package/build/services/search/providers/redis/redis.d.ts +19 -0
  184. package/build/services/search/providers/redis/redis.js +134 -0
  185. package/build/services/serializer/index.d.ts +42 -0
  186. package/build/services/serializer/index.js +282 -0
  187. package/build/services/store/cache.d.ts +67 -0
  188. package/build/services/store/cache.js +128 -0
  189. package/build/services/store/factory.d.ts +8 -0
  190. package/build/services/store/factory.js +24 -0
  191. package/build/services/store/index.d.ts +89 -0
  192. package/build/services/store/index.js +9 -0
  193. package/build/services/store/providers/postgres/kvsql.d.ts +168 -0
  194. package/build/services/store/providers/postgres/kvsql.js +198 -0
  195. package/build/services/store/providers/postgres/kvtables.d.ts +20 -0
  196. package/build/services/store/providers/postgres/kvtables.js +441 -0
  197. package/build/services/store/providers/postgres/kvtransaction.d.ts +36 -0
  198. package/build/services/store/providers/postgres/kvtransaction.js +248 -0
  199. package/build/services/store/providers/postgres/kvtypes/hash.d.ts +60 -0
  200. package/build/services/store/providers/postgres/kvtypes/hash.js +1287 -0
  201. package/build/services/store/providers/postgres/kvtypes/list.d.ts +33 -0
  202. package/build/services/store/providers/postgres/kvtypes/list.js +194 -0
  203. package/build/services/store/providers/postgres/kvtypes/string.d.ts +20 -0
  204. package/build/services/store/providers/postgres/kvtypes/string.js +115 -0
  205. package/build/services/store/providers/postgres/kvtypes/zset.d.ts +41 -0
  206. package/build/services/store/providers/postgres/kvtypes/zset.js +214 -0
  207. package/build/services/store/providers/postgres/postgres.d.ts +178 -0
  208. package/build/services/store/providers/postgres/postgres.js +1244 -0
  209. package/build/services/store/providers/redis/_base.d.ts +137 -0
  210. package/build/services/store/providers/redis/_base.js +980 -0
  211. package/build/services/store/providers/redis/ioredis.d.ts +20 -0
  212. package/build/services/store/providers/redis/ioredis.js +180 -0
  213. package/build/services/store/providers/redis/redis.d.ts +18 -0
  214. package/build/services/store/providers/redis/redis.js +199 -0
  215. package/build/services/store/providers/store-initializable.d.ts +5 -0
  216. package/build/services/store/providers/store-initializable.js +2 -0
  217. package/build/services/stream/factory.d.ts +8 -0
  218. package/build/services/stream/factory.js +37 -0
  219. package/build/services/stream/index.d.ts +69 -0
  220. package/build/services/stream/index.js +11 -0
  221. package/build/services/stream/providers/nats/nats.d.ts +60 -0
  222. package/build/services/stream/providers/nats/nats.js +225 -0
  223. package/build/services/stream/providers/postgres/kvtables.d.ts +3 -0
  224. package/build/services/stream/providers/postgres/kvtables.js +146 -0
  225. package/build/services/stream/providers/postgres/postgres.d.ts +107 -0
  226. package/build/services/stream/providers/postgres/postgres.js +519 -0
  227. package/build/services/stream/providers/redis/ioredis.d.ts +61 -0
  228. package/build/services/stream/providers/redis/ioredis.js +272 -0
  229. package/build/services/stream/providers/redis/redis.d.ts +61 -0
  230. package/build/services/stream/providers/redis/redis.js +305 -0
  231. package/build/services/stream/providers/stream-initializable.d.ts +4 -0
  232. package/build/services/stream/providers/stream-initializable.js +2 -0
  233. package/build/services/sub/factory.d.ts +7 -0
  234. package/build/services/sub/factory.js +29 -0
  235. package/build/services/sub/index.d.ts +22 -0
  236. package/build/services/sub/index.js +10 -0
  237. package/build/services/sub/providers/nats/nats.d.ts +19 -0
  238. package/build/services/sub/providers/nats/nats.js +105 -0
  239. package/build/services/sub/providers/postgres/postgres.d.ts +19 -0
  240. package/build/services/sub/providers/postgres/postgres.js +92 -0
  241. package/build/services/sub/providers/redis/ioredis.d.ts +17 -0
  242. package/build/services/sub/providers/redis/ioredis.js +81 -0
  243. package/build/services/sub/providers/redis/redis.d.ts +17 -0
  244. package/build/services/sub/providers/redis/redis.js +72 -0
  245. package/build/services/task/index.d.ts +48 -0
  246. package/build/services/task/index.js +253 -0
  247. package/build/services/telemetry/index.d.ts +52 -0
  248. package/build/services/telemetry/index.js +306 -0
  249. package/build/services/worker/index.d.ts +77 -0
  250. package/build/services/worker/index.js +197 -0
  251. package/package.json +1 -1
  252. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  253. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  254. package/typedoc.json +0 -47
  255. package/types/activity.ts +0 -268
  256. package/types/app.ts +0 -20
  257. package/types/async.ts +0 -6
  258. package/types/cache.ts +0 -1
  259. package/types/collator.ts +0 -9
  260. package/types/error.ts +0 -56
  261. package/types/exporter.ts +0 -102
  262. package/types/hook.ts +0 -44
  263. package/types/hotmesh.ts +0 -314
  264. package/types/index.ts +0 -306
  265. package/types/job.ts +0 -233
  266. package/types/logger.ts +0 -8
  267. package/types/manifest.ts +0 -70
  268. package/types/map.ts +0 -5
  269. package/types/memflow.ts +0 -645
  270. package/types/meshcall.ts +0 -235
  271. package/types/meshdata.ts +0 -278
  272. package/types/ms.d.ts +0 -7
  273. package/types/nats.ts +0 -270
  274. package/types/pipe.ts +0 -90
  275. package/types/postgres.ts +0 -114
  276. package/types/provider.ts +0 -161
  277. package/types/quorum.ts +0 -167
  278. package/types/redis.ts +0 -404
  279. package/types/serializer.ts +0 -40
  280. package/types/stats.ts +0 -117
  281. package/types/stream.ts +0 -231
  282. package/types/task.ts +0 -7
  283. package/types/telemetry.ts +0 -16
  284. package/types/transition.ts +0 -20
@@ -0,0 +1,1244 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.PostgresStoreService = void 0;
27
+ const errors_1 = require("../../../../modules/errors");
28
+ const key_1 = require("../../../../modules/key");
29
+ const serializer_1 = require("../../../serializer");
30
+ const utils_1 = require("../../../../modules/utils");
31
+ const enums_1 = require("../../../../modules/enums");
32
+ const cache_1 = require("../../cache");
33
+ const __1 = require("../..");
34
+ const kvsql_1 = require("./kvsql");
35
+ const kvtables_1 = require("./kvtables");
36
+ class PostgresStoreService extends __1.StoreService {
37
+ transact() {
38
+ return this.storeClient.transact();
39
+ }
40
+ constructor(storeClient) {
41
+ super(storeClient);
42
+ this.isScout = false;
43
+ //Instead of directly referencing the 'pg' package and methods like 'query',
44
+ // the PostgresStore wraps the 'pg' client in a class that implements
45
+ // the Redis client interface. This allows the same methods to be called
46
+ // that were used when authoring the Redis client store provider.
47
+ //In general, this.storeClient will behave like Redis, but will
48
+ // use the 'pg' package and will read/write to a Postgres database.
49
+ this.pgClient = storeClient;
50
+ this.storeClient = new kvsql_1.KVSQL(storeClient, this.namespace, this.appId);
51
+ //kvTables will provision tables and indexes in the Postgres db as necessary
52
+ this.kvTables = (0, kvtables_1.KVTables)(this);
53
+ }
54
+ async init(namespace = key_1.HMNS, appId, logger) {
55
+ //bind appId and namespace to storeClient once initialized
56
+ // (it uses these values to construct keys for the store)
57
+ this.storeClient.namespace = this.namespace = namespace;
58
+ this.storeClient.appId = this.appId = appId;
59
+ this.logger = logger;
60
+ //confirm db tables exist
61
+ await this.kvTables.deploy(appId);
62
+ // Deploy time notification triggers
63
+ await this.deployTimeNotificationTriggers(appId);
64
+ //note: getSettings will contact db to confirm r/w access
65
+ const settings = await this.getSettings(true);
66
+ this.cache = new cache_1.Cache(appId, settings);
67
+ this.serializer = new serializer_1.SerializerService();
68
+ await this.getApp(appId);
69
+ return this.cache.getApps();
70
+ }
71
+ isSuccessful(result) {
72
+ return result > 0 || result === 'OK' || result === true;
73
+ }
74
+ async delistSignalKey(key, target) {
75
+ await this.kvsql().del(`${key}:${target}`);
76
+ }
77
+ async zAdd(key, score, value, transaction) {
78
+ //default call signature uses 'ioredis' NPM Package format
79
+ return await this.kvsql(transaction).zadd(key, Number(score), value.toString());
80
+ }
81
+ async zRangeByScore(key, score, value) {
82
+ const result = await this.kvsql().zrangebyscore(key, Number(score), Number(value));
83
+ if (result?.length > 0) {
84
+ return result[0];
85
+ }
86
+ return null;
87
+ }
88
+ mintKey(type, params) {
89
+ if (!this.namespace)
90
+ throw new Error('namespace not set');
91
+ return key_1.KeyService.mintKey(this.namespace, type, params);
92
+ }
93
+ /**
94
+ * strongly types the transaction or storeClient as KVSQL,
95
+ * so methods are visible to the compiler/code editor
96
+ */
97
+ kvsql(transaction) {
98
+ return (transaction || this.storeClient);
99
+ }
100
+ invalidateCache() {
101
+ this.cache.invalidate();
102
+ }
103
+ /**
104
+ * At any given time only a single engine will
105
+ * check for and process work items in the
106
+ * time and signal task queues.
107
+ */
108
+ async reserveScoutRole(scoutType, delay = enums_1.HMSH_SCOUT_INTERVAL_SECONDS) {
109
+ const key = this.mintKey(key_1.KeyType.WORK_ITEMS, {
110
+ appId: this.appId,
111
+ scoutType,
112
+ });
113
+ const success = await this.kvsql().set(key, `${scoutType}:${(0, utils_1.formatISODate)(new Date())}`, { nx: true, ex: delay - 1 });
114
+ return this.isSuccessful(success);
115
+ }
116
+ async releaseScoutRole(scoutType) {
117
+ const key = this.mintKey(key_1.KeyType.WORK_ITEMS, {
118
+ appId: this.appId,
119
+ scoutType,
120
+ });
121
+ const success = await this.kvsql().del(key);
122
+ return this.isSuccessful(success);
123
+ }
124
+ async getSettings(bCreate = false) {
125
+ let settings = this.cache?.getSettings();
126
+ if (settings) {
127
+ return settings;
128
+ }
129
+ else {
130
+ if (bCreate) {
131
+ const packageJson = await Promise.resolve().then(() => __importStar(require('../../../../package.json')));
132
+ const version = packageJson['version'] || '0.0.0';
133
+ settings = { namespace: key_1.HMNS, version };
134
+ await this.setSettings(settings);
135
+ return settings;
136
+ }
137
+ }
138
+ throw new Error('settings not found');
139
+ }
140
+ async setSettings(manifest) {
141
+ //HotMesh heartbeat. If a connection is made, the version will be set
142
+ const params = {};
143
+ const key = this.mintKey(key_1.KeyType.HOTMESH, params);
144
+ return await this.kvsql().hset(key, manifest);
145
+ }
146
+ async reserveSymbolRange(target, size, type, tryCount = 1) {
147
+ const rangeKey = this.mintKey(key_1.KeyType.SYMKEYS, { appId: this.appId });
148
+ const symbolKey = this.mintKey(key_1.KeyType.SYMKEYS, {
149
+ activityId: target,
150
+ appId: this.appId,
151
+ });
152
+ //reserve the slot in a `pending` state (range will be established in the next step)
153
+ const response = await this.kvsql().hsetnx(rangeKey, target, '?:?');
154
+ if (response) {
155
+ //if the key didn't exist, set the inclusive range and seed metadata fields
156
+ const upperLimit = await this.kvsql().hincrbyfloat(rangeKey, ':cursor', size);
157
+ const lowerLimit = upperLimit - size;
158
+ const inclusiveRange = `${lowerLimit}:${upperLimit - 1}`;
159
+ await this.kvsql().hset(rangeKey, { [target]: inclusiveRange });
160
+ const metadataSeeds = this.seedSymbols(target, type, lowerLimit);
161
+ await this.kvsql().hset(symbolKey, metadataSeeds);
162
+ return [lowerLimit + serializer_1.MDATA_SYMBOLS.SLOTS, upperLimit - 1, {}];
163
+ }
164
+ else {
165
+ //if the key already existed, get the lower limit and add the number of symbols
166
+ const range = await this.kvsql().hget(rangeKey, target);
167
+ const [lowerLimitString] = range.split(':');
168
+ if (lowerLimitString === '?') {
169
+ await (0, utils_1.sleepFor)(tryCount * 1000);
170
+ if (tryCount < 5) {
171
+ return this.reserveSymbolRange(target, size, type, tryCount + 1);
172
+ }
173
+ else {
174
+ throw new Error('Symbol range reservation failed due to deployment contention');
175
+ }
176
+ }
177
+ else {
178
+ const lowerLimit = parseInt(lowerLimitString, 10);
179
+ const symbols = await this.kvsql().hgetall(symbolKey);
180
+ const symbolCount = Object.keys(symbols).length;
181
+ const actualLowerLimit = lowerLimit + serializer_1.MDATA_SYMBOLS.SLOTS + symbolCount;
182
+ const upperLimit = Number(lowerLimit + size - 1);
183
+ return [actualLowerLimit, upperLimit, symbols];
184
+ }
185
+ }
186
+ }
187
+ async getAllSymbols() {
188
+ //get hash with all reserved symbol ranges
189
+ const rangeKey = this.mintKey(key_1.KeyType.SYMKEYS, { appId: this.appId });
190
+ const ranges = await this.kvsql().hgetall(rangeKey);
191
+ const rangeKeys = Object.keys(ranges).sort();
192
+ delete rangeKeys[':cursor'];
193
+ //wrap the transact call in kvsql so datatypes are consistent
194
+ const transaction = this.kvsql(this.transact());
195
+ for (const rangeKey of rangeKeys) {
196
+ const symbolKey = this.mintKey(key_1.KeyType.SYMKEYS, {
197
+ activityId: rangeKey,
198
+ appId: this.appId,
199
+ });
200
+ transaction.hgetall(symbolKey);
201
+ }
202
+ const results = (await transaction.exec());
203
+ const symbolSets = {};
204
+ results.forEach((result, index) => {
205
+ if (result) {
206
+ let vals;
207
+ if (Array.isArray(result) && result.length === 2) {
208
+ vals = result[1];
209
+ }
210
+ else {
211
+ vals = result;
212
+ }
213
+ for (const [key, value] of Object.entries(vals)) {
214
+ symbolSets[value] = key.startsWith(rangeKeys[index])
215
+ ? key
216
+ : `${rangeKeys[index]}/${key}`;
217
+ }
218
+ }
219
+ });
220
+ return symbolSets;
221
+ }
222
+ async getSymbols(activityId) {
223
+ let symbols = this.cache.getSymbols(this.appId, activityId);
224
+ if (symbols) {
225
+ return symbols;
226
+ }
227
+ else {
228
+ const params = { activityId, appId: this.appId };
229
+ const key = this.mintKey(key_1.KeyType.SYMKEYS, params);
230
+ symbols = (await this.kvsql().hgetall(key));
231
+ this.cache.setSymbols(this.appId, activityId, symbols);
232
+ return symbols;
233
+ }
234
+ }
235
+ async addSymbols(activityId, symbols) {
236
+ if (!symbols || !Object.keys(symbols).length)
237
+ return false;
238
+ const params = { activityId, appId: this.appId };
239
+ const key = this.mintKey(key_1.KeyType.SYMKEYS, params);
240
+ const success = await this.kvsql().hset(key, symbols);
241
+ this.cache.deleteSymbols(this.appId, activityId);
242
+ return success > 0;
243
+ }
244
+ seedSymbols(target, type, startIndex) {
245
+ if (type === 'JOB') {
246
+ return this.seedJobSymbols(startIndex);
247
+ }
248
+ return this.seedActivitySymbols(startIndex, target);
249
+ }
250
+ seedJobSymbols(startIndex) {
251
+ const hash = {};
252
+ serializer_1.MDATA_SYMBOLS.JOB.KEYS.forEach((key) => {
253
+ hash[`metadata/${key}`] = (0, utils_1.getSymKey)(startIndex);
254
+ startIndex++;
255
+ });
256
+ return hash;
257
+ }
258
+ seedActivitySymbols(startIndex, activityId) {
259
+ const hash = {};
260
+ serializer_1.MDATA_SYMBOLS.ACTIVITY.KEYS.forEach((key) => {
261
+ hash[`${activityId}/output/metadata/${key}`] = (0, utils_1.getSymKey)(startIndex);
262
+ startIndex++;
263
+ });
264
+ return hash;
265
+ }
266
+ async getSymbolValues() {
267
+ let symvals = this.cache.getSymbolValues(this.appId);
268
+ if (symvals) {
269
+ return symvals;
270
+ }
271
+ else {
272
+ const key = this.mintKey(key_1.KeyType.SYMVALS, { appId: this.appId });
273
+ symvals = await this.kvsql().hgetall(key);
274
+ this.cache.setSymbolValues(this.appId, symvals);
275
+ return symvals;
276
+ }
277
+ }
278
+ async addSymbolValues(symvals) {
279
+ if (!symvals || !Object.keys(symvals).length)
280
+ return false;
281
+ const key = this.mintKey(key_1.KeyType.SYMVALS, { appId: this.appId });
282
+ const success = await this.kvsql().hset(key, symvals);
283
+ this.cache.deleteSymbolValues(this.appId);
284
+ return this.isSuccessful(success);
285
+ }
286
+ async getSymbolKeys(symbolNames) {
287
+ const symbolLookups = [];
288
+ for (const symbolName of symbolNames) {
289
+ symbolLookups.push(this.getSymbols(symbolName));
290
+ }
291
+ const symbolSets = await Promise.all(symbolLookups);
292
+ const symKeys = {};
293
+ for (const symbolName of symbolNames) {
294
+ symKeys[symbolName] = symbolSets.shift();
295
+ }
296
+ return symKeys;
297
+ }
298
+ async getApp(id, refresh = false) {
299
+ let app = this.cache.getApp(id);
300
+ if (refresh || !(app && Object.keys(app).length > 0)) {
301
+ const params = { appId: id };
302
+ const key = this.mintKey(key_1.KeyType.APP, params);
303
+ const sApp = await this.kvsql().hgetall(key);
304
+ if (!sApp)
305
+ return null;
306
+ app = {};
307
+ for (const field in sApp) {
308
+ try {
309
+ if (field === 'active') {
310
+ app[field] = sApp[field] === 'true';
311
+ }
312
+ else {
313
+ app[field] = sApp[field];
314
+ }
315
+ }
316
+ catch (e) {
317
+ app[field] = sApp[field];
318
+ }
319
+ }
320
+ this.cache.setApp(id, app);
321
+ }
322
+ return app;
323
+ }
324
+ async setApp(id, version) {
325
+ const params = { appId: id };
326
+ const key = this.mintKey(key_1.KeyType.APP, params);
327
+ const versionId = `versions/${version}`;
328
+ const payload = {
329
+ id,
330
+ version,
331
+ [versionId]: `deployed:${(0, utils_1.formatISODate)(new Date())}`,
332
+ };
333
+ await this.kvsql().hset(key, payload);
334
+ this.cache.setApp(id, payload);
335
+ return payload;
336
+ }
337
+ async activateAppVersion(id, version) {
338
+ const params = { appId: id };
339
+ const key = this.mintKey(key_1.KeyType.APP, params);
340
+ const versionId = `versions/${version}`;
341
+ const app = await this.getApp(id, true);
342
+ if (app && app[versionId]) {
343
+ const payload = {
344
+ id,
345
+ version: version.toString(),
346
+ [versionId]: `activated:${(0, utils_1.formatISODate)(new Date())}`,
347
+ active: true,
348
+ };
349
+ Object.entries(payload).forEach(([key, value]) => {
350
+ payload[key] = value.toString();
351
+ });
352
+ await this.kvsql().hset(key, payload);
353
+ return true;
354
+ }
355
+ throw new Error(`Version ${version} does not exist for app ${id}`);
356
+ }
357
+ async registerAppVersion(appId, version) {
358
+ const params = { appId };
359
+ const key = this.mintKey(key_1.KeyType.APP, params);
360
+ const payload = {
361
+ id: appId,
362
+ version: version.toString(),
363
+ [`versions/${version}`]: (0, utils_1.formatISODate)(new Date()),
364
+ };
365
+ return await this.kvsql().hset(key, payload);
366
+ }
367
+ async setStats(jobKey, jobId, dateTime, stats, appVersion, transaction) {
368
+ const params = {
369
+ appId: appVersion.id,
370
+ jobId,
371
+ jobKey,
372
+ dateTime,
373
+ };
374
+ const localTransaction = transaction || this.transact();
375
+ if (stats.general.length) {
376
+ const generalStatsKey = this.mintKey(key_1.KeyType.JOB_STATS_GENERAL, params);
377
+ for (const { target, value } of stats.general) {
378
+ this.kvsql(localTransaction).hincrbyfloat(generalStatsKey, target, value);
379
+ }
380
+ }
381
+ for (const { target, value } of stats.index) {
382
+ const indexParams = { ...params, facet: target };
383
+ const indexStatsKey = this.mintKey(key_1.KeyType.JOB_STATS_INDEX, indexParams);
384
+ this.kvsql(localTransaction).rpush(indexStatsKey, value.toString());
385
+ }
386
+ for (const { target, value } of stats.median) {
387
+ const medianParams = { ...params, facet: target };
388
+ const medianStatsKey = this.mintKey(key_1.KeyType.JOB_STATS_MEDIAN, medianParams);
389
+ await this.kvsql(localTransaction).zadd(medianStatsKey, Number(value), target);
390
+ }
391
+ if (!transaction) {
392
+ return await localTransaction.exec();
393
+ }
394
+ }
395
+ hGetAllResult(result) {
396
+ //default response signature uses 'redis' NPM Package format
397
+ return result;
398
+ }
399
+ async getJobStats(jobKeys) {
400
+ const transaction = this.kvsql(this.transact());
401
+ for (const jobKey of jobKeys) {
402
+ transaction.hgetall(jobKey);
403
+ }
404
+ const results = await transaction.exec();
405
+ const output = {};
406
+ for (const [index, result] of results.entries()) {
407
+ const key = jobKeys[index];
408
+ const statsHash = result;
409
+ if (statsHash && Object.keys(statsHash).length > 0) {
410
+ const resolvedStatsHash = { ...statsHash };
411
+ for (const [key, val] of Object.entries(resolvedStatsHash)) {
412
+ resolvedStatsHash[key] = Number(val);
413
+ }
414
+ output[key] = resolvedStatsHash;
415
+ }
416
+ else {
417
+ output[key] = {};
418
+ }
419
+ }
420
+ return output;
421
+ }
422
+ async getJobIds(indexKeys, idRange) {
423
+ const transaction = this.kvsql(this.transact());
424
+ for (const idsKey of indexKeys) {
425
+ transaction.lrange(idsKey, idRange[0], idRange[1]); //0,-1 returns all ids
426
+ }
427
+ const results = await transaction.exec();
428
+ const output = {};
429
+ for (const [index, result] of results.entries()) {
430
+ const key = indexKeys[index];
431
+ const idsList = result[1] || result;
432
+ if (idsList && idsList.length > 0) {
433
+ output[key] = idsList;
434
+ }
435
+ else {
436
+ output[key] = [];
437
+ }
438
+ }
439
+ return output;
440
+ }
441
+ async setStatus(collationKeyStatus, jobId, appId, transaction) {
442
+ const jobKey = this.mintKey(key_1.KeyType.JOB_STATE, { appId, jobId });
443
+ return await this.kvsql(transaction).hincrbyfloat(jobKey, ':', collationKeyStatus);
444
+ }
445
+ async getStatus(jobId, appId) {
446
+ const jobKey = this.mintKey(key_1.KeyType.JOB_STATE, { appId, jobId });
447
+ const status = await this.kvsql().hget(jobKey, ':');
448
+ if (status === null) {
449
+ throw new Error(`Job ${jobId} not found`);
450
+ }
451
+ return Number(status);
452
+ }
453
+ async setState({ ...state }, status, jobId, symbolNames, dIds, transaction) {
454
+ delete state['metadata/js'];
455
+ const hashKey = this.mintKey(key_1.KeyType.JOB_STATE, {
456
+ appId: this.appId,
457
+ jobId,
458
+ });
459
+ const symKeys = await this.getSymbolKeys(symbolNames);
460
+ const symVals = await this.getSymbolValues();
461
+ this.serializer.resetSymbols(symKeys, symVals, dIds);
462
+ const hashData = this.serializer.package(state, symbolNames);
463
+ if (status !== null) {
464
+ hashData[':'] = status.toString();
465
+ }
466
+ else {
467
+ delete hashData[':'];
468
+ }
469
+ await this.kvsql(transaction).hset(hashKey, hashData);
470
+ return jobId;
471
+ }
472
+ /**
473
+ * Returns custom search fields and values.
474
+ * NOTE: The `fields` param should NOT prefix items with an underscore.
475
+ * NOTE: Literals are allowed if quoted.
476
+ */
477
+ async getQueryState(jobId, fields) {
478
+ const key = this.mintKey(key_1.KeyType.JOB_STATE, { appId: this.appId, jobId });
479
+ const _fields = fields.map((field) => {
480
+ if (field.startsWith('"')) {
481
+ return field.slice(1, -1);
482
+ }
483
+ return `_${field}`;
484
+ });
485
+ const jobDataArray = await this.kvsql().hmget(key, _fields);
486
+ const jobData = {};
487
+ fields.forEach((field, index) => {
488
+ if (field.startsWith('"')) {
489
+ field = field.slice(1, -1);
490
+ }
491
+ jobData[field] = jobDataArray[index];
492
+ });
493
+ return jobData;
494
+ }
495
+ async getState(jobId, consumes, dIds) {
496
+ //get abbreviated field list (the symbols for the paths)
497
+ const key = this.mintKey(key_1.KeyType.JOB_STATE, { appId: this.appId, jobId });
498
+ const symbolNames = Object.keys(consumes);
499
+ const symKeys = await this.getSymbolKeys(symbolNames);
500
+ this.serializer.resetSymbols(symKeys, {}, dIds);
501
+ const fields = this.serializer.abbreviate(consumes, symbolNames, [':']);
502
+ const jobDataArray = await this.kvsql().hmget(key, fields);
503
+ const jobData = {};
504
+ let atLeast1 = false; //if status field (':') isn't present assume 404
505
+ fields.forEach((field, index) => {
506
+ if (jobDataArray[index]) {
507
+ atLeast1 = true;
508
+ }
509
+ jobData[field] = jobDataArray[index];
510
+ });
511
+ if (atLeast1) {
512
+ const symVals = await this.getSymbolValues();
513
+ this.serializer.resetSymbols(symKeys, symVals, dIds);
514
+ const state = this.serializer.unpackage(jobData, symbolNames);
515
+ let status = 0;
516
+ if (state[':']) {
517
+ status = Number(state[':']);
518
+ state[`metadata/js`] = status;
519
+ delete state[':'];
520
+ }
521
+ return [state, status];
522
+ }
523
+ else {
524
+ throw new errors_1.GetStateError(jobId);
525
+ }
526
+ }
527
+ async getRaw(jobId) {
528
+ const jobKey = this.mintKey(key_1.KeyType.JOB_STATE, {
529
+ appId: this.appId,
530
+ jobId,
531
+ });
532
+ const job = await this.kvsql().hgetall(jobKey);
533
+ if (!job) {
534
+ throw new errors_1.GetStateError(jobId);
535
+ }
536
+ return job;
537
+ }
538
+ /**
539
+ * collate is a generic method for incrementing a value in a hash
540
+ * in order to track their progress during processing.
541
+ */
542
+ async collate(jobId, activityId, amount, dIds, transaction) {
543
+ const jobKey = this.mintKey(key_1.KeyType.JOB_STATE, {
544
+ appId: this.appId,
545
+ jobId,
546
+ });
547
+ const collationKey = `${activityId}/output/metadata/as`; //activity state
548
+ const symbolNames = [activityId];
549
+ const symKeys = await this.getSymbolKeys(symbolNames);
550
+ const symVals = await this.getSymbolValues();
551
+ this.serializer.resetSymbols(symKeys, symVals, dIds);
552
+ const payload = { [collationKey]: amount.toString() };
553
+ const hashData = this.serializer.package(payload, symbolNames);
554
+ const targetId = Object.keys(hashData)[0];
555
+ return await this.kvsql(transaction).hincrbyfloat(jobKey, targetId, amount);
556
+ }
557
+ /**
558
+ * Synthentic collation affects those activities in the graph
559
+ * that represent the synthetic DAG that was materialized during compilation;
560
+ * Synthetic collation distinguishes `re-entry due to failure` from
561
+ * `purposeful re-entry`.
562
+ */
563
+ async collateSynthetic(jobId, guid, amount, transaction) {
564
+ const jobKey = this.mintKey(key_1.KeyType.JOB_STATE, {
565
+ appId: this.appId,
566
+ jobId,
567
+ });
568
+ return await this.kvsql(transaction).hincrbyfloat(jobKey, guid, amount);
569
+ }
570
+ async setStateNX(jobId, appId, status) {
571
+ const hashKey = this.mintKey(key_1.KeyType.JOB_STATE, { appId, jobId });
572
+ const result = await this.kvsql().hsetnx(hashKey, ':', status?.toString() ?? '1');
573
+ return this.isSuccessful(result);
574
+ }
575
+ async getSchema(activityId, appVersion) {
576
+ const schema = this.cache.getSchema(appVersion.id, appVersion.version, activityId);
577
+ if (schema) {
578
+ return schema;
579
+ }
580
+ else {
581
+ const schemas = await this.getSchemas(appVersion);
582
+ return schemas[activityId];
583
+ }
584
+ }
585
+ async getSchemas(appVersion) {
586
+ let schemas = this.cache.getSchemas(appVersion.id, appVersion.version);
587
+ if (schemas && Object.keys(schemas).length > 0) {
588
+ return schemas;
589
+ }
590
+ else {
591
+ const params = {
592
+ appId: appVersion.id,
593
+ appVersion: appVersion.version,
594
+ };
595
+ const key = this.mintKey(key_1.KeyType.SCHEMAS, params);
596
+ schemas = {};
597
+ const hash = await this.kvsql().hgetall(key);
598
+ Object.entries(hash).forEach(([key, value]) => {
599
+ schemas[key] = JSON.parse(value);
600
+ });
601
+ this.cache.setSchemas(appVersion.id, appVersion.version, schemas);
602
+ return schemas;
603
+ }
604
+ }
605
+ async setSchemas(schemas, appVersion) {
606
+ const params = {
607
+ appId: appVersion.id,
608
+ appVersion: appVersion.version,
609
+ };
610
+ const key = this.mintKey(key_1.KeyType.SCHEMAS, params);
611
+ const _schemas = { ...schemas };
612
+ Object.entries(_schemas).forEach(([key, value]) => {
613
+ _schemas[key] = JSON.stringify(value);
614
+ });
615
+ const response = await this.kvsql().hset(key, _schemas);
616
+ this.cache.setSchemas(appVersion.id, appVersion.version, schemas);
617
+ return response;
618
+ }
619
+ async setSubscriptions(subscriptions, appVersion) {
620
+ const params = {
621
+ appId: appVersion.id,
622
+ appVersion: appVersion.version,
623
+ };
624
+ const key = this.mintKey(key_1.KeyType.SUBSCRIPTIONS, params);
625
+ const _subscriptions = { ...subscriptions };
626
+ Object.entries(_subscriptions).forEach(([key, value]) => {
627
+ _subscriptions[key] = JSON.stringify(value);
628
+ });
629
+ const status = await this.kvsql().hset(key, _subscriptions);
630
+ this.cache.setSubscriptions(appVersion.id, appVersion.version, subscriptions);
631
+ return this.isSuccessful(status);
632
+ }
633
+ async getSubscriptions(appVersion) {
634
+ let subscriptions = this.cache.getSubscriptions(appVersion.id, appVersion.version);
635
+ if (subscriptions && Object.keys(subscriptions).length > 0) {
636
+ return subscriptions;
637
+ }
638
+ else {
639
+ const params = {
640
+ appId: appVersion.id,
641
+ appVersion: appVersion.version,
642
+ };
643
+ const key = this.mintKey(key_1.KeyType.SUBSCRIPTIONS, params);
644
+ subscriptions = await this.kvsql().hgetall(key) || {};
645
+ Object.entries(subscriptions).forEach(([key, value]) => {
646
+ subscriptions[key] = JSON.parse(value);
647
+ });
648
+ this.cache.setSubscriptions(appVersion.id, appVersion.version, subscriptions);
649
+ return subscriptions;
650
+ }
651
+ }
652
+ async getSubscription(topic, appVersion) {
653
+ const subscriptions = await this.getSubscriptions(appVersion);
654
+ return subscriptions[topic];
655
+ }
656
+ async setTransitions(transitions, appVersion) {
657
+ const params = {
658
+ appId: appVersion.id,
659
+ appVersion: appVersion.version,
660
+ };
661
+ const key = this.mintKey(key_1.KeyType.SUBSCRIPTION_PATTERNS, params);
662
+ const _subscriptions = { ...transitions };
663
+ Object.entries(_subscriptions).forEach(([key, value]) => {
664
+ _subscriptions[key] = JSON.stringify(value);
665
+ });
666
+ if (Object.keys(_subscriptions).length !== 0) {
667
+ const response = await this.kvsql().hset(key, _subscriptions);
668
+ this.cache.setTransitions(appVersion.id, appVersion.version, transitions);
669
+ return response;
670
+ }
671
+ }
672
+ async getTransitions(appVersion) {
673
+ let transitions = this.cache.getTransitions(appVersion.id, appVersion.version);
674
+ if (transitions && Object.keys(transitions).length > 0) {
675
+ return transitions;
676
+ }
677
+ else {
678
+ const params = {
679
+ appId: appVersion.id,
680
+ appVersion: appVersion.version,
681
+ };
682
+ const key = this.mintKey(key_1.KeyType.SUBSCRIPTION_PATTERNS, params);
683
+ transitions = {};
684
+ const hash = await this.kvsql().hgetall(key);
685
+ Object.entries(hash).forEach(([key, value]) => {
686
+ transitions[key] = JSON.parse(value);
687
+ });
688
+ this.cache.setTransitions(appVersion.id, appVersion.version, transitions);
689
+ return transitions;
690
+ }
691
+ }
692
+ async setHookRules(hookRules) {
693
+ const key = this.mintKey(key_1.KeyType.HOOKS, { appId: this.appId });
694
+ const _hooks = {};
695
+ Object.entries(hookRules).forEach(([key, value]) => {
696
+ _hooks[key.toString()] = JSON.stringify(value);
697
+ });
698
+ if (Object.keys(_hooks).length !== 0) {
699
+ const response = await this.kvsql().hset(key, _hooks);
700
+ this.cache.setHookRules(this.appId, hookRules);
701
+ return response;
702
+ }
703
+ }
704
+ async getHookRules() {
705
+ let patterns = this.cache.getHookRules(this.appId);
706
+ if (patterns && Object.keys(patterns).length > 0) {
707
+ return patterns;
708
+ }
709
+ else {
710
+ const key = this.mintKey(key_1.KeyType.HOOKS, { appId: this.appId });
711
+ const _hooks = await this.kvsql().hgetall(key);
712
+ patterns = {};
713
+ Object.entries(_hooks).forEach(([key, value]) => {
714
+ patterns[key] = JSON.parse(value);
715
+ });
716
+ this.cache.setHookRules(this.appId, patterns);
717
+ return patterns;
718
+ }
719
+ }
720
+ async setHookSignal(hook, transaction) {
721
+ const key = this.mintKey(key_1.KeyType.SIGNALS, { appId: this.appId });
722
+ const { topic, resolved, jobId } = hook;
723
+ const signalKey = `${topic}:${resolved}`;
724
+ await this.kvsql(transaction).setnxex(`${key}:${signalKey}`, jobId, Math.max(hook.expire, enums_1.HMSH_SIGNAL_EXPIRE));
725
+ }
726
+ async getHookSignal(topic, resolved) {
727
+ const key = this.mintKey(key_1.KeyType.SIGNALS, { appId: this.appId });
728
+ const response = await this.kvsql().get(`${key}:${topic}:${resolved}`);
729
+ return response ? response.toString() : undefined;
730
+ }
731
+ async deleteHookSignal(topic, resolved) {
732
+ const key = this.mintKey(key_1.KeyType.SIGNALS, { appId: this.appId });
733
+ const response = await this.kvsql().del(`${key}:${topic}:${resolved}`);
734
+ return response ? Number(response) : undefined;
735
+ }
736
+ async addTaskQueues(keys) {
737
+ const transaction = this.kvsql(this.transact());
738
+ const zsetKey = this.mintKey(key_1.KeyType.WORK_ITEMS, { appId: this.appId });
739
+ for (const key of keys) {
740
+ transaction.zadd(zsetKey, Date.now(), key, { nx: true });
741
+ }
742
+ await transaction.exec();
743
+ }
744
+ async getActiveTaskQueue() {
745
+ let workItemKey = this.cache.getActiveTaskQueue(this.appId) || null;
746
+ if (!workItemKey) {
747
+ const zsetKey = this.mintKey(key_1.KeyType.WORK_ITEMS, { appId: this.appId });
748
+ const result = await this.kvsql().zrange(zsetKey, 0, 0);
749
+ workItemKey = result.length > 0 ? result[0] : null;
750
+ if (workItemKey) {
751
+ this.cache.setWorkItem(this.appId, workItemKey);
752
+ }
753
+ }
754
+ return workItemKey;
755
+ }
756
+ async deleteProcessedTaskQueue(workItemKey, key, processedKey, scrub = false) {
757
+ const zsetKey = this.mintKey(key_1.KeyType.WORK_ITEMS, { appId: this.appId });
758
+ const didRemove = await this.kvsql().zrem(zsetKey, workItemKey);
759
+ if (didRemove) {
760
+ if (scrub) {
761
+ //indexes can be designed to be self-cleaning; `engine.hookAll` exposes this option
762
+ this.kvsql().del(processedKey);
763
+ this.kvsql().del(key.split(':').slice(0, 5).join(':'));
764
+ }
765
+ else {
766
+ await this.kvsql().rename(processedKey, key);
767
+ }
768
+ }
769
+ this.cache.removeWorkItem(this.appId);
770
+ }
771
+ async processTaskQueue(sourceKey, destinationKey) {
772
+ return await this.kvsql().lmove(sourceKey, destinationKey, 'LEFT', 'RIGHT');
773
+ }
774
+ async expireJob(jobId, inSeconds, transaction) {
775
+ if (!isNaN(inSeconds) && inSeconds > 0) {
776
+ const jobKey = this.mintKey(key_1.KeyType.JOB_STATE, {
777
+ appId: this.appId,
778
+ jobId,
779
+ });
780
+ await this.kvsql(transaction).expire(jobKey, inSeconds);
781
+ }
782
+ }
783
+ async getDependencies(jobId) {
784
+ const depParams = { appId: this.appId, jobId };
785
+ const depKey = this.mintKey(key_1.KeyType.JOB_DEPENDENTS, depParams);
786
+ return this.kvsql().lrange(depKey, 0, -1);
787
+ }
788
+ /**
789
+ * registers a hook activity to be awakened (uses ZSET to
790
+ * store the 'sleep group' and LIST to store the events
791
+ * for the given sleep group. Sleep groups are
792
+ * organized into 'n'-second blocks (LISTS))
793
+ */
794
+ async registerTimeHook(jobId, gId, activityId, type, deletionTime, dad, transaction) {
795
+ const listKey = this.mintKey(key_1.KeyType.TIME_RANGE, {
796
+ appId: this.appId,
797
+ timeValue: deletionTime,
798
+ });
799
+ //register the task in the LIST
800
+ const timeEvent = [type, activityId, gId, dad, jobId].join(key_1.VALSEP);
801
+ const len = await this.kvsql(transaction).rpush(listKey, timeEvent);
802
+ //register the LIST in the ZSET
803
+ if (transaction || len === 1) {
804
+ const zsetKey = this.mintKey(key_1.KeyType.TIME_RANGE, { appId: this.appId });
805
+ await this.zAdd(zsetKey, deletionTime.toString(), listKey, transaction);
806
+ }
807
+ }
808
+ async getNextTask(listKey) {
809
+ const zsetKey = this.mintKey(key_1.KeyType.TIME_RANGE, { appId: this.appId });
810
+ listKey = listKey || await this.zRangeByScore(zsetKey, 0, Date.now());
811
+ if (listKey) {
812
+ let [pType, pKey] = this.resolveTaskKeyContext(listKey);
813
+ const timeEvent = await this.kvsql().lpop(pKey);
814
+ if (timeEvent) {
815
+ //deconstruct composite key
816
+ let [type, activityId, gId, _pd, ...jobId] = timeEvent.split(key_1.VALSEP);
817
+ const jid = jobId.join(key_1.VALSEP);
818
+ if (type === 'delist') {
819
+ pType = 'delist';
820
+ }
821
+ else if (type === 'child') {
822
+ pType = 'child';
823
+ }
824
+ else if (type === 'expire-child') {
825
+ type = 'expire';
826
+ }
827
+ return [listKey, jid, gId, activityId, pType];
828
+ }
829
+ await this.kvsql().zrem(zsetKey, listKey);
830
+ return true;
831
+ }
832
+ return false;
833
+ }
834
+ /**
835
+ * when processing time jobs, the target LIST ID returned
836
+ * from the ZSET query can be prefixed to denote what to
837
+ * do with the work list. (not everything is known in advance,
838
+ * so the ZSET key defines HOW to approach the work in the
839
+ * generic LIST (lists typically contain target job ids)
840
+ * @param {string} listKey - composite key
841
+ */
842
+ resolveTaskKeyContext(listKey) {
843
+ if (listKey.startsWith(`${key_1.TYPSEP}INTERRUPT`)) {
844
+ return ['interrupt', listKey.split(key_1.TYPSEP)[2]];
845
+ }
846
+ else if (listKey.startsWith(`${key_1.TYPSEP}EXPIRE`)) {
847
+ return ['expire', listKey.split(key_1.TYPSEP)[2]];
848
+ }
849
+ else {
850
+ return ['sleep', listKey];
851
+ }
852
+ }
853
+ /**
854
+ * Interrupts a job and sets sets a job error (410), if 'throw'!=false.
855
+ * This method is called by the engine and not by an activity and is
856
+ * followed by a call to execute job completion/cleanup tasks
857
+ * associated with a job completion event.
858
+ *
859
+ * Todo: move most of this logic to the engine (too much logic for the store)
860
+ */
861
+ async interrupt(topic, jobId, options = {}) {
862
+ try {
863
+ //verify job exists
864
+ const status = await this.getStatus(jobId, this.appId);
865
+ if (status <= 0) {
866
+ //verify still active; job already completed
867
+ throw new Error(`Job ${jobId} already completed`);
868
+ }
869
+ //decrement job status (:) by 1bil
870
+ const amount = -1000000000;
871
+ const jobKey = this.mintKey(key_1.KeyType.JOB_STATE, {
872
+ appId: this.appId,
873
+ jobId,
874
+ });
875
+ const result = await this.kvsql().hincrbyfloat(jobKey, ':', amount);
876
+ if (result <= amount) {
877
+ //verify active state; job already interrupted
878
+ throw new Error(`Job ${jobId} already completed`);
879
+ }
880
+ //persist the error unless specifically told not to
881
+ if (options.throw !== false) {
882
+ const errKey = `metadata/err`; //job errors are stored at the path `metadata/err`
883
+ const symbolNames = [`$${topic}`]; //the symbol for `metadata/err` is in the backend
884
+ const symKeys = await this.getSymbolKeys(symbolNames);
885
+ const symVals = await this.getSymbolValues();
886
+ this.serializer.resetSymbols(symKeys, symVals, {});
887
+ //persists the standard 410 error (job is `gone`)
888
+ const err = JSON.stringify({
889
+ code: options.code ?? enums_1.HMSH_CODE_INTERRUPT,
890
+ message: options.reason ?? `job [${jobId}] interrupted`,
891
+ stack: options.stack ?? '',
892
+ job_id: jobId,
893
+ });
894
+ const payload = { [errKey]: amount.toString() };
895
+ const hashData = this.serializer.package(payload, symbolNames);
896
+ const errSymbol = Object.keys(hashData)[0];
897
+ await this.kvsql().hset(jobKey, { [errSymbol]: err });
898
+ }
899
+ }
900
+ catch (e) {
901
+ if (!options.suppress) {
902
+ throw e;
903
+ }
904
+ else {
905
+ this.logger.debug('suppressed-interrupt', { message: e.message });
906
+ }
907
+ }
908
+ }
909
+ async scrub(jobId) {
910
+ const jobKey = this.mintKey(key_1.KeyType.JOB_STATE, {
911
+ appId: this.appId,
912
+ jobId,
913
+ });
914
+ await this.kvsql().del(jobKey);
915
+ }
916
+ async findJobs(queryString = '*', limit = 1000,
917
+ //NOTE: unused in SQL provider; leave to keep signature consistent
918
+ batchSize = 1000, cursor = '0') {
919
+ const matchKey = this.mintKey(key_1.KeyType.JOB_STATE, {
920
+ appId: this.appId,
921
+ jobId: queryString,
922
+ });
923
+ const { cursor: _cursor, keys } = await this.kvsql().scan(Number(cursor), limit, matchKey);
924
+ return [_cursor.toPrecision(), keys];
925
+ }
926
+ async findJobFields(jobId, fieldMatchPattern = '*', limit = 1000, batchSize = 1000, // Unused in SQL provider
927
+ cursor = '0') {
928
+ const matchingFields = {};
929
+ const jobKey = this.mintKey(key_1.KeyType.JOB_STATE, {
930
+ appId: this.appId,
931
+ jobId,
932
+ });
933
+ let enumType;
934
+ let dimension = null;
935
+ if (fieldMatchPattern.includes(',')) {
936
+ const dimReg = /\d[^-]+-/gi;
937
+ const dimensions = fieldMatchPattern.match(dimReg);
938
+ dimension = ',' + (dimensions?.[0] ?? '');
939
+ enumType = 'hmark';
940
+ }
941
+ else {
942
+ enumType = 'jmark';
943
+ }
944
+ const offset = parseInt(cursor, 10) || 0; // Convert cursor to numeric offset
945
+ const tableName = this.kvsql().tableForKey(jobKey, 'hash');
946
+ // Initialize parameters array and parameter index
947
+ const params = [jobKey];
948
+ let paramIndex = params.length + 1; // Starts from 2 since $1 is jobKey
949
+ // Build the valid_job CTE to get the job's UUID id
950
+ const validJobSql = `
951
+ SELECT id
952
+ FROM ${tableName}
953
+ WHERE key = $1
954
+ AND (expired_at IS NULL OR expired_at > NOW())
955
+ LIMIT 1
956
+ `;
957
+ // Build conditions for the WHERE clause
958
+ const conditions = [];
959
+ // Add enumType condition
960
+ conditions.push(`a.type = $${paramIndex}`);
961
+ params.push(enumType);
962
+ paramIndex++;
963
+ // Add dimension condition if applicable
964
+ if (dimension) {
965
+ conditions.push(`a.field LIKE $${paramIndex}`);
966
+ params.push(`%${dimension}%`);
967
+ paramIndex++;
968
+ }
969
+ // Add limit and offset parameters
970
+ const limitParamIndex = paramIndex;
971
+ const offsetParamIndex = paramIndex + 1;
972
+ params.push(limit, offset);
973
+ paramIndex += 2;
974
+ // Construct the final SQL query
975
+ const sql = `
976
+ WITH valid_job AS (
977
+ ${validJobSql}
978
+ )
979
+ SELECT a.field, a.value
980
+ FROM ${tableName}_attributes a
981
+ JOIN valid_job j ON a.job_id = j.id
982
+ WHERE ${conditions.join(' AND ')}
983
+ LIMIT $${limitParamIndex} OFFSET $${offsetParamIndex}
984
+ `;
985
+ // Execute the query and map the results
986
+ const res = await this.pgClient.query(sql, params);
987
+ for (const row of res.rows) {
988
+ matchingFields[row.field] = row.value;
989
+ }
990
+ // Determine the next cursor
991
+ const nextCursor = res.rows.length < limit ? '0' : String(offset + res.rows.length);
992
+ return [nextCursor, matchingFields];
993
+ }
994
+ async setThrottleRate(options) {
995
+ const key = this.mintKey(key_1.KeyType.THROTTLE_RATE, { appId: this.appId });
996
+ //engine guids are session specific. no need to persist
997
+ if (options.guid) {
998
+ return;
999
+ }
1000
+ //if a topic, update one
1001
+ const rate = options.throttle.toString();
1002
+ if (options.topic) {
1003
+ await this.kvsql().hset(key, {
1004
+ [options.topic]: rate,
1005
+ });
1006
+ }
1007
+ else {
1008
+ //if no topic, update all
1009
+ const transaction = this.transact();
1010
+ transaction.del(key);
1011
+ transaction.hset(key, { ':': rate });
1012
+ await transaction.exec();
1013
+ }
1014
+ }
1015
+ async getThrottleRates() {
1016
+ const key = this.mintKey(key_1.KeyType.THROTTLE_RATE, { appId: this.appId });
1017
+ const response = await this.kvsql().hgetall(key);
1018
+ return response ?? {};
1019
+ }
1020
+ async getThrottleRate(topic) {
1021
+ //always return a valid number range
1022
+ const resolveRate = (response, topic) => {
1023
+ const rate = topic in response ? Number(response[topic]) : 0;
1024
+ if (isNaN(rate))
1025
+ return 0;
1026
+ if (rate == -1)
1027
+ return enums_1.MAX_DELAY;
1028
+ return Math.max(Math.min(rate, enums_1.MAX_DELAY), 0);
1029
+ };
1030
+ const response = await this.getThrottleRates();
1031
+ const globalRate = resolveRate(response, ':');
1032
+ if (topic === ':' || !(topic in response)) {
1033
+ //use global rate unless worker specifies rate
1034
+ return globalRate;
1035
+ }
1036
+ return resolveRate(response, topic);
1037
+ }
1038
+ /**
1039
+ * Deploy time-aware notification triggers and functions
1040
+ */
1041
+ async deployTimeNotificationTriggers(appId) {
1042
+ const schemaName = this.kvsql().safeName(appId);
1043
+ const client = this.pgClient;
1044
+ try {
1045
+ // Read the SQL template and replace schema placeholder
1046
+ const fs = await Promise.resolve().then(() => __importStar(require('fs')));
1047
+ const path = await Promise.resolve().then(() => __importStar(require('path')));
1048
+ const sqlTemplate = fs.readFileSync(path.join(__dirname, 'time-notify.sql'), 'utf8');
1049
+ const sql = sqlTemplate.replace(/{schema}/g, schemaName);
1050
+ // Execute the entire SQL as one statement (functions contain $$ blocks with semicolons)
1051
+ await client.query(sql);
1052
+ this.logger.info('postgres-time-notifications-deployed', {
1053
+ appId,
1054
+ schemaName,
1055
+ message: 'Time-aware notifications ENABLED - using LISTEN/NOTIFY instead of polling'
1056
+ });
1057
+ }
1058
+ catch (error) {
1059
+ this.logger.error('postgres-time-notifications-deploy-error', {
1060
+ appId,
1061
+ schemaName,
1062
+ error: error.message
1063
+ });
1064
+ // Don't throw - fall back to polling mode
1065
+ }
1066
+ }
1067
+ /**
1068
+ * Enhanced time scout that uses LISTEN/NOTIFY to reduce polling
1069
+ */
1070
+ async startTimeScoutWithNotifications(timeEventCallback) {
1071
+ const channelName = `time_hooks_${this.appId}`;
1072
+ try {
1073
+ // Set up LISTEN for time notifications
1074
+ await this.pgClient.query(`LISTEN "${channelName}"`);
1075
+ // Set up notification handler
1076
+ this.pgClient.on('notification', (notification) => {
1077
+ this.handleTimeNotification(notification, timeEventCallback);
1078
+ });
1079
+ this.logger.debug('postgres-time-scout-notifications-started', {
1080
+ appId: this.appId,
1081
+ channelName
1082
+ });
1083
+ // Start the enhanced time scout loop
1084
+ await this.processTimeHooksWithNotifications(timeEventCallback);
1085
+ }
1086
+ catch (error) {
1087
+ this.logger.error('postgres-time-scout-notifications-error', {
1088
+ appId: this.appId,
1089
+ error
1090
+ });
1091
+ // Fall back to regular polling mode
1092
+ throw error;
1093
+ }
1094
+ }
1095
+ /**
1096
+ * Handle time notifications from PostgreSQL
1097
+ */
1098
+ async handleTimeNotification(notification, timeEventCallback) {
1099
+ try {
1100
+ const payload = JSON.parse(notification.payload);
1101
+ const { type, app_id, next_awakening, ready_at } = payload;
1102
+ if (app_id !== this.appId) {
1103
+ return; // Not for this app
1104
+ }
1105
+ this.logger.debug('postgres-time-notification-received', {
1106
+ type,
1107
+ appId: app_id,
1108
+ nextAwakening: next_awakening,
1109
+ readyAt: ready_at
1110
+ });
1111
+ if (type === 'time_hooks_ready') {
1112
+ // Process any ready time hooks immediately
1113
+ await this.processReadyTimeHooks(timeEventCallback);
1114
+ }
1115
+ else if (type === 'time_schedule_updated') {
1116
+ // Update our sleep timing if we're the time scout
1117
+ await this.updateTimeScoutSleep(next_awakening);
1118
+ }
1119
+ }
1120
+ catch (error) {
1121
+ this.logger.error('postgres-time-notification-handle-error', {
1122
+ notification,
1123
+ error
1124
+ });
1125
+ }
1126
+ }
1127
+ /**
1128
+ * Process time hooks that are ready to be awakened
1129
+ */
1130
+ async processReadyTimeHooks(timeEventCallback) {
1131
+ let hasMoreTasks = true;
1132
+ while (hasMoreTasks) {
1133
+ const workListTask = await this.getNextTask();
1134
+ if (Array.isArray(workListTask)) {
1135
+ const [listKey, target, gId, activityId, type] = workListTask;
1136
+ if (type === 'child') {
1137
+ // Skip child tasks - they're handled by ancestors
1138
+ }
1139
+ else if (type === 'delist') {
1140
+ // Delist the signal key
1141
+ const key = this.mintKey(key_1.KeyType.SIGNALS, { appId: this.appId });
1142
+ await this.delistSignalKey(key, target);
1143
+ }
1144
+ else {
1145
+ // Process the task
1146
+ await timeEventCallback(target, gId, activityId, type);
1147
+ }
1148
+ }
1149
+ else if (workListTask === true) {
1150
+ // A worklist was emptied, continue processing
1151
+ continue;
1152
+ }
1153
+ else {
1154
+ // No more tasks ready
1155
+ hasMoreTasks = false;
1156
+ }
1157
+ }
1158
+ }
1159
+ /**
1160
+ * Enhanced time scout process that uses notifications
1161
+ */
1162
+ async processTimeHooksWithNotifications(timeEventCallback) {
1163
+ let currentSleepTimeout = null;
1164
+ // Function to calculate next sleep duration
1165
+ const calculateNextSleep = async () => {
1166
+ const nextAwakeningTime = await this.getNextAwakeningTime();
1167
+ if (nextAwakeningTime) {
1168
+ const sleepMs = nextAwakeningTime - Date.now();
1169
+ return Math.max(sleepMs, 0);
1170
+ }
1171
+ // Default sleep if no tasks scheduled
1172
+ return enums_1.HMSH_FIDELITY_SECONDS * 1000;
1173
+ };
1174
+ // Main loop
1175
+ while (true) {
1176
+ try {
1177
+ if (await this.shouldScout()) {
1178
+ // Process any ready tasks
1179
+ await this.processReadyTimeHooks(timeEventCallback);
1180
+ // Calculate next sleep
1181
+ const sleepMs = await calculateNextSleep();
1182
+ // Sleep with ability to be interrupted by notifications
1183
+ await new Promise((resolve) => {
1184
+ currentSleepTimeout = setTimeout(resolve, sleepMs);
1185
+ });
1186
+ }
1187
+ else {
1188
+ // Not the scout, sleep longer
1189
+ await (0, utils_1.sleepFor)(enums_1.HMSH_SCOUT_INTERVAL_SECONDS * 1000);
1190
+ }
1191
+ }
1192
+ catch (error) {
1193
+ this.logger.error('postgres-time-scout-loop-error', { error });
1194
+ await (0, utils_1.sleepFor)(1000);
1195
+ }
1196
+ }
1197
+ }
1198
+ /**
1199
+ * Get the next awakening time from the database
1200
+ */
1201
+ async getNextAwakeningTime() {
1202
+ const schemaName = this.kvsql().safeName(this.appId);
1203
+ const appKey = `${this.appId}:time_range`;
1204
+ try {
1205
+ const result = await this.pgClient.query(`SELECT ${schemaName}.get_next_awakening_time($1) as next_time`, [appKey]);
1206
+ if (result.rows[0]?.next_time) {
1207
+ return new Date(result.rows[0].next_time).getTime();
1208
+ }
1209
+ return null;
1210
+ }
1211
+ catch (error) {
1212
+ this.logger.error('postgres-get-next-awakening-error', { error });
1213
+ return null;
1214
+ }
1215
+ }
1216
+ /**
1217
+ * Update the time scout's sleep timing based on schedule changes
1218
+ */
1219
+ async updateTimeScoutSleep(nextAwakening) {
1220
+ // This could be used to interrupt current sleep and recalculate
1221
+ // For now, just log the schedule update
1222
+ this.logger.debug('postgres-time-schedule-updated', {
1223
+ nextAwakening,
1224
+ currentTime: Date.now()
1225
+ });
1226
+ }
1227
+ /**
1228
+ * Enhanced shouldScout that can handle notifications
1229
+ */
1230
+ async shouldScout() {
1231
+ const wasScout = this.isScout;
1232
+ const isScout = wasScout || (this.isScout = await this.reserveScoutRole('time'));
1233
+ if (isScout) {
1234
+ if (!wasScout) {
1235
+ setTimeout(() => {
1236
+ this.isScout = false;
1237
+ }, enums_1.HMSH_SCOUT_INTERVAL_SECONDS * 1000);
1238
+ }
1239
+ return true;
1240
+ }
1241
+ return false;
1242
+ }
1243
+ }
1244
+ exports.PostgresStoreService = PostgresStoreService;