@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.
Files changed (42) hide show
  1. package/README.md +13 -13
  2. package/build/package.json +3 -3
  3. package/build/services/activities/activity.d.ts +2 -0
  4. package/build/services/activities/activity.js +27 -9
  5. package/build/services/activities/worker.js +1 -0
  6. package/build/services/collator/index.d.ts +7 -5
  7. package/build/services/collator/index.js +29 -6
  8. package/build/services/compiler/deployer.d.ts +4 -3
  9. package/build/services/compiler/deployer.js +14 -0
  10. package/build/services/dimension/index.d.ts +1 -1
  11. package/build/services/durable/client.d.ts +3 -3
  12. package/build/services/durable/client.js +10 -1
  13. package/build/services/durable/handle.d.ts +1 -1
  14. package/build/services/durable/worker.js +20 -2
  15. package/build/services/engine/index.d.ts +1 -1
  16. package/build/services/engine/index.js +22 -5
  17. package/build/services/hotmesh/index.d.ts +1 -1
  18. package/build/services/hotmesh/index.js +2 -2
  19. package/build/services/serializer/index.d.ts +6 -1
  20. package/build/services/serializer/index.js +55 -22
  21. package/build/services/signaler/stream.js +1 -0
  22. package/build/services/store/index.d.ts +3 -3
  23. package/build/services/store/index.js +16 -24
  24. package/build/types/activity.d.ts +1 -0
  25. package/build/types/hotmesh.d.ts +5 -5
  26. package/package.json +3 -3
  27. package/services/activities/activity.ts +28 -9
  28. package/services/activities/worker.ts +1 -0
  29. package/services/collator/index.ts +38 -13
  30. package/services/compiler/deployer.ts +24 -9
  31. package/services/dimension/index.ts +1 -1
  32. package/services/durable/client.ts +13 -6
  33. package/services/durable/handle.ts +2 -2
  34. package/services/durable/worker.ts +16 -2
  35. package/services/engine/index.ts +23 -5
  36. package/services/hotmesh/index.ts +2 -2
  37. package/services/mapper/index.ts +0 -1
  38. package/services/serializer/index.ts +57 -22
  39. package/services/signaler/stream.ts +1 -0
  40. package/services/store/index.ts +15 -23
  41. package/types/activity.ts +1 -0
  42. package/types/hotmesh.ts +5 -5
@@ -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: { aid: `${hookRule.to}`, topic },
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: activityId,
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
- const output = await this.store.getState(jobId, consumes);
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);
@@ -80,5 +80,4 @@ class MapperService {
80
80
  }
81
81
  }
82
82
 
83
-
84
83
  export { MapperService }
@@ -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
- resetSymbols(symKeys: SymbolSets, symVals: Symbols): void {
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 result: StringStringType = { ...document };
129
+ let source: StringStringType = { ...document };
130
+ let result: StringStringType = { };
91
131
 
92
- const compressWithMap = (abbreviationMap: SymbolMap) => {
93
- for (let key in result) {
94
- let safeKey = abbreviationMap.get(key) || key;
95
- let value = result[key];
96
- let safeValue = abbreviationMap.get(value) || value;
97
- if (safeKey !== key || safeValue !== value) {
98
- result[safeKey] = safeValue;
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
- let safeKey = reversedAbbreviationMap.get(key) || key;
124
- let value = result[key];
125
- let safeValue = reversedAbbreviationMap.get(value) || value;
126
- if (safeKey !== key || safeValue !== value) {
127
- result[safeKey] = safeValue;
128
- if (safeKey !== key) {
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
  };
@@ -150,6 +150,7 @@ class StreamSignaler {
150
150
  try {
151
151
  output = await callback(input);
152
152
  } catch (err) {
153
+ console.error(err);
153
154
  this.logger.error(`stream-call-function-error`, { stream, id, err });
154
155
  output = this.structureUnhandledError(input, err);
155
156
  }
@@ -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
- app[field] = sApp[field];
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, appId: string, symbolNames: string[], multi? : U): Promise<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
- //todo: refactor into semantic submethods
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
@@ -26,6 +26,7 @@ interface BaseActivity {
26
26
  subscribes?: string; //compiler
27
27
  trigger?: string; //compiler
28
28
  parent?: string; //compiler
29
+ ancestors?: string[]; //compiler
29
30
  }
30
31
 
31
32
  interface Measure {
package/types/hotmesh.ts CHANGED
@@ -1,8 +1,8 @@
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";
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