@hotmeshio/hotmesh 0.0.53 → 0.0.54

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 (40) hide show
  1. package/build/modules/errors.d.ts +6 -48
  2. package/build/modules/errors.js +5 -5
  3. package/build/package.json +1 -1
  4. package/build/services/activities/hook.js +5 -1
  5. package/build/services/activities/trigger.d.ts +5 -2
  6. package/build/services/activities/trigger.js +22 -1
  7. package/build/services/durable/client.js +1 -8
  8. package/build/services/durable/exporter.d.ts +24 -13
  9. package/build/services/durable/exporter.js +145 -127
  10. package/build/services/durable/handle.d.ts +2 -2
  11. package/build/services/durable/handle.js +2 -2
  12. package/build/services/durable/worker.js +1 -1
  13. package/build/services/durable/workflow.d.ts +29 -17
  14. package/build/services/durable/workflow.js +116 -96
  15. package/build/services/engine/index.d.ts +2 -2
  16. package/build/services/engine/index.js +2 -2
  17. package/build/services/hotmesh/index.d.ts +2 -2
  18. package/build/services/hotmesh/index.js +2 -2
  19. package/build/types/durable.d.ts +15 -3
  20. package/build/types/error.d.ts +48 -0
  21. package/build/types/error.js +2 -0
  22. package/build/types/exporter.d.ts +26 -20
  23. package/build/types/index.d.ts +2 -1
  24. package/build/types/job.d.ts +24 -1
  25. package/modules/errors.ts +18 -55
  26. package/package.json +1 -1
  27. package/services/activities/hook.ts +8 -1
  28. package/services/activities/trigger.ts +27 -2
  29. package/services/durable/client.ts +2 -8
  30. package/services/durable/exporter.ts +149 -128
  31. package/services/durable/handle.ts +3 -3
  32. package/services/durable/worker.ts +1 -1
  33. package/services/durable/workflow.ts +136 -103
  34. package/services/engine/index.ts +4 -3
  35. package/services/hotmesh/index.ts +4 -3
  36. package/types/durable.ts +18 -3
  37. package/types/error.ts +52 -0
  38. package/types/exporter.ts +31 -23
  39. package/types/index.ts +8 -1
  40. package/types/job.ts +27 -0
@@ -1,5 +1,6 @@
1
1
  import { ActivityDuplex } from "../types/activity";
2
2
  import { CollationFaultType, CollationStage } from "../types/collator";
3
+ import { DurableChildErrorType, DurableProxyErrorType, DurableSleepErrorType, DurableWaitForAllErrorType, DurableWaitForErrorType } from "../types/error";
3
4
  declare class GetStateError extends Error {
4
5
  jobId: string;
5
6
  code: number;
@@ -14,12 +15,7 @@ declare class DurableWaitForError extends Error {
14
15
  workflowId: string;
15
16
  index: number;
16
17
  workflowDimension: string;
17
- constructor(params: {
18
- signalId: string;
19
- index: number;
20
- workflowDimension: string;
21
- workflowId: string;
22
- });
18
+ constructor(params: DurableWaitForErrorType);
23
19
  }
24
20
  declare class DurableProxyError extends Error {
25
21
  activityName: string;
@@ -34,19 +30,7 @@ declare class DurableProxyError extends Error {
34
30
  workflowDimension: string;
35
31
  workflowId: string;
36
32
  workflowTopic: string;
37
- constructor(params: {
38
- arguments: string[];
39
- activityName: string;
40
- backoffCoefficient?: number;
41
- index: number;
42
- maximumAttempts?: number;
43
- maximumInterval?: number;
44
- originJobId: string | null;
45
- parentWorkflowId: string;
46
- workflowDimension: string;
47
- workflowId: string;
48
- workflowTopic: string;
49
- });
33
+ constructor(params: DurableProxyErrorType);
50
34
  }
51
35
  declare class DurableChildError extends Error {
52
36
  await: boolean;
@@ -61,19 +45,7 @@ declare class DurableChildError extends Error {
61
45
  parentWorkflowId: string;
62
46
  workflowId: string;
63
47
  workflowTopic: string;
64
- constructor(params: {
65
- arguments: string[];
66
- await?: boolean;
67
- backoffCoefficient?: number;
68
- index: number;
69
- maximumAttempts?: number;
70
- maximumInterval?: number;
71
- originJobId: string | null;
72
- parentWorkflowId: string;
73
- workflowDimension: string;
74
- workflowId: string;
75
- workflowTopic: string;
76
- });
48
+ constructor(params: DurableChildErrorType);
77
49
  }
78
50
  declare class DurableWaitForAllError extends Error {
79
51
  items: any[];
@@ -85,16 +57,7 @@ declare class DurableWaitForAllError extends Error {
85
57
  parentWorkflowId: string;
86
58
  workflowId: string;
87
59
  workflowTopic: string;
88
- constructor(params: {
89
- items: string[];
90
- workflowId: string;
91
- workflowTopic: string;
92
- parentWorkflowId: string;
93
- originJobId: string | null;
94
- size: number;
95
- index: number;
96
- workflowDimension: string;
97
- });
60
+ constructor(params: DurableWaitForAllErrorType);
98
61
  }
99
62
  declare class DurableSleepError extends Error {
100
63
  workflowId: string;
@@ -102,12 +65,7 @@ declare class DurableSleepError extends Error {
102
65
  duration: number;
103
66
  index: number;
104
67
  workflowDimension: string;
105
- constructor(params: {
106
- duration: number;
107
- index: number;
108
- workflowDimension: string;
109
- workflowId: string;
110
- });
68
+ constructor(params: DurableSleepErrorType);
111
69
  }
112
70
  declare class DurableTimeoutError extends Error {
113
71
  code: number;
@@ -18,7 +18,7 @@ class SetStateError extends Error {
18
18
  exports.SetStateError = SetStateError;
19
19
  class DurableWaitForError extends Error {
20
20
  constructor(params) {
21
- super(`Durable WaitFor Error [${params.workflowId}]`);
21
+ super(`WaitFor Interruption`);
22
22
  this.signalId = params.signalId;
23
23
  this.index = params.index;
24
24
  this.workflowDimension = params.workflowDimension;
@@ -28,7 +28,7 @@ class DurableWaitForError extends Error {
28
28
  exports.DurableWaitForError = DurableWaitForError;
29
29
  class DurableProxyError extends Error {
30
30
  constructor(params) {
31
- super(`Durable Proxy Activity Error [${params.parentWorkflowId}] => [${params.workflowId}]`);
31
+ super(`ProxyActivity Interruption`);
32
32
  this.arguments = params.arguments;
33
33
  this.workflowId = params.workflowId;
34
34
  this.workflowTopic = params.workflowTopic;
@@ -46,7 +46,7 @@ class DurableProxyError extends Error {
46
46
  exports.DurableProxyError = DurableProxyError;
47
47
  class DurableChildError extends Error {
48
48
  constructor(params) {
49
- super(`Durable Child Error [${params.parentWorkflowId}] => [${params.workflowId}]`);
49
+ super(`ExecChild Interruption`);
50
50
  this.arguments = params.arguments;
51
51
  this.workflowId = params.workflowId;
52
52
  this.workflowTopic = params.workflowTopic;
@@ -64,7 +64,7 @@ class DurableChildError extends Error {
64
64
  exports.DurableChildError = DurableChildError;
65
65
  class DurableWaitForAllError extends Error {
66
66
  constructor(params) {
67
- super(`Durable Wait for All Error [${params.parentWorkflowId}] => [${params.workflowId}]`);
67
+ super(`Collation Interruption`);
68
68
  this.items = params.items;
69
69
  this.size = params.size;
70
70
  this.workflowId = params.workflowId;
@@ -79,7 +79,7 @@ class DurableWaitForAllError extends Error {
79
79
  exports.DurableWaitForAllError = DurableWaitForAllError;
80
80
  class DurableSleepError extends Error {
81
81
  constructor(params) {
82
- super(`Durable Sleep Error [${params.workflowId}]`);
82
+ super(`SleepFor Interruption`);
83
83
  this.duration = params.duration;
84
84
  this.workflowId = params.workflowId;
85
85
  this.index = params.index;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.0.53",
3
+ "version": "0.0.54",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -65,7 +65,11 @@ class Hook extends activity_1.Activity {
65
65
  * does this activity use a time-hook or web-hook
66
66
  */
67
67
  doesHook() {
68
- return !!(this.config.hook?.topic || this.config.sleep);
68
+ if (this.config.sleep) {
69
+ const duration = pipe_1.Pipe.resolve(this.config.sleep, this.context);
70
+ return !isNaN(duration) && Number(duration) > 0;
71
+ }
72
+ return !!this.config.hook?.topic;
69
73
  }
70
74
  async doHook(telemetry) {
71
75
  const multi = this.store.getMulti();
@@ -1,12 +1,15 @@
1
1
  import { Activity } from './activity';
2
2
  import { EngineService } from '../engine';
3
3
  import { ActivityData, ActivityMetadata, ActivityType, TriggerActivity } from '../../types/activity';
4
- import { JobState } from '../../types/job';
4
+ import { JobState, ExtensionType } from '../../types/job';
5
5
  import { RedisMulti } from '../../types/redis';
6
6
  declare class Trigger extends Activity {
7
7
  config: TriggerActivity;
8
8
  constructor(config: ActivityType, data: ActivityData, metadata: ActivityMetadata, hook: ActivityData | null, engine: EngineService, context?: JobState);
9
- process(): Promise<string>;
9
+ process(options?: ExtensionType): Promise<string>;
10
+ safeKey(key: string): string;
11
+ bindSearchData(options?: ExtensionType): void;
12
+ bindMarkerData(options?: ExtensionType): void;
10
13
  setStatus(amount: number): Promise<void>;
11
14
  execAdjacentParent(): Promise<void>;
12
15
  createInputContext(): Partial<JobState>;
@@ -13,7 +13,7 @@ class Trigger extends activity_1.Activity {
13
13
  constructor(config, data, metadata, hook, engine, context) {
14
14
  super(config, data, metadata, hook, engine, context);
15
15
  }
16
- async process() {
16
+ async process(options) {
17
17
  this.logger.debug('trigger-process', { subscribes: this.config.subscribes });
18
18
  let telemetry;
19
19
  try {
@@ -26,6 +26,8 @@ class Trigger extends activity_1.Activity {
26
26
  await this.setStateNX();
27
27
  this.adjacencyList = await this.filterAdjacent();
28
28
  await this.setStatus(this.adjacencyList.length);
29
+ this.bindSearchData(options);
30
+ this.bindMarkerData(options);
29
31
  const multi = this.store.getMulti();
30
32
  await this.setState(multi);
31
33
  await this.setStats(multi);
@@ -61,6 +63,25 @@ class Trigger extends activity_1.Activity {
61
63
  this.logger.debug('trigger-process-end', { subscribes: this.config.subscribes, jid: this.context.metadata.jid, gid: this.context.metadata.gid });
62
64
  }
63
65
  }
66
+ safeKey(key) {
67
+ return `_${key}`;
68
+ }
69
+ bindSearchData(options) {
70
+ if (options?.search) {
71
+ Object.keys(options.search).forEach((key) => {
72
+ this.context.data[this.safeKey(key)] = options.search[key].toString();
73
+ });
74
+ }
75
+ }
76
+ bindMarkerData(options) {
77
+ if (options?.marker) {
78
+ Object.keys(options.marker).forEach((key) => {
79
+ if (key.startsWith('-')) {
80
+ this.context.data[key] = options.marker[key].toString();
81
+ }
82
+ });
83
+ }
84
+ }
64
85
  async setStatus(amount) {
65
86
  this.context.metadata.js = amount;
66
87
  }
@@ -103,14 +103,7 @@ class ClientService {
103
103
  maximumInterval: (0, ms_1.default)(options.config?.maximumInterval || enums_1.HMSH_DURABLE_MAX_INTERVAL) / 1000,
104
104
  };
105
105
  const context = { metadata: { trc, spn }, data: {} };
106
- const jobId = await hotMeshClient.pub(`${options.namespace ?? factory_1.APP_ID}.execute`, payload, context);
107
- // Seed search data
108
- if (jobId && options.search?.data) {
109
- const searchSessionId = `-search-0`;
110
- const search = new search_1.Search(jobId, hotMeshClient, searchSessionId);
111
- const entries = Object.entries(options.search.data).flat();
112
- await search.set(...entries);
113
- }
106
+ const jobId = await hotMeshClient.pub(`${options.namespace ?? factory_1.APP_ID}.execute`, payload, context, { search: options?.search?.data, marker: options?.marker });
114
107
  return new handle_1.WorkflowHandleService(hotMeshClient, workflowTopic, jobId);
115
108
  },
116
109
  /**
@@ -1,6 +1,6 @@
1
1
  import { ILogger } from '../logger';
2
2
  import { StoreService } from '../store';
3
- import { ExportOptions, DurableJobExport } from '../../types/exporter';
3
+ import { ExportOptions, DurableJobExport, TimelineType, TransitionType, ExportFields } from '../../types/exporter';
4
4
  import { RedisClient, RedisMulti } from '../../types/redis';
5
5
  import { StringStringType, Symbols } from "../../types/serializer";
6
6
  declare class ExporterService {
@@ -8,33 +8,44 @@ declare class ExporterService {
8
8
  logger: ILogger;
9
9
  store: StoreService<RedisClient, RedisMulti>;
10
10
  symbols: Promise<Symbols> | Symbols;
11
+ private static symbols;
11
12
  constructor(appId: string, store: StoreService<RedisClient, RedisMulti>, logger: ILogger);
12
13
  /**
13
14
  * Convert the job hash from its compiles format into a DurableJobExport object with
14
15
  * facets that describe the workflow in terms relevant to narrative storytelling.
15
16
  */
16
17
  export(jobId: string, options?: ExportOptions): Promise<DurableJobExport>;
18
+ /**
19
+ * Inflates the job data from Redis into a DurableJobExport object
20
+ * @param jobHash - the job data from Redis
21
+ * @param dependencyList - the list of dependencies for the job
22
+ * @returns - the inflated job data
23
+ */
24
+ inflate(jobHash: StringStringType, options: ExportOptions): DurableJobExport;
25
+ resolveValue(raw: string, withValues: boolean): Record<string, any> | string | number | null;
17
26
  /**
18
27
  * Inflates the key from Redis, 3-character symbol
19
28
  * into a human-readable JSON path, reflecting the
20
29
  * tree-like structure of the unidimensional Hash
30
+ * @private
21
31
  */
22
32
  inflateKey(key: string): string;
33
+ filterFields(fullObject: DurableJobExport, block?: ExportFields[], allow?: ExportFields[]): Partial<DurableJobExport>;
34
+ inflateTransition(match: RegExpMatchArray, value: string, transitionsObject: Record<string, TransitionType>): void;
35
+ sortEntriesByCreated(obj: {
36
+ [key: string]: TransitionType;
37
+ }): TransitionType[];
23
38
  /**
24
- * Inflates the dependency data from Redis into a DurableJobExport object by
25
- * organizing the dimensional isolate in sch a way asto interleave
26
- * into a story
27
- * @param data - the dependency data from Redis
28
- * @returns - the organized dependency data
39
+ * marker names are overloaded with details like sequence, type, etc
29
40
  */
30
- inflateDependencyData(data: string[]): Record<string, any>[];
41
+ keyToObject(key: string): {
42
+ index: number;
43
+ dimension?: string;
44
+ secondary?: number;
45
+ };
31
46
  /**
32
- * Inflates the job data from Redis into a DurableJobExport object
33
- * @param jobHash - the job data from Redis
34
- * @param dependencyList - the list of dependencies for the job
35
- * @returns - the inflated job data
47
+ * idem list has a complicated sort order based on indexes and dimensions
36
48
  */
37
- inflate(jobHash: StringStringType): DurableJobExport;
38
- inflateProcess(match: RegExpMatchArray, value: string, replay: Record<string, Record<string, any>>): void;
49
+ sortParts(parts: TimelineType[]): TimelineType[];
39
50
  }
40
51
  export { ExporterService };
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ExporterService = void 0;
4
- const key_1 = require("../../modules/key");
5
4
  const utils_1 = require("../../modules/utils");
6
5
  const serializer_1 = require("../serializer");
7
6
  class ExporterService {
@@ -15,174 +14,122 @@ class ExporterService {
15
14
  * facets that describe the workflow in terms relevant to narrative storytelling.
16
15
  */
17
16
  async export(jobId, options = {}) {
18
- if (!this.symbols) {
19
- this.symbols = this.store.getAllSymbols();
20
- this.symbols = await this.symbols;
17
+ if (!ExporterService.symbols.has(this.appId)) {
18
+ const symbols = this.store.getAllSymbols();
19
+ ExporterService.symbols.set(this.appId, await symbols);
21
20
  }
22
21
  const jobData = await this.store.getRaw(jobId);
23
- const jobExport = this.inflate(jobData /*, depData*/);
22
+ const jobExport = this.inflate(jobData, options);
24
23
  return jobExport;
25
24
  }
26
- /**
27
- * Inflates the key from Redis, 3-character symbol
28
- * into a human-readable JSON path, reflecting the
29
- * tree-like structure of the unidimensional Hash
30
- */
31
- inflateKey(key) {
32
- if (key in this.symbols) {
33
- const path = this.symbols[key];
34
- const parts = path.split('/');
35
- return parts.join('/');
36
- }
37
- return key;
38
- }
39
- /**
40
- * Inflates the dependency data from Redis into a DurableJobExport object by
41
- * organizing the dimensional isolate in sch a way asto interleave
42
- * into a story
43
- * @param data - the dependency data from Redis
44
- * @returns - the organized dependency data
45
- */
46
- inflateDependencyData(data) {
47
- return data.map((dependency, index) => {
48
- const [action, topic, gid, dimension, ...jid] = dependency.split(key_1.VALSEP);
49
- const job_id = jid.join(key_1.VALSEP);
50
- return {
51
- index,
52
- action,
53
- topic,
54
- gid,
55
- dimension,
56
- job_id,
57
- };
58
- });
59
- }
60
25
  /**
61
26
  * Inflates the job data from Redis into a DurableJobExport object
62
27
  * @param jobHash - the job data from Redis
63
28
  * @param dependencyList - the list of dependencies for the job
64
29
  * @returns - the inflated job data
65
30
  */
66
- inflate(jobHash) {
67
- const idempotents = [];
31
+ inflate(jobHash, options) {
32
+ const timeline = [];
68
33
  const state = {};
69
34
  const data = {};
70
- const other = [];
71
- const replay = {};
35
+ const transitionsObject = {};
72
36
  const regex = /^([a-zA-Z]{3}),(\d+(?:,\d+)*)/;
73
37
  Object.entries(jobHash).forEach(([key, value]) => {
74
38
  const match = key.match(regex);
75
39
  if (match) {
76
- //activity process state
77
- this.inflateProcess(match, value, replay);
40
+ //transitions
41
+ this.inflateTransition(match, value, transitionsObject);
78
42
  }
79
43
  else if (key.length === 3) {
80
- //job state
44
+ //state
81
45
  state[this.inflateKey(key)] = serializer_1.SerializerService.fromString(value);
82
46
  }
83
47
  else if (key.startsWith('_')) {
84
- //job data
48
+ //data
85
49
  data[key.substring(1)] = value;
86
50
  }
87
51
  else if (key.startsWith('-')) {
88
- //actions with side effect (replayable)
89
- idempotents.push({
52
+ //timeline
53
+ const keyParts = this.keyToObject(key); //key parts have meaning
54
+ timeline.push({
55
+ ...keyParts,
90
56
  key,
91
- value: serializer_1.SerializerService.fromString(value),
92
- parts: extractParts(key),
57
+ value: this.resolveValue(value, options.values),
93
58
  });
94
59
  }
95
- else {
96
- //collator guids, etc
97
- other.push([null, key, value]);
98
- }
99
60
  });
100
- const sortEntriesByCreated = (obj) => {
101
- const entriesArray = Object.values(obj);
102
- entriesArray.sort((a, b) => {
103
- return (a.created || a.updated).localeCompare(b.created || b.updated);
104
- });
105
- return entriesArray;
106
- };
107
- /**
108
- * idem list has a complicated sort order based on indexes and dimensions
109
- */
110
- const sortParts = (parts) => {
111
- return parts.sort((a, b) => {
112
- const { dimension: aDim, index: aIdx, secondary: aSec } = a.parts;
113
- const { dimension: bDim, index: bIdx, secondary: bSec } = b.parts;
114
- if (aDim === undefined && bDim !== undefined)
115
- return -1;
116
- if (aDim !== undefined && bDim === undefined)
117
- return 1;
118
- if (aDim !== undefined && bDim !== undefined) {
119
- if (aDim < bDim)
120
- return -1;
121
- if (aDim > bDim)
122
- return 1;
123
- }
124
- if (aIdx < bIdx)
125
- return -1;
126
- if (aIdx > bIdx)
127
- return 1;
128
- if (aSec === undefined && bSec !== undefined)
129
- return -1;
130
- if (aSec !== undefined && bSec === undefined)
131
- return 1;
132
- if (aSec !== undefined && bSec !== undefined) {
133
- if (aSec < bSec)
134
- return -1;
135
- if (aSec > bSec)
136
- return 1;
61
+ return this.filterFields({
62
+ data: (0, utils_1.restoreHierarchy)(data),
63
+ state: Object.entries((0, utils_1.restoreHierarchy)(state))[0][1],
64
+ status: parseInt(jobHash[':'], 10),
65
+ timeline: this.sortParts(timeline),
66
+ transitions: this.sortEntriesByCreated(transitionsObject),
67
+ }, options.block, options.allow);
68
+ }
69
+ resolveValue(raw, withValues) {
70
+ const resolved = serializer_1.SerializerService.fromString(raw);
71
+ if (withValues !== false) {
72
+ return resolved;
73
+ }
74
+ if (resolved && typeof resolved === 'object') {
75
+ if ('data' in resolved) {
76
+ resolved.data = {};
77
+ }
78
+ if ('$error' in resolved) {
79
+ resolved.$error = {};
80
+ }
81
+ }
82
+ return resolved;
83
+ }
84
+ /**
85
+ * Inflates the key from Redis, 3-character symbol
86
+ * into a human-readable JSON path, reflecting the
87
+ * tree-like structure of the unidimensional Hash
88
+ * @private
89
+ */
90
+ inflateKey(key) {
91
+ const symbols = ExporterService.symbols.get(this.appId);
92
+ if (key in symbols) {
93
+ const path = symbols[key];
94
+ const parts = path.split('/');
95
+ return parts.join('/');
96
+ }
97
+ return key;
98
+ }
99
+ filterFields(fullObject, block = [], allow = []) {
100
+ let result = {};
101
+ if (allow && allow.length > 0) {
102
+ allow.forEach(field => {
103
+ if (field in fullObject) {
104
+ result[field] = fullObject[field];
137
105
  }
138
- return 0;
139
106
  });
140
- };
141
- function extractParts(key) {
142
- function extractDimension(label) {
143
- const parts = label.split(',');
144
- if (parts.length > 1) {
145
- parts.shift();
146
- return parts.join(',');
107
+ }
108
+ else {
109
+ result = { ...fullObject };
110
+ }
111
+ if (block && block.length > 0) {
112
+ block.forEach(field => {
113
+ if (field in result) {
114
+ delete result[field];
147
115
  }
148
- }
149
- const parts = key.split('-');
150
- if (parts.length === 4) {
151
- //-proxy-5- -search-1-1-
152
- return {
153
- index: parseInt(parts[2], 10),
154
- dimension: extractDimension(parts[1]),
155
- };
156
- }
157
- else {
158
- //-search,0,0-1-1- -proxy,0,0-1-
159
- return {
160
- index: parseInt(parts[2], 10),
161
- secondary: parseInt(parts[3], 10),
162
- dimension: extractDimension(parts[1]),
163
- };
164
- }
116
+ });
165
117
  }
166
- return {
167
- data: (0, utils_1.restoreHierarchy)(data),
168
- idempotents: sortParts(idempotents),
169
- state: Object.entries((0, utils_1.restoreHierarchy)(state))[0][1],
170
- status: jobHash[':'],
171
- replay: sortEntriesByCreated(replay),
172
- };
118
+ return result;
173
119
  }
174
- inflateProcess(match, value, replay) {
120
+ inflateTransition(match, value, transitionsObject) {
175
121
  const [_, letters, dimensions] = match;
176
122
  const path = this.inflateKey(letters);
177
123
  const parts = path.split('/');
178
124
  const activity = parts[0];
179
125
  const isCreate = path.endsWith('/output/metadata/ac');
180
126
  const isUpdate = path.endsWith('/output/metadata/au');
127
+ //for now only export activity start/stop; activity data would also be interesting
181
128
  if (isCreate || isUpdate) {
182
129
  const targetName = `${activity},${dimensions}`;
183
- let target = replay[targetName];
130
+ let target = transitionsObject[targetName];
184
131
  if (!target) {
185
- replay[targetName] = {
132
+ transitionsObject[targetName] = {
186
133
  activity,
187
134
  dimensions,
188
135
  created: isCreate ? value : null,
@@ -194,5 +141,76 @@ class ExporterService {
194
141
  }
195
142
  }
196
143
  }
144
+ sortEntriesByCreated(obj) {
145
+ const entriesArray = Object.values(obj);
146
+ entriesArray.sort((a, b) => {
147
+ return (a.created || a.updated).localeCompare(b.created || b.updated);
148
+ });
149
+ return entriesArray;
150
+ }
151
+ /**
152
+ * marker names are overloaded with details like sequence, type, etc
153
+ */
154
+ keyToObject(key) {
155
+ function extractDimension(label) {
156
+ const parts = label.split(',');
157
+ if (parts.length > 1) {
158
+ parts.shift();
159
+ return parts.join(',');
160
+ }
161
+ }
162
+ const parts = key.split('-');
163
+ if (parts.length === 4) {
164
+ //-proxy-5- -search-1-1-
165
+ return {
166
+ index: parseInt(parts[2], 10),
167
+ dimension: extractDimension(parts[1]),
168
+ };
169
+ }
170
+ else {
171
+ //-search,0,0-1-1- -proxy,0,0-1-
172
+ return {
173
+ index: parseInt(parts[2], 10),
174
+ secondary: parseInt(parts[3], 10),
175
+ dimension: extractDimension(parts[1]),
176
+ };
177
+ }
178
+ }
179
+ /**
180
+ * idem list has a complicated sort order based on indexes and dimensions
181
+ */
182
+ sortParts(parts) {
183
+ return parts.sort((a, b) => {
184
+ const { dimension: aDim, index: aIdx, secondary: aSec } = a;
185
+ const { dimension: bDim, index: bIdx, secondary: bSec } = b;
186
+ if (aDim === undefined && bDim !== undefined)
187
+ return -1;
188
+ if (aDim !== undefined && bDim === undefined)
189
+ return 1;
190
+ if (aDim !== undefined && bDim !== undefined) {
191
+ if (aDim < bDim)
192
+ return -1;
193
+ if (aDim > bDim)
194
+ return 1;
195
+ }
196
+ if (aIdx < bIdx)
197
+ return -1;
198
+ if (aIdx > bIdx)
199
+ return 1;
200
+ if (aSec === undefined && bSec !== undefined)
201
+ return -1;
202
+ if (aSec !== undefined && bSec === undefined)
203
+ return 1;
204
+ if (aSec !== undefined && bSec !== undefined) {
205
+ if (aSec < bSec)
206
+ return -1;
207
+ if (aSec > bSec)
208
+ return 1;
209
+ }
210
+ return 0;
211
+ });
212
+ }
213
+ ;
197
214
  }
198
215
  exports.ExporterService = ExporterService;
216
+ ExporterService.symbols = new Map();
@@ -1,6 +1,6 @@
1
1
  import { ExporterService } from './exporter';
2
2
  import { HotMeshService as HotMesh } from '../hotmesh';
3
- import { DurableJobExport } from '../../types/exporter';
3
+ import { DurableJobExport, ExportOptions } from '../../types/exporter';
4
4
  import { JobInterruptOptions } from '../../types/job';
5
5
  import { StreamError } from '../../types/stream';
6
6
  export declare class WorkflowHandleService {
@@ -9,7 +9,7 @@ export declare class WorkflowHandleService {
9
9
  workflowTopic: string;
10
10
  workflowId: string;
11
11
  constructor(hotMesh: HotMesh, workflowTopic: string, workflowId: string);
12
- export(): Promise<DurableJobExport>;
12
+ export(options?: ExportOptions): Promise<DurableJobExport>;
13
13
  /**
14
14
  * Sends a signal to the workflow. This is a way to send
15
15
  * a message to a workflow that is paused due to having
@@ -9,8 +9,8 @@ class WorkflowHandleService {
9
9
  this.hotMesh = hotMesh;
10
10
  this.exporter = new exporter_1.ExporterService(this.hotMesh.appId, this.hotMesh.engine.store, this.hotMesh.engine.logger);
11
11
  }
12
- async export() {
13
- return this.exporter.export(this.workflowId);
12
+ async export(options) {
13
+ return this.exporter.export(this.workflowId, options);
14
14
  }
15
15
  /**
16
16
  * Sends a signal to the workflow. This is a way to send