@hotmeshio/hotmesh 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/build/package.json +3 -3
- package/build/services/activities/activity.d.ts +2 -0
- package/build/services/activities/activity.js +27 -9
- package/build/services/activities/worker.js +1 -0
- package/build/services/collator/index.d.ts +7 -5
- package/build/services/collator/index.js +29 -6
- package/build/services/compiler/deployer.d.ts +4 -3
- package/build/services/compiler/deployer.js +14 -0
- package/build/services/dimension/index.d.ts +1 -1
- package/build/services/durable/client.d.ts +3 -3
- package/build/services/durable/client.js +10 -1
- package/build/services/durable/handle.d.ts +1 -1
- package/build/services/durable/worker.js +20 -2
- package/build/services/engine/index.d.ts +1 -1
- package/build/services/engine/index.js +22 -5
- package/build/services/hotmesh/index.d.ts +1 -1
- package/build/services/hotmesh/index.js +2 -2
- package/build/services/serializer/index.d.ts +6 -1
- package/build/services/serializer/index.js +55 -22
- package/build/services/signaler/stream.js +1 -0
- package/build/services/store/index.d.ts +3 -3
- package/build/services/store/index.js +16 -24
- package/build/types/activity.d.ts +1 -0
- package/build/types/hotmesh.d.ts +5 -5
- package/package.json +3 -3
- package/services/activities/activity.ts +28 -9
- package/services/activities/worker.ts +1 -0
- package/services/collator/index.ts +38 -13
- package/services/compiler/deployer.ts +24 -9
- package/services/dimension/index.ts +1 -1
- package/services/durable/client.ts +13 -6
- package/services/durable/handle.ts +2 -2
- package/services/durable/worker.ts +16 -2
- package/services/engine/index.ts +23 -5
- package/services/hotmesh/index.ts +2 -2
- package/services/mapper/index.ts +0 -1
- package/services/serializer/index.ts +57 -22
- package/services/signaler/stream.ts +1 -0
- package/services/store/index.ts +15 -23
- package/types/activity.ts +1 -0
- package/types/hotmesh.ts +5 -5
package/services/engine/index.ts
CHANGED
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
StreamError,
|
|
64
64
|
StreamRole,
|
|
65
65
|
StreamStatus } from '../../types/stream';
|
|
66
|
+
import { StringStringType } from '../../types';
|
|
66
67
|
|
|
67
68
|
//wait time to see if a job is complete
|
|
68
69
|
const OTT_WAIT_TIME = 1000;
|
|
@@ -408,21 +409,37 @@ class EngineService {
|
|
|
408
409
|
}
|
|
409
410
|
|
|
410
411
|
// ****************** `HOOK` ACTIVITY RE-ENTRY POINT *****************
|
|
411
|
-
async hook(topic: string, data: JobData): Promise<JobStatus | void> {
|
|
412
|
+
async hook(topic: string, data: JobData, dad?: string): Promise<JobStatus | void> {
|
|
412
413
|
const hookRule = await this.storeSignaler.getHookRule(topic);
|
|
414
|
+
const [aid, schema] = await this.getSchema(`.${hookRule.to}`);
|
|
415
|
+
if (!dad) {
|
|
416
|
+
//assume dimensional address is singular (0)
|
|
417
|
+
// for ancestors and self if not provided
|
|
418
|
+
// todo: register
|
|
419
|
+
dad = ',0'.repeat(schema.ancestors.length + 1);
|
|
420
|
+
}
|
|
413
421
|
const streamData: StreamData = {
|
|
414
422
|
type: StreamDataType.WEBHOOK,
|
|
415
|
-
metadata: {
|
|
423
|
+
metadata: {
|
|
424
|
+
//jid is unknown at this point; will be resolved using the data
|
|
425
|
+
aid,
|
|
426
|
+
dad,
|
|
427
|
+
topic
|
|
428
|
+
},
|
|
416
429
|
data,
|
|
417
430
|
};
|
|
418
431
|
await this.streamSignaler.publishMessage(null, streamData);
|
|
419
432
|
}
|
|
420
433
|
async hookTime(jobId: string, activityId: string): Promise<JobStatus | void> {
|
|
434
|
+
//the activityid is concatenated with its dimensional address (dad); split to resolve
|
|
435
|
+
const [aid, ...dimensions] = activityId.split(',');
|
|
436
|
+
const dad = `,${dimensions.join(',')}`;
|
|
421
437
|
const streamData: StreamData = {
|
|
422
438
|
type: StreamDataType.TIMEHOOK,
|
|
423
439
|
metadata: {
|
|
424
440
|
jid: jobId,
|
|
425
|
-
aid
|
|
441
|
+
aid,
|
|
442
|
+
dad,
|
|
426
443
|
},
|
|
427
444
|
data: { timestamp: Date.now() },
|
|
428
445
|
};
|
|
@@ -586,12 +603,13 @@ class EngineService {
|
|
|
586
603
|
// (e.g, if {dimensions:true}, use hscan to deliver
|
|
587
604
|
// the full set of dimensional job data)
|
|
588
605
|
async getState(topic: string, jobId: string): Promise<JobOutput> {
|
|
589
|
-
const { id: appId } = await this.getVID();
|
|
590
606
|
const jobSymbols = await this.store.getSymbols(`$${topic}`);
|
|
591
607
|
const consumes: Consumes = {
|
|
592
608
|
[`$${topic}`]: Object.keys(jobSymbols)
|
|
593
609
|
}
|
|
594
|
-
|
|
610
|
+
//job data exists at the 'zero' dimension; pass an empty object
|
|
611
|
+
const dIds = {} as StringStringType;
|
|
612
|
+
const output = await this.store.getState(jobId, consumes, dIds);
|
|
595
613
|
if (!output) {
|
|
596
614
|
throw new Error(`not found ${jobId}`);
|
|
597
615
|
}
|
|
@@ -161,9 +161,9 @@ class HotMeshService {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
// ****** `HOOK` ACTIVITY RE-ENTRY POINT ******
|
|
164
|
-
async hook(topic: string, data: JobData): Promise<JobStatus | void> {
|
|
164
|
+
async hook(topic: string, data: JobData, dad?: string): Promise<JobStatus | void> {
|
|
165
165
|
//return collation int
|
|
166
|
-
return await this.engine?.hook(topic, data);
|
|
166
|
+
return await this.engine?.hook(topic, data, dad);
|
|
167
167
|
}
|
|
168
168
|
async hookAll(hookTopic: string, data: JobData, query: JobStatsInput, queryFacets: string[] = []): Promise<string[]> {
|
|
169
169
|
return await this.engine?.hookAll(hookTopic, data, query, queryFacets);
|
package/services/mapper/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getSymVal } from '../../modules/utils';
|
|
2
|
+
import { Consumes } from '../../types/activity';
|
|
2
3
|
import {
|
|
3
4
|
StringStringType,
|
|
4
5
|
StringAnyType,
|
|
@@ -26,16 +27,53 @@ export const MDATA_SYMBOLS = {
|
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
export class SerializerService {
|
|
30
|
+
dIds: StringStringType;
|
|
29
31
|
symKeys: SymbolMaps;
|
|
30
32
|
symReverseKeys: SymbolMaps;
|
|
31
33
|
symValMaps: SymbolMap;
|
|
32
34
|
symValReverseMaps: SymbolMap;
|
|
33
35
|
|
|
34
36
|
constructor() {
|
|
35
|
-
this.resetSymbols({}, {});
|
|
37
|
+
this.resetSymbols({}, {}, {});
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
abbreviate(consumes: Consumes, symbolNames: string[], fields: string[] = []): string[] {
|
|
41
|
+
for (const symbolName of symbolNames) {
|
|
42
|
+
const symbolSet = this.symKeys.get(symbolName);
|
|
43
|
+
const symbolPaths = consumes[symbolName];
|
|
44
|
+
for (const symbolPath of symbolPaths) {
|
|
45
|
+
const abbreviation = symbolSet.get(symbolPath);
|
|
46
|
+
if (abbreviation) {
|
|
47
|
+
const dimensionalIndex = this.resolveDimensionalIndex(symbolPath);
|
|
48
|
+
fields.push(`${abbreviation}${dimensionalIndex}`);
|
|
49
|
+
} else {
|
|
50
|
+
fields.push(symbolPath);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return fields;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
resolveDimensionalIndex(path: string): string {
|
|
58
|
+
if (this.isJobPath(path)) {
|
|
59
|
+
return '';
|
|
60
|
+
} else {
|
|
61
|
+
const [activityId] = path.split('/');
|
|
62
|
+
if (activityId in this.dIds) {
|
|
63
|
+
return this.dIds[activityId];
|
|
64
|
+
} else if ('$ADJACENT' in this.dIds) {
|
|
65
|
+
//else=> pre-authorizing adjacent activity entry
|
|
66
|
+
return this.dIds['$ADJACENT'];
|
|
67
|
+
}
|
|
68
|
+
return ',0';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
isJobPath(path: string): boolean {
|
|
73
|
+
return path.startsWith('data/') || path.startsWith('metadata/');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
resetSymbols(symKeys: SymbolSets, symVals: Symbols, dIds: StringStringType): void {
|
|
39
77
|
this.symKeys = new Map();
|
|
40
78
|
this.symReverseKeys = new Map();
|
|
41
79
|
for (const id in symKeys) {
|
|
@@ -43,6 +81,7 @@ export class SerializerService {
|
|
|
43
81
|
}
|
|
44
82
|
this.symValMaps = new Map(Object.entries(symVals));
|
|
45
83
|
this.symValReverseMaps = this.getReverseValueMap(this.symValMaps);
|
|
84
|
+
this.dIds = dIds;
|
|
46
85
|
}
|
|
47
86
|
|
|
48
87
|
getReverseKeyMap(keyMap: SymbolMap, id?: string): SymbolMap {
|
|
@@ -87,25 +126,23 @@ export class SerializerService {
|
|
|
87
126
|
if (this.symKeys.size === 0) {
|
|
88
127
|
return document;
|
|
89
128
|
}
|
|
90
|
-
let
|
|
129
|
+
let source: StringStringType = { ...document };
|
|
130
|
+
let result: StringStringType = { };
|
|
91
131
|
|
|
92
|
-
const compressWithMap = (abbreviationMap: SymbolMap) => {
|
|
93
|
-
for (let key in
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
result[
|
|
99
|
-
if (safeKey !== key) {
|
|
100
|
-
delete result[key];
|
|
132
|
+
const compressWithMap = (abbreviationMap: SymbolMap, id: string) => {
|
|
133
|
+
for (let key in source) {
|
|
134
|
+
if (key.startsWith(`${id}/`) || (id.startsWith('$') && ['data', 'metadata'].includes(key.split('/')[0]))) {
|
|
135
|
+
const dimensionalIndex = this.resolveDimensionalIndex(key);
|
|
136
|
+
let shortKey = abbreviationMap.get(key) || key;
|
|
137
|
+
const shortDimensionalKey = `${shortKey}${dimensionalIndex}`;
|
|
138
|
+
result[shortDimensionalKey] = source[key];
|
|
101
139
|
}
|
|
102
|
-
}
|
|
103
140
|
}
|
|
104
141
|
};
|
|
105
142
|
for (let id of ids) {
|
|
106
143
|
const abbreviationMap = this.symKeys.get(id);
|
|
107
144
|
if (abbreviationMap) {
|
|
108
|
-
compressWithMap(abbreviationMap);
|
|
145
|
+
compressWithMap(abbreviationMap, id);
|
|
109
146
|
}
|
|
110
147
|
}
|
|
111
148
|
return result;
|
|
@@ -120,14 +157,12 @@ export class SerializerService {
|
|
|
120
157
|
const inflateWithMap = (abbreviationMap: SymbolMap, id: string) => {
|
|
121
158
|
const reversedAbbreviationMap = this.getReverseKeyMap(abbreviationMap, id);
|
|
122
159
|
for (let key in result) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
let
|
|
126
|
-
if (
|
|
127
|
-
result[
|
|
128
|
-
|
|
129
|
-
delete result[key];
|
|
130
|
-
}
|
|
160
|
+
//strip dimensional index from key
|
|
161
|
+
const shortKey = key.split(',')[0];
|
|
162
|
+
let longKey = reversedAbbreviationMap.get(shortKey);
|
|
163
|
+
if (longKey) {
|
|
164
|
+
result[longKey] = result[key];
|
|
165
|
+
delete result[key];
|
|
131
166
|
}
|
|
132
167
|
}
|
|
133
168
|
};
|
package/services/store/index.ts
CHANGED
|
@@ -310,7 +310,11 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
|
|
|
310
310
|
app = {};
|
|
311
311
|
for (const field in sApp) {
|
|
312
312
|
try {
|
|
313
|
-
|
|
313
|
+
if (field === 'active') {
|
|
314
|
+
app[field] = sApp[field] === 'true';
|
|
315
|
+
} else {
|
|
316
|
+
app[field] = sApp[field];
|
|
317
|
+
}
|
|
314
318
|
} catch (e) {
|
|
315
319
|
app[field] = sApp[field];
|
|
316
320
|
}
|
|
@@ -448,12 +452,12 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
|
|
|
448
452
|
return Number(status);
|
|
449
453
|
}
|
|
450
454
|
|
|
451
|
-
async setState({ ...state }: StringAnyType, status: number | null, jobId: string,
|
|
455
|
+
async setState({ ...state }: StringAnyType, status: number | null, jobId: string, symbolNames: string[], dIds: StringStringType, multi? : U): Promise<string> {
|
|
452
456
|
delete state['metadata/js'];
|
|
453
|
-
const hashKey = this.mintKey(KeyType.JOB_STATE, { appId, jobId });
|
|
457
|
+
const hashKey = this.mintKey(KeyType.JOB_STATE, { appId: this.appId, jobId });
|
|
454
458
|
const symKeys = await this.getSymbolKeys(symbolNames);
|
|
455
459
|
const symVals = await this.getSymbolValues();
|
|
456
|
-
this.serializer.resetSymbols(symKeys, symVals);
|
|
460
|
+
this.serializer.resetSymbols(symKeys, symVals, dIds);
|
|
457
461
|
|
|
458
462
|
const hashData = this.serializer.package(state, symbolNames);
|
|
459
463
|
if (status !== null) {
|
|
@@ -465,26 +469,14 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
|
|
|
465
469
|
return jobId;
|
|
466
470
|
}
|
|
467
471
|
|
|
468
|
-
async getState(jobId: string, consumes: Consumes): Promise<[StringAnyType, number] | undefined> {
|
|
469
|
-
//
|
|
472
|
+
async getState(jobId: string, consumes: Consumes, dIds: StringStringType): Promise<[StringAnyType, number] | undefined> {
|
|
473
|
+
//get abbreviated field list (the symbols for the paths)
|
|
470
474
|
const key = this.mintKey(KeyType.JOB_STATE, { appId: this.appId, jobId });
|
|
471
475
|
const symbolNames = Object.keys(consumes);
|
|
472
476
|
const symKeys = await this.getSymbolKeys(symbolNames);
|
|
477
|
+
this.serializer.resetSymbols(symKeys, {}, dIds);
|
|
478
|
+
const fields = this.serializer.abbreviate(consumes, symbolNames, [':']);
|
|
473
479
|
|
|
474
|
-
//always fetch the job status (':') when fetching state
|
|
475
|
-
const fields = [':'];
|
|
476
|
-
for (const symbolName of symbolNames) {
|
|
477
|
-
const symbolSet = symKeys[symbolName];
|
|
478
|
-
const symbolPaths = consumes[symbolName];
|
|
479
|
-
for (const symbolPath of symbolPaths) {
|
|
480
|
-
const abbreviation = symbolSet[symbolPath];
|
|
481
|
-
if (abbreviation) {
|
|
482
|
-
fields.push(abbreviation);
|
|
483
|
-
} else {
|
|
484
|
-
fields.push(symbolPath);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
480
|
const jobDataArray = await this.redisClient[this.commands.hmget](key, fields);
|
|
489
481
|
const jobData: StringAnyType = {};
|
|
490
482
|
let atLeast1 = false; //if status field (':') isn't present assume 404
|
|
@@ -496,7 +488,7 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
|
|
|
496
488
|
});
|
|
497
489
|
if (atLeast1) {
|
|
498
490
|
const symVals = await this.getSymbolValues();
|
|
499
|
-
this.serializer.resetSymbols(symKeys, symVals);
|
|
491
|
+
this.serializer.resetSymbols(symKeys, symVals, dIds);
|
|
500
492
|
const state = this.serializer.unpackage(jobData, symbolNames);
|
|
501
493
|
let status = 0;
|
|
502
494
|
if (state[':']) {
|
|
@@ -508,13 +500,13 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
|
|
|
508
500
|
}
|
|
509
501
|
}
|
|
510
502
|
|
|
511
|
-
async collate(jobId: string, activityId: string, amount: number, multi? : U): Promise<number> {
|
|
503
|
+
async collate(jobId: string, activityId: string, amount: number, dIds: StringStringType, multi? : U): Promise<number> {
|
|
512
504
|
const jobKey = this.mintKey(KeyType.JOB_STATE, { appId: this.appId, jobId });
|
|
513
505
|
const collationKey = `${activityId}/output/metadata/as`; //activity state
|
|
514
506
|
const symbolNames = [activityId];
|
|
515
507
|
const symKeys = await this.getSymbolKeys(symbolNames);
|
|
516
508
|
const symVals = await this.getSymbolValues();
|
|
517
|
-
this.serializer.resetSymbols(symKeys, symVals);
|
|
509
|
+
this.serializer.resetSymbols(symKeys, symVals, dIds);
|
|
518
510
|
|
|
519
511
|
const payload = { [collationKey]: amount.toString() }
|
|
520
512
|
const hashData = this.serializer.package(payload, symbolNames);
|
package/types/activity.ts
CHANGED
package/types/hotmesh.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ILogger } from
|
|
2
|
-
import { HotMeshService } from
|
|
3
|
-
import { HookRules } from
|
|
4
|
-
import { RedisClass, RedisClient, RedisOptions } from
|
|
5
|
-
import { StreamData, StreamDataResponse } from
|
|
1
|
+
import { ILogger } from '../services/logger';
|
|
2
|
+
import { HotMeshService } from '../services/hotmesh';
|
|
3
|
+
import { HookRules } from './hook';
|
|
4
|
+
import { RedisClass, RedisClient, RedisOptions } from './redis';
|
|
5
|
+
import { StreamData, StreamDataResponse } from './stream';
|
|
6
6
|
|
|
7
7
|
type HotMesh = typeof HotMeshService;
|
|
8
8
|
|