@hotmeshio/hotmesh 0.12.1 → 0.14.0
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 +18 -22
- package/build/modules/enums.d.ts +60 -5
- package/build/modules/enums.js +62 -7
- package/build/modules/errors.d.ts +15 -2
- package/build/modules/errors.js +17 -1
- package/build/modules/storage.d.ts +1 -0
- package/build/modules/storage.js +2 -1
- package/build/package.json +8 -2
- package/build/services/activities/activity/context.d.ts +22 -0
- package/build/services/activities/activity/context.js +76 -0
- package/build/services/activities/activity/index.d.ts +116 -0
- package/build/services/activities/activity/index.js +299 -0
- package/build/services/activities/activity/mapping.d.ts +12 -0
- package/build/services/activities/activity/mapping.js +63 -0
- package/build/services/activities/activity/process.d.ts +28 -0
- package/build/services/activities/activity/process.js +100 -0
- package/build/services/activities/activity/protocol.d.ts +39 -0
- package/build/services/activities/activity/protocol.js +151 -0
- package/build/services/activities/activity/state.d.ts +40 -0
- package/build/services/activities/activity/state.js +143 -0
- package/build/services/activities/activity/transition.d.ts +23 -0
- package/build/services/activities/activity/transition.js +71 -0
- package/build/services/activities/activity/verify.d.ts +22 -0
- package/build/services/activities/activity/verify.js +85 -0
- package/build/services/activities/await.d.ts +1 -4
- package/build/services/activities/await.js +2 -36
- package/build/services/activities/cycle.d.ts +1 -11
- package/build/services/activities/cycle.js +3 -46
- package/build/services/activities/hook.d.ts +2 -11
- package/build/services/activities/hook.js +30 -50
- package/build/services/activities/interrupt.d.ts +2 -4
- package/build/services/activities/interrupt.js +4 -38
- package/build/services/activities/signal.d.ts +1 -11
- package/build/services/activities/signal.js +3 -48
- package/build/services/activities/trigger.d.ts +1 -3
- package/build/services/activities/trigger.js +0 -3
- package/build/services/activities/worker.d.ts +3 -6
- package/build/services/activities/worker.js +4 -40
- package/build/services/connector/factory.d.ts +6 -0
- package/build/services/connector/factory.js +24 -0
- package/build/services/dba/index.d.ts +14 -4
- package/build/services/dba/index.js +57 -18
- package/build/services/durable/activity.d.ts +30 -0
- package/build/services/durable/activity.js +46 -0
- package/build/services/durable/client.d.ts +26 -31
- package/build/services/durable/client.js +26 -31
- package/build/services/durable/connection.d.ts +13 -7
- package/build/services/durable/connection.js +13 -7
- package/build/services/durable/exporter.d.ts +2 -2
- package/build/services/durable/exporter.js +27 -12
- package/build/services/durable/handle.d.ts +59 -41
- package/build/services/durable/handle.js +61 -41
- package/build/services/durable/index.d.ts +152 -283
- package/build/services/durable/index.js +161 -289
- package/build/services/durable/interceptor.d.ts +43 -33
- package/build/services/durable/interceptor.js +59 -39
- package/build/services/durable/schemas/factory.d.ts +2 -3
- package/build/services/durable/schemas/factory.js +180 -30
- package/build/services/durable/telemetry.d.ts +80 -0
- package/build/services/durable/telemetry.js +137 -0
- package/build/services/durable/worker.d.ts +100 -21
- package/build/services/durable/worker.js +314 -60
- package/build/services/durable/workflow/all.d.ts +1 -1
- package/build/services/durable/workflow/all.js +1 -1
- package/build/services/durable/workflow/cancellationScope.d.ts +104 -0
- package/build/services/durable/workflow/cancellationScope.js +139 -0
- package/build/services/durable/workflow/common.d.ts +5 -4
- package/build/services/durable/workflow/common.js +6 -1
- package/build/services/durable/workflow/{waitFor.d.ts → condition.d.ts} +9 -8
- package/build/services/durable/workflow/{waitFor.js → condition.js} +44 -11
- package/build/services/durable/workflow/continueAsNew.d.ts +65 -0
- package/build/services/durable/workflow/continueAsNew.js +92 -0
- package/build/services/durable/workflow/didRun.d.ts +2 -2
- package/build/services/durable/workflow/didRun.js +4 -4
- package/build/services/durable/workflow/enrich.d.ts +5 -0
- package/build/services/durable/workflow/enrich.js +5 -0
- package/build/services/durable/workflow/entityMethods.d.ts +7 -0
- package/build/services/durable/workflow/entityMethods.js +7 -0
- package/build/services/durable/workflow/execHook.js +3 -3
- package/build/services/durable/workflow/execHookBatch.js +2 -2
- package/build/services/durable/workflow/{execChild.d.ts → executeChild.d.ts} +4 -40
- package/build/services/durable/workflow/{execChild.js → executeChild.js} +36 -45
- package/build/services/durable/workflow/hook.d.ts +1 -1
- package/build/services/durable/workflow/hook.js +4 -3
- package/build/services/durable/workflow/index.d.ts +45 -50
- package/build/services/durable/workflow/index.js +46 -51
- package/build/services/durable/workflow/interruption.d.ts +7 -6
- package/build/services/durable/workflow/interruption.js +11 -7
- package/build/services/durable/workflow/patched.d.ts +72 -0
- package/build/services/durable/workflow/patched.js +110 -0
- package/build/services/durable/workflow/proxyActivities.d.ts +7 -7
- package/build/services/durable/workflow/proxyActivities.js +51 -15
- package/build/services/durable/workflow/searchMethods.d.ts +7 -0
- package/build/services/durable/workflow/searchMethods.js +7 -0
- package/build/services/durable/workflow/signal.d.ts +4 -4
- package/build/services/durable/workflow/signal.js +4 -4
- package/build/services/durable/workflow/{sleepFor.d.ts → sleep.d.ts} +7 -7
- package/build/services/durable/workflow/{sleepFor.js → sleep.js} +39 -10
- package/build/services/durable/workflow/terminate.d.ts +55 -0
- package/build/services/durable/workflow/{interrupt.js → terminate.js} +21 -21
- package/build/services/durable/workflow/trace.js +2 -2
- package/build/services/durable/workflow/uuid4.d.ts +14 -0
- package/build/services/durable/workflow/uuid4.js +39 -0
- package/build/services/durable/workflow/{context.d.ts → workflowInfo.d.ts} +5 -5
- package/build/services/durable/workflow/{context.js → workflowInfo.js} +7 -7
- package/build/services/engine/compiler.d.ts +19 -0
- package/build/services/engine/compiler.js +20 -0
- package/build/services/engine/completion.d.ts +46 -0
- package/build/services/engine/completion.js +145 -0
- package/build/services/engine/dispatch.d.ts +24 -0
- package/build/services/engine/dispatch.js +98 -0
- package/build/services/engine/index.d.ts +49 -81
- package/build/services/engine/index.js +175 -573
- package/build/services/engine/init.d.ts +42 -0
- package/build/services/engine/init.js +74 -0
- package/build/services/engine/pubsub.d.ts +50 -0
- package/build/services/engine/pubsub.js +118 -0
- package/build/services/engine/reporting.d.ts +20 -0
- package/build/services/engine/reporting.js +38 -0
- package/build/services/engine/schema.d.ts +23 -0
- package/build/services/engine/schema.js +62 -0
- package/build/services/engine/signal.d.ts +57 -0
- package/build/services/engine/signal.js +117 -0
- package/build/services/engine/state.d.ts +35 -0
- package/build/services/engine/state.js +61 -0
- package/build/services/engine/version.d.ts +31 -0
- package/build/services/engine/version.js +73 -0
- package/build/services/hotmesh/deployment.d.ts +21 -0
- package/build/services/hotmesh/deployment.js +25 -0
- package/build/services/hotmesh/index.d.ts +142 -533
- package/build/services/hotmesh/index.js +223 -674
- package/build/services/hotmesh/init.d.ts +42 -0
- package/build/services/hotmesh/init.js +93 -0
- package/build/services/hotmesh/jobs.d.ts +67 -0
- package/build/services/hotmesh/jobs.js +99 -0
- package/build/services/hotmesh/pubsub.d.ts +38 -0
- package/build/services/hotmesh/pubsub.js +54 -0
- package/build/services/hotmesh/quorum.d.ts +30 -0
- package/build/services/hotmesh/quorum.js +62 -0
- package/build/services/hotmesh/validation.d.ts +6 -0
- package/build/services/hotmesh/validation.js +28 -0
- package/build/services/quorum/index.js +1 -0
- package/build/services/router/consumption/index.d.ts +11 -5
- package/build/services/router/consumption/index.js +24 -17
- package/build/services/router/error-handling/index.d.ts +2 -2
- package/build/services/router/error-handling/index.js +14 -14
- package/build/services/router/index.d.ts +1 -1
- package/build/services/router/index.js +2 -2
- package/build/services/serializer/index.d.ts +22 -0
- package/build/services/serializer/index.js +39 -1
- package/build/services/store/index.d.ts +1 -0
- package/build/services/store/providers/postgres/exporter-sql.d.ts +2 -2
- package/build/services/store/providers/postgres/exporter-sql.js +4 -4
- package/build/services/store/providers/postgres/kvtables.js +7 -6
- package/build/services/store/providers/postgres/kvtypes/hash/basic.js +67 -52
- package/build/services/store/providers/postgres/kvtypes/hash/jsonb.js +87 -72
- package/build/services/store/providers/postgres/kvtypes/hash/udata.js +106 -79
- package/build/services/store/providers/postgres/kvtypes/hash/utils.d.ts +16 -0
- package/build/services/store/providers/postgres/kvtypes/hash/utils.js +29 -16
- package/build/services/store/providers/postgres/postgres.d.ts +1 -0
- package/build/services/store/providers/postgres/postgres.js +14 -4
- package/build/services/stream/factory.d.ts +3 -1
- package/build/services/stream/factory.js +2 -2
- package/build/services/stream/index.d.ts +1 -0
- package/build/services/stream/providers/nats/nats.d.ts +1 -0
- package/build/services/stream/providers/nats/nats.js +1 -0
- package/build/services/stream/providers/postgres/credentials.d.ts +56 -0
- package/build/services/stream/providers/postgres/credentials.js +129 -0
- package/build/services/stream/providers/postgres/kvtables.js +18 -0
- package/build/services/stream/providers/postgres/messages.js +7 -7
- package/build/services/stream/providers/postgres/notifications.js +16 -2
- package/build/services/stream/providers/postgres/postgres.d.ts +7 -0
- package/build/services/stream/providers/postgres/postgres.js +35 -4
- package/build/services/stream/providers/postgres/procedures.d.ts +21 -0
- package/build/services/stream/providers/postgres/procedures.js +213 -0
- package/build/services/stream/providers/postgres/secured.d.ts +34 -0
- package/build/services/stream/providers/postgres/secured.js +146 -0
- package/build/services/stream/providers/postgres/stats.d.ts +1 -0
- package/build/services/stream/providers/postgres/stats.js +1 -0
- package/build/services/stream/registry.d.ts +1 -1
- package/build/services/stream/registry.js +5 -2
- package/build/services/telemetry/index.d.ts +10 -1
- package/build/services/telemetry/index.js +40 -7
- package/build/services/worker/credentials.d.ts +51 -0
- package/build/services/worker/credentials.js +87 -0
- package/build/services/worker/index.d.ts +2 -2
- package/build/services/worker/index.js +7 -6
- package/build/types/codec.d.ts +84 -0
- package/build/types/codec.js +2 -0
- package/build/types/dba.d.ts +39 -3
- package/build/types/durable.d.ts +123 -25
- package/build/types/error.d.ts +10 -0
- package/build/types/exporter.d.ts +1 -1
- package/build/types/hotmesh.d.ts +67 -4
- package/build/types/index.d.ts +2 -1
- package/build/types/provider.d.ts +2 -2
- package/build/types/quorum.d.ts +35 -1
- package/build/types/stream.d.ts +12 -6
- package/package.json +8 -2
- package/build/services/activities/activity.d.ts +0 -192
- package/build/services/activities/activity.js +0 -786
- package/build/services/durable/workflow/interrupt.d.ts +0 -55
|
@@ -1,786 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Activity = void 0;
|
|
4
|
-
const enums_1 = require("../../modules/enums");
|
|
5
|
-
const errors_1 = require("../../modules/errors");
|
|
6
|
-
const utils_1 = require("../../modules/utils");
|
|
7
|
-
const collator_1 = require("../collator");
|
|
8
|
-
const mapper_1 = require("../mapper");
|
|
9
|
-
const pipe_1 = require("../pipe");
|
|
10
|
-
const serializer_1 = require("../serializer");
|
|
11
|
-
const telemetry_1 = require("../telemetry");
|
|
12
|
-
const stream_1 = require("../../types/stream");
|
|
13
|
-
/**
|
|
14
|
-
* Base class for all HotMesh activity types. Activities are the execution
|
|
15
|
-
* units within a YAML-defined workflow graph. Each activity represents a
|
|
16
|
-
* node in a Directed Acyclic Graph (DAG) that the engine orchestrates.
|
|
17
|
-
*
|
|
18
|
-
* ## Activity Categories
|
|
19
|
-
*
|
|
20
|
-
* Activities fall into three execution categories:
|
|
21
|
-
*
|
|
22
|
-
* - **Category A (Duplex)**: Two-phase activities with Leg 1 (dispatch) and
|
|
23
|
-
* Leg 2 (response). Used by `Worker` and `Await`. Leg 1
|
|
24
|
-
* publishes a message and waits; Leg 2 handles the response via
|
|
25
|
-
* `processEvent` and transitions to adjacent activities.
|
|
26
|
-
*
|
|
27
|
-
* - **Category B (Leg1-only with children)**: Single-phase activities that
|
|
28
|
-
* execute work and transition to children using the crash-safe
|
|
29
|
-
* `executeLeg1StepProtocol`. Used by `Hook` (passthrough mode),
|
|
30
|
-
* `Signal`, and `Interrupt` (target mode).
|
|
31
|
-
*
|
|
32
|
-
* - **Category C (Leg1-only, no children)**: Terminal activities that
|
|
33
|
-
* execute without spawning children. Used by `Interrupt` (self mode).
|
|
34
|
-
*
|
|
35
|
-
* ## Shared YAML Configuration
|
|
36
|
-
*
|
|
37
|
-
* All activity types support these base properties in the YAML descriptor:
|
|
38
|
-
*
|
|
39
|
-
* | Property | Type | Description |
|
|
40
|
-
* |----------------------|---------|-------------|
|
|
41
|
-
* | `type` | string | Activity type: `trigger`, `worker`, `await`, `hook`, `signal`, `interrupt`, `cycle` |
|
|
42
|
-
* | `title` | string | Human-readable label for the activity |
|
|
43
|
-
* | `input.schema` | object | JSON Schema for input validation |
|
|
44
|
-
* | `input.maps` | object | Maps data from other activities into this activity's input |
|
|
45
|
-
* | `output.schema` | object | JSON Schema for output validation |
|
|
46
|
-
* | `output.maps` | object | Maps/transforms the activity's own output data |
|
|
47
|
-
* | `job.maps` | object | Maps activity data to the shared job state |
|
|
48
|
-
* | `emit` | boolean | If `true`, emits a message to the graph's `publishes` topic |
|
|
49
|
-
* | `persist` | boolean | If `true`, emits the job-completed event while keeping the job active |
|
|
50
|
-
* | `expire` | number | Seconds until the job expires after completion (`-1` = forever) |
|
|
51
|
-
* | `statusThreshold` | number | Custom semaphore threshold for Dynamic Activation Control |
|
|
52
|
-
* | `cycle` | boolean | If `true`, leaves Leg 2 open so the activity can be re-entered |
|
|
53
|
-
*
|
|
54
|
-
* ## Data Mapping Syntax
|
|
55
|
-
*
|
|
56
|
-
* Mapping expressions use curly-brace references to bind data between
|
|
57
|
-
* activities and the shared job state:
|
|
58
|
-
*
|
|
59
|
-
* ```yaml
|
|
60
|
-
* input:
|
|
61
|
-
* maps:
|
|
62
|
-
* x: '{t1.output.data.fieldName}' # reference another activity's output
|
|
63
|
-
* y: '{$self.output.data.fieldName}' # reference own output
|
|
64
|
-
* z: '{$job.data.fieldName}' # reference shared job state
|
|
65
|
-
* s: '{$app.settings.configKey}' # reference app-level settings
|
|
66
|
-
* ```
|
|
67
|
-
*
|
|
68
|
-
* @see {@link https://hotmeshio.github.io/sdk-typescript/docs/quickstart | Quick Start Guide}
|
|
69
|
-
* @see [Model Driven Development](https://hotmeshio.github.io/sdk-typescript/docs/model_driven_development)
|
|
70
|
-
*/
|
|
71
|
-
class Activity {
|
|
72
|
-
constructor(config, data, metadata, hook, engine, context) {
|
|
73
|
-
this.status = stream_1.StreamStatus.SUCCESS;
|
|
74
|
-
this.code = 200;
|
|
75
|
-
this.adjacentIndex = 0;
|
|
76
|
-
this.guidLedger = 0;
|
|
77
|
-
this.config = config;
|
|
78
|
-
this.data = data;
|
|
79
|
-
this.metadata = metadata;
|
|
80
|
-
this.hook = hook;
|
|
81
|
-
this.engine = engine;
|
|
82
|
-
this.context = context || { data: {}, metadata: {} };
|
|
83
|
-
this.logger = engine.logger;
|
|
84
|
-
this.store = engine.store;
|
|
85
|
-
}
|
|
86
|
-
setLeg(leg) {
|
|
87
|
-
this.leg = leg;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* A job is assumed to be complete when its status (a semaphore)
|
|
91
|
-
* reaches `0`. A different threshold can be set in the
|
|
92
|
-
* activity YAML, in support of Dynamic Activation Control.
|
|
93
|
-
*/
|
|
94
|
-
mapStatusThreshold() {
|
|
95
|
-
if (this.config.statusThreshold !== undefined) {
|
|
96
|
-
const threshold = pipe_1.Pipe.resolve(this.config.statusThreshold, this.context);
|
|
97
|
-
if (threshold !== undefined && !isNaN(Number(threshold))) {
|
|
98
|
-
return threshold;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return 0;
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Upon entering leg 1 of a duplexed activity
|
|
105
|
-
*/
|
|
106
|
-
async verifyEntry() {
|
|
107
|
-
this.setLeg(1);
|
|
108
|
-
await this.getState();
|
|
109
|
-
const threshold = this.mapStatusThreshold();
|
|
110
|
-
try {
|
|
111
|
-
collator_1.CollatorService.assertJobActive(this.context.metadata.js, this.context.metadata.jid, this.metadata.aid, threshold);
|
|
112
|
-
}
|
|
113
|
-
catch (error) {
|
|
114
|
-
await collator_1.CollatorService.notarizeEntry(this);
|
|
115
|
-
if (threshold > 0) {
|
|
116
|
-
if (this.context.metadata.js <= threshold) {
|
|
117
|
-
//Dynamic Activation Control: convergent claim — only the
|
|
118
|
-
//activity whose HINCRBY reaches exactly 0 runs completion.
|
|
119
|
-
const status = await this.setStatus(-threshold);
|
|
120
|
-
if (Number(status) === 0) {
|
|
121
|
-
const txn = this.store.transact();
|
|
122
|
-
await this.engine.runJobCompletionTasks(this.context, {}, txn);
|
|
123
|
-
await txn.exec();
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
throw error;
|
|
129
|
-
}
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
await collator_1.CollatorService.notarizeEntry(this);
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Upon entering leg 2 of a duplexed activity.
|
|
136
|
-
* Increments both the activity ledger (+1) and GUID ledger (+1).
|
|
137
|
-
* Stores the GUID ledger value for step-level resume decisions.
|
|
138
|
-
*/
|
|
139
|
-
async verifyReentry() {
|
|
140
|
-
const msgGuid = this.context.metadata.guid;
|
|
141
|
-
this.setLeg(2);
|
|
142
|
-
await this.getState();
|
|
143
|
-
this.context.metadata.guid = msgGuid;
|
|
144
|
-
collator_1.CollatorService.assertJobActive(this.context.metadata.js, this.context.metadata.jid, this.metadata.aid);
|
|
145
|
-
const [activityLedger, guidLedger] = await collator_1.CollatorService.notarizeLeg2Entry(this, msgGuid);
|
|
146
|
-
this.guidLedger = guidLedger;
|
|
147
|
-
return activityLedger;
|
|
148
|
-
}
|
|
149
|
-
//******** DUPLEX RE-ENTRY POINT ********//
|
|
150
|
-
async processEvent(status = stream_1.StreamStatus.SUCCESS, code = 200, type = 'output') {
|
|
151
|
-
this.setLeg(2);
|
|
152
|
-
const jid = this.context.metadata.jid;
|
|
153
|
-
if (!jid) {
|
|
154
|
-
this.logger.error('activity-process-event-error', {
|
|
155
|
-
message: 'job id is undefined',
|
|
156
|
-
});
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
const aid = this.metadata.aid;
|
|
160
|
-
this.status = status;
|
|
161
|
-
this.code = code;
|
|
162
|
-
this.logger.debug('activity-process-event', {
|
|
163
|
-
topic: this.config.subtype,
|
|
164
|
-
jid,
|
|
165
|
-
aid,
|
|
166
|
-
status,
|
|
167
|
-
code,
|
|
168
|
-
});
|
|
169
|
-
let telemetry;
|
|
170
|
-
try {
|
|
171
|
-
const collationKey = await this.verifyReentry();
|
|
172
|
-
this.adjacentIndex = collator_1.CollatorService.getDimensionalIndex(collationKey);
|
|
173
|
-
telemetry = new telemetry_1.TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
|
|
174
|
-
telemetry.startActivitySpan(this.leg);
|
|
175
|
-
//bind data per status type
|
|
176
|
-
if (status === stream_1.StreamStatus.ERROR) {
|
|
177
|
-
this.bindActivityError(this.data);
|
|
178
|
-
this.adjacencyList = await this.filterAdjacent();
|
|
179
|
-
if (!this.adjacencyList.length) {
|
|
180
|
-
this.bindJobError(this.data);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
this.bindActivityData(type);
|
|
185
|
-
this.adjacencyList = await this.filterAdjacent();
|
|
186
|
-
}
|
|
187
|
-
this.mapJobData();
|
|
188
|
-
//When an unrecoverable error has no matching transitions
|
|
189
|
-
//(e.g., code 500 from raw errors after retries exhausted),
|
|
190
|
-
//mark the job as terminally errored so the step protocol
|
|
191
|
-
//can force completion via the isErrorTerminal path.
|
|
192
|
-
if (status === stream_1.StreamStatus.ERROR && !this.adjacencyList?.length) {
|
|
193
|
-
if (!this.context.data)
|
|
194
|
-
this.context.data = {};
|
|
195
|
-
this.context.data.done = true;
|
|
196
|
-
this.context.data.$error = {
|
|
197
|
-
message: this.data?.message || 'unknown error',
|
|
198
|
-
code: enums_1.HMSH_CODE_DURABLE_MAXED,
|
|
199
|
-
stack: this.data?.stack,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
//determine step parameters
|
|
203
|
-
const delta = status === stream_1.StreamStatus.PENDING
|
|
204
|
-
? this.adjacencyList.length
|
|
205
|
-
: this.adjacencyList.length - 1;
|
|
206
|
-
const shouldFinalize = status !== stream_1.StreamStatus.PENDING;
|
|
207
|
-
//execute 3-step protocol
|
|
208
|
-
const thresholdHit = await this.executeStepProtocol(delta, shouldFinalize);
|
|
209
|
-
//telemetry
|
|
210
|
-
telemetry.mapActivityAttributes();
|
|
211
|
-
telemetry.setActivityAttributes({});
|
|
212
|
-
}
|
|
213
|
-
catch (error) {
|
|
214
|
-
if (error instanceof errors_1.CollationError) {
|
|
215
|
-
this.logger.info(`process-event-${error.fault}-error`, { error });
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
else if (error instanceof errors_1.InactiveJobError) {
|
|
219
|
-
this.logger.info('process-event-inactive-job-error', { error });
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
else if (error instanceof errors_1.GenerationalError) {
|
|
223
|
-
this.logger.info('process-event-generational-job-error', { error });
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
else if (error instanceof errors_1.GetStateError) {
|
|
227
|
-
this.logger.info('process-event-get-job-error', { error });
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
this.logger.error('activity-process-event-error', {
|
|
231
|
-
error,
|
|
232
|
-
message: error.message,
|
|
233
|
-
stack: error.stack,
|
|
234
|
-
name: error.name,
|
|
235
|
-
});
|
|
236
|
-
telemetry?.setActivityError(error.message);
|
|
237
|
-
throw error;
|
|
238
|
-
}
|
|
239
|
-
finally {
|
|
240
|
-
telemetry?.endActivitySpan();
|
|
241
|
-
this.logger.debug('activity-process-event-end', { jid, aid });
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Executes the 3-step Leg2 protocol using GUID ledger for
|
|
246
|
-
* crash-safe resume. Each step bundles durable writes with
|
|
247
|
-
* its concluding digit update in a single transaction.
|
|
248
|
-
*
|
|
249
|
-
* @returns true if this transition caused the job to complete
|
|
250
|
-
*/
|
|
251
|
-
async executeStepProtocol(delta, shouldFinalize) {
|
|
252
|
-
const msgGuid = this.context.metadata.guid;
|
|
253
|
-
const threshold = this.mapStatusThreshold();
|
|
254
|
-
const { id: appId } = await this.engine.getVID();
|
|
255
|
-
//Step 1: Save work (skip if GUID 10B already set)
|
|
256
|
-
if (!collator_1.CollatorService.isGuidStep1Done(this.guidLedger)) {
|
|
257
|
-
const txn1 = this.store.transact();
|
|
258
|
-
await this.setState(txn1);
|
|
259
|
-
await collator_1.CollatorService.notarizeStep1(this, msgGuid, txn1);
|
|
260
|
-
await txn1.exec();
|
|
261
|
-
}
|
|
262
|
-
//Step 2: Spawn children + semaphore + edge capture (skip if GUID 1B already set)
|
|
263
|
-
let thresholdHit = false;
|
|
264
|
-
if (!collator_1.CollatorService.isGuidStep2Done(this.guidLedger)) {
|
|
265
|
-
const txn2 = this.store.transact();
|
|
266
|
-
//queue step markers first
|
|
267
|
-
await collator_1.CollatorService.notarizeStep2(this, msgGuid, txn2);
|
|
268
|
-
//queue child publications
|
|
269
|
-
for (const child of this.adjacencyList) {
|
|
270
|
-
await this.engine.router?.publishMessage(null, child, txn2);
|
|
271
|
-
}
|
|
272
|
-
//queue semaphore update + edge capture LAST (so result is at end)
|
|
273
|
-
await this.store.setStatusAndCollateGuid(delta, threshold, this.context.metadata.jid, appId, msgGuid, collator_1.CollatorService.WEIGHTS.GUID_SNAPSHOT, txn2);
|
|
274
|
-
const results = (await txn2.exec());
|
|
275
|
-
thresholdHit = this.resolveThresholdHit(results);
|
|
276
|
-
this.logger.debug('step-protocol-step2-complete', {
|
|
277
|
-
jid: this.context.metadata.jid,
|
|
278
|
-
aid: this.metadata.aid,
|
|
279
|
-
delta,
|
|
280
|
-
threshold,
|
|
281
|
-
thresholdHit,
|
|
282
|
-
lastResult: results[results.length - 1],
|
|
283
|
-
resultCount: results.length,
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
//Step 2 already done; check GUID snapshot for edge
|
|
288
|
-
thresholdHit = collator_1.CollatorService.isGuidJobClosed(this.guidLedger);
|
|
289
|
-
}
|
|
290
|
-
//Step 3: Job completion tasks (edge hit OR emit/persist, skip if GUID 100M already set)
|
|
291
|
-
//When an activity marks the job done with an unrecoverable error
|
|
292
|
-
//(e.g., stopper after max retries), force completion even when the
|
|
293
|
-
//semaphore threshold isn't hit (the signaler's +1 contribution
|
|
294
|
-
//prevents threshold 0 from matching).
|
|
295
|
-
const isErrorTerminal = !thresholdHit
|
|
296
|
-
&& this.context.data?.done === true
|
|
297
|
-
&& !!this.context.data?.$error;
|
|
298
|
-
const needsCompletion = thresholdHit || this.shouldEmit() || this.shouldPersistJob() || isErrorTerminal;
|
|
299
|
-
if (needsCompletion && !collator_1.CollatorService.isGuidStep3Done(this.guidLedger)) {
|
|
300
|
-
const txn3 = this.store.transact();
|
|
301
|
-
const options = (thresholdHit || isErrorTerminal) ? {} : { emit: !this.shouldPersistJob() };
|
|
302
|
-
await this.engine.runJobCompletionTasks(this.context, options, txn3);
|
|
303
|
-
await collator_1.CollatorService.notarizeStep3(this, msgGuid, txn3);
|
|
304
|
-
const shouldFinalizeNow = (thresholdHit || isErrorTerminal) ? shouldFinalize : this.shouldPersistJob();
|
|
305
|
-
if (shouldFinalizeNow) {
|
|
306
|
-
await collator_1.CollatorService.notarizeFinalize(this, txn3);
|
|
307
|
-
}
|
|
308
|
-
await txn3.exec();
|
|
309
|
-
}
|
|
310
|
-
else if (needsCompletion) {
|
|
311
|
-
this.logger.debug('step-protocol-step3-skipped-already-done', {
|
|
312
|
-
jid: this.context.metadata.jid,
|
|
313
|
-
aid: this.metadata.aid,
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
this.logger.debug('step-protocol-no-threshold', {
|
|
318
|
-
jid: this.context.metadata.jid,
|
|
319
|
-
aid: this.metadata.aid,
|
|
320
|
-
thresholdHit,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
return thresholdHit;
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* Extracts the thresholdHit value from transaction results.
|
|
327
|
-
* The setStatusAndCollateGuid result is the last item.
|
|
328
|
-
*/
|
|
329
|
-
resolveThresholdHit(results) {
|
|
330
|
-
const last = results[results.length - 1];
|
|
331
|
-
const value = Array.isArray(last) ? last[1] : last;
|
|
332
|
-
return Number(value) === 1;
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Extracts the job status from the last result of a transaction.
|
|
336
|
-
* Used by subclass Leg1 process methods for telemetry.
|
|
337
|
-
*/
|
|
338
|
-
resolveStatus(multiResponse) {
|
|
339
|
-
const activityStatus = multiResponse[multiResponse.length - 1];
|
|
340
|
-
if (Array.isArray(activityStatus)) {
|
|
341
|
-
return Number(activityStatus[1]);
|
|
342
|
-
}
|
|
343
|
-
else {
|
|
344
|
-
return Number(activityStatus);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
/**
|
|
348
|
-
* Leg1 entry verification for Category B activities (Leg1-only with children).
|
|
349
|
-
* Returns true if this is a resume (Leg1 already completed on a prior attempt).
|
|
350
|
-
* On resume, loads the GUID ledger for step-level resume decisions.
|
|
351
|
-
*/
|
|
352
|
-
async verifyLeg1Entry() {
|
|
353
|
-
const msgGuid = this.context.metadata.guid;
|
|
354
|
-
this.setLeg(1);
|
|
355
|
-
await this.getState();
|
|
356
|
-
this.context.metadata.guid = msgGuid;
|
|
357
|
-
const threshold = this.mapStatusThreshold();
|
|
358
|
-
try {
|
|
359
|
-
collator_1.CollatorService.assertJobActive(this.context.metadata.js, this.context.metadata.jid, this.metadata.aid, threshold);
|
|
360
|
-
}
|
|
361
|
-
catch (error) {
|
|
362
|
-
if (error instanceof errors_1.InactiveJobError && threshold > 0) {
|
|
363
|
-
//Dynamic Activation Control: threshold met, close the job
|
|
364
|
-
await collator_1.CollatorService.notarizeEntry(this);
|
|
365
|
-
if (this.context.metadata.js === threshold) {
|
|
366
|
-
//conclude job EXACTLY ONCE
|
|
367
|
-
const status = await this.setStatus(-threshold);
|
|
368
|
-
if (Number(status) === 0) {
|
|
369
|
-
await this.engine.runJobCompletionTasks(this.context);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
throw error;
|
|
374
|
-
}
|
|
375
|
-
try {
|
|
376
|
-
await collator_1.CollatorService.notarizeEntry(this);
|
|
377
|
-
return false;
|
|
378
|
-
}
|
|
379
|
-
catch (error) {
|
|
380
|
-
if (error instanceof errors_1.CollationError && error.fault === 'duplicate') {
|
|
381
|
-
if (this.config.cycle) {
|
|
382
|
-
//Cycle re-entry: Leg1 already complete from prior iteration.
|
|
383
|
-
//Increment Leg2 counter to derive the new dimensional index,
|
|
384
|
-
//so children run in a fresh dimensional plane.
|
|
385
|
-
const [activityLedger, guidLedger] = await collator_1.CollatorService.notarizeLeg2Entry(this, msgGuid);
|
|
386
|
-
this.adjacentIndex =
|
|
387
|
-
collator_1.CollatorService.getDimensionalIndex(activityLedger);
|
|
388
|
-
this.guidLedger = guidLedger;
|
|
389
|
-
return false;
|
|
390
|
-
}
|
|
391
|
-
//100B is set — Leg1 work already committed. Load GUID for step resume.
|
|
392
|
-
const guidValue = await this.store.collateSynthetic(this.context.metadata.jid, msgGuid, 0);
|
|
393
|
-
this.guidLedger = guidValue;
|
|
394
|
-
return true;
|
|
395
|
-
}
|
|
396
|
-
throw error;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* Executes the 3-step Leg1 protocol for Category B activities
|
|
401
|
-
* (Leg1-only with children, e.g., Hook passthrough, Signal, Interrupt-another).
|
|
402
|
-
* Uses the incoming Leg1 message GUID as the GUID ledger key.
|
|
403
|
-
*
|
|
404
|
-
* Step A: setState + notarizeLeg1Completion + step1 markers (transaction 1)
|
|
405
|
-
* Step B: publish children + step2 markers + setStatusAndCollateGuid (transaction 2)
|
|
406
|
-
* Step C: if edge → runJobCompletionTasks + step3 markers + finalize (transaction 3)
|
|
407
|
-
*
|
|
408
|
-
* @returns true if this transition caused the job to complete
|
|
409
|
-
*/
|
|
410
|
-
async executeLeg1StepProtocol(delta) {
|
|
411
|
-
const msgGuid = this.context.metadata.guid;
|
|
412
|
-
const threshold = this.mapStatusThreshold();
|
|
413
|
-
const { id: appId } = await this.engine.getVID();
|
|
414
|
-
//Step A: Save work + Leg1 completion marker
|
|
415
|
-
if (!collator_1.CollatorService.isGuidStep1Done(this.guidLedger)) {
|
|
416
|
-
const txn1 = this.store.transact();
|
|
417
|
-
await this.setState(txn1);
|
|
418
|
-
if (this.adjacentIndex === 0) {
|
|
419
|
-
//First entry: mark Leg1 complete. On cycle re-entry
|
|
420
|
-
//(adjacentIndex > 0), Leg1 is already complete and the
|
|
421
|
-
//Leg2 counter was already incremented by notarizeLeg2Entry.
|
|
422
|
-
await collator_1.CollatorService.notarizeLeg1Completion(this, txn1);
|
|
423
|
-
}
|
|
424
|
-
await collator_1.CollatorService.notarizeStep1(this, msgGuid, txn1);
|
|
425
|
-
await txn1.exec();
|
|
426
|
-
}
|
|
427
|
-
//Step B: Spawn children + semaphore + edge capture
|
|
428
|
-
let thresholdHit = false;
|
|
429
|
-
if (!collator_1.CollatorService.isGuidStep2Done(this.guidLedger)) {
|
|
430
|
-
const txn2 = this.store.transact();
|
|
431
|
-
await collator_1.CollatorService.notarizeStep2(this, msgGuid, txn2);
|
|
432
|
-
for (const child of this.adjacencyList) {
|
|
433
|
-
await this.engine.router?.publishMessage(null, child, txn2);
|
|
434
|
-
}
|
|
435
|
-
await this.store.setStatusAndCollateGuid(delta, threshold, this.context.metadata.jid, appId, msgGuid, collator_1.CollatorService.WEIGHTS.GUID_SNAPSHOT, txn2);
|
|
436
|
-
const results = (await txn2.exec());
|
|
437
|
-
thresholdHit = this.resolveThresholdHit(results);
|
|
438
|
-
this.logger.debug('leg1-step-protocol-stepB-complete', {
|
|
439
|
-
jid: this.context.metadata.jid,
|
|
440
|
-
aid: this.metadata.aid,
|
|
441
|
-
delta,
|
|
442
|
-
threshold,
|
|
443
|
-
thresholdHit,
|
|
444
|
-
lastResult: results[results.length - 1],
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
else {
|
|
448
|
-
thresholdHit = collator_1.CollatorService.isGuidJobClosed(this.guidLedger);
|
|
449
|
-
}
|
|
450
|
-
//Step C: Job completion tasks (edge hit OR emit/persist)
|
|
451
|
-
//When an activity marks the job done with an unrecoverable error
|
|
452
|
-
//(e.g., stopper after max retries), force completion even when the
|
|
453
|
-
//semaphore threshold isn't hit.
|
|
454
|
-
const isErrorTerminal = !thresholdHit
|
|
455
|
-
&& this.context.data?.done === true
|
|
456
|
-
&& !!this.context.data?.$error;
|
|
457
|
-
const needsCompletion = thresholdHit || this.shouldEmit() || this.shouldPersistJob() || isErrorTerminal;
|
|
458
|
-
if (needsCompletion && !collator_1.CollatorService.isGuidStep3Done(this.guidLedger)) {
|
|
459
|
-
const txn3 = this.store.transact();
|
|
460
|
-
const options = (thresholdHit || isErrorTerminal) ? {} : { emit: !this.shouldPersistJob() };
|
|
461
|
-
await this.engine.runJobCompletionTasks(this.context, options, txn3);
|
|
462
|
-
await collator_1.CollatorService.notarizeStep3(this, msgGuid, txn3);
|
|
463
|
-
await collator_1.CollatorService.notarizeFinalize(this, txn3);
|
|
464
|
-
await txn3.exec();
|
|
465
|
-
}
|
|
466
|
-
return thresholdHit;
|
|
467
|
-
}
|
|
468
|
-
mapJobData() {
|
|
469
|
-
if (this.config.job?.maps) {
|
|
470
|
-
const mapper = new mapper_1.MapperService((0, utils_1.deepCopy)(this.config.job.maps), this.context);
|
|
471
|
-
const output = mapper.mapRules();
|
|
472
|
-
if (output) {
|
|
473
|
-
for (const key in output) {
|
|
474
|
-
const f1 = key.indexOf('[');
|
|
475
|
-
//keys with array notation suffix `somekey[]` represent
|
|
476
|
-
//dynamically-keyed mappings whose `value` must be moved to the output.
|
|
477
|
-
//The `value` must be an object with keys appropriate to the
|
|
478
|
-
//notation type: `somekey[0] (array)`, `somekey[-] (mark)`, OR `somekey[_] (search)`
|
|
479
|
-
if (f1 > -1) {
|
|
480
|
-
const amount = key.substring(f1 + 1).split(']')[0];
|
|
481
|
-
if (!isNaN(Number(amount))) {
|
|
482
|
-
const left = key.substring(0, f1);
|
|
483
|
-
output[left] = output[key];
|
|
484
|
-
delete output[key];
|
|
485
|
-
}
|
|
486
|
-
else if (amount === '-' || amount === '_') {
|
|
487
|
-
const obj = output[key];
|
|
488
|
-
Object.keys(obj).forEach((newKey) => {
|
|
489
|
-
output[newKey] = obj[newKey];
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
this.context.data = output;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
mapInputData() {
|
|
499
|
-
if (this.config.input?.maps) {
|
|
500
|
-
const mapper = new mapper_1.MapperService((0, utils_1.deepCopy)(this.config.input.maps), this.context);
|
|
501
|
-
this.context.data = mapper.mapRules();
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
mapOutputData() {
|
|
505
|
-
//activity YAML may include output map data that produces/extends activity output data.
|
|
506
|
-
if (this.config.output?.maps) {
|
|
507
|
-
const mapper = new mapper_1.MapperService((0, utils_1.deepCopy)(this.config.output.maps), this.context);
|
|
508
|
-
const actOutData = mapper.mapRules();
|
|
509
|
-
const activityId = this.metadata.aid;
|
|
510
|
-
const data = { ...this.context[activityId].output, ...actOutData };
|
|
511
|
-
this.context[activityId].output.data = data;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
async registerTimeout() {
|
|
515
|
-
//set timeout in support of hook and/or duplex
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* Any StreamMessage with a status of ERROR is bound to the activity
|
|
519
|
-
*/
|
|
520
|
-
bindActivityError(data) {
|
|
521
|
-
const md = this.context[this.metadata.aid].output.metadata;
|
|
522
|
-
md.err = JSON.stringify(this.data);
|
|
523
|
-
//(temporary...useful for mapping error parts in the app.yaml)
|
|
524
|
-
md.$error = { ...data, is_stream_error: true };
|
|
525
|
-
}
|
|
526
|
-
/**
|
|
527
|
-
* unhandled activity errors (activities that return an ERROR StreamMessage
|
|
528
|
-
* status and have no adjacent children to transition to) are bound to the job
|
|
529
|
-
*/
|
|
530
|
-
bindJobError(data) {
|
|
531
|
-
this.context.metadata.err = JSON.stringify({
|
|
532
|
-
...data,
|
|
533
|
-
is_stream_error: true,
|
|
534
|
-
});
|
|
535
|
-
}
|
|
536
|
-
async getTriggerConfig() {
|
|
537
|
-
return await this.store.getSchema(this.config.trigger, await this.engine.getVID());
|
|
538
|
-
}
|
|
539
|
-
getJobStatus() {
|
|
540
|
-
return null;
|
|
541
|
-
}
|
|
542
|
-
async setStatus(amount, transaction) {
|
|
543
|
-
const { id: appId } = await this.engine.getVID();
|
|
544
|
-
return await this.store.setStatus(amount, this.context.metadata.jid, appId, transaction);
|
|
545
|
-
}
|
|
546
|
-
authorizeEntry(_state) {
|
|
547
|
-
//seed writes removed: child activities increment from 0 (null field).
|
|
548
|
-
//FINALIZE (200T) sets pos 1 directly to 2 without needing a 100T base.
|
|
549
|
-
return [];
|
|
550
|
-
}
|
|
551
|
-
bindDimensionalAddress(state) {
|
|
552
|
-
const dad = this.resolveDad();
|
|
553
|
-
state[`${this.metadata.aid}/output/metadata/dad`] = dad;
|
|
554
|
-
}
|
|
555
|
-
async setState(transaction) {
|
|
556
|
-
const jobId = this.context.metadata.jid;
|
|
557
|
-
this.bindJobMetadata();
|
|
558
|
-
this.bindActivityMetadata();
|
|
559
|
-
const state = {};
|
|
560
|
-
await this.bindJobState(state);
|
|
561
|
-
const presets = this.authorizeEntry(state);
|
|
562
|
-
this.bindDimensionalAddress(state);
|
|
563
|
-
this.bindActivityState(state);
|
|
564
|
-
//symbolNames holds symkeys
|
|
565
|
-
const symbolNames = [
|
|
566
|
-
`$${this.config.subscribes}`,
|
|
567
|
-
this.metadata.aid,
|
|
568
|
-
...presets,
|
|
569
|
-
];
|
|
570
|
-
const dIds = collator_1.CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], this.resolveDad());
|
|
571
|
-
return await this.store.setState(state, this.getJobStatus(), jobId, symbolNames, dIds, transaction);
|
|
572
|
-
}
|
|
573
|
-
bindJobMetadata() {
|
|
574
|
-
//both legs of the most recently run activity (1 and 2) modify ju (job_updated)
|
|
575
|
-
this.context.metadata.ju = (0, utils_1.formatISODate)(new Date());
|
|
576
|
-
}
|
|
577
|
-
bindActivityMetadata() {
|
|
578
|
-
const self = this.context['$self'];
|
|
579
|
-
if (!self.output.metadata) {
|
|
580
|
-
self.output.metadata = {};
|
|
581
|
-
}
|
|
582
|
-
if (this.status === stream_1.StreamStatus.ERROR) {
|
|
583
|
-
self.output.metadata.err = JSON.stringify(this.data);
|
|
584
|
-
}
|
|
585
|
-
const ts = (0, utils_1.formatISODate)(new Date());
|
|
586
|
-
self.output.metadata.ac = ts;
|
|
587
|
-
self.output.metadata.au = ts;
|
|
588
|
-
self.output.metadata.atp = this.config.type;
|
|
589
|
-
if (this.config.subtype) {
|
|
590
|
-
self.output.metadata.stp = this.config.subtype;
|
|
591
|
-
}
|
|
592
|
-
self.output.metadata.aid = this.metadata.aid;
|
|
593
|
-
}
|
|
594
|
-
async bindJobState(state) {
|
|
595
|
-
const triggerConfig = await this.getTriggerConfig();
|
|
596
|
-
const PRODUCES = [
|
|
597
|
-
...(triggerConfig.PRODUCES || []),
|
|
598
|
-
...this.bindJobMetadataPaths(),
|
|
599
|
-
];
|
|
600
|
-
for (const path of PRODUCES) {
|
|
601
|
-
const value = (0, utils_1.getValueByPath)(this.context, path);
|
|
602
|
-
if (value !== undefined) {
|
|
603
|
-
state[path] = value;
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
for (const key in this.context?.data ?? {}) {
|
|
607
|
-
if (key.startsWith('-') || key.startsWith('_')) {
|
|
608
|
-
state[key] = this.context.data[key];
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
telemetry_1.TelemetryService.bindJobTelemetryToState(state, this.config, this.context);
|
|
612
|
-
}
|
|
613
|
-
bindActivityState(state) {
|
|
614
|
-
const produces = [
|
|
615
|
-
...this.config.produces,
|
|
616
|
-
...this.bindActivityMetadataPaths(),
|
|
617
|
-
];
|
|
618
|
-
for (const path of produces) {
|
|
619
|
-
const prefixedPath = `${this.metadata.aid}/${path}`;
|
|
620
|
-
const value = (0, utils_1.getValueByPath)(this.context, prefixedPath);
|
|
621
|
-
if (value !== undefined) {
|
|
622
|
-
state[prefixedPath] = value;
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
telemetry_1.TelemetryService.bindActivityTelemetryToState(state, this.config, this.metadata, this.context, this.leg);
|
|
626
|
-
}
|
|
627
|
-
bindJobMetadataPaths() {
|
|
628
|
-
return serializer_1.MDATA_SYMBOLS.JOB_UPDATE.KEYS.map((key) => `metadata/${key}`);
|
|
629
|
-
}
|
|
630
|
-
bindActivityMetadataPaths() {
|
|
631
|
-
const keys_to_save = this.leg === 1 ? 'ACTIVITY' : 'ACTIVITY_UPDATE';
|
|
632
|
-
return serializer_1.MDATA_SYMBOLS[keys_to_save].KEYS.map((key) => `output/metadata/${key}`);
|
|
633
|
-
}
|
|
634
|
-
async getState() {
|
|
635
|
-
const gid = this.context.metadata.gid;
|
|
636
|
-
const jobSymbolHashName = `$${this.config.subscribes}`;
|
|
637
|
-
const consumes = {
|
|
638
|
-
[jobSymbolHashName]: serializer_1.MDATA_SYMBOLS.JOB.KEYS.map((key) => `metadata/${key}`),
|
|
639
|
-
};
|
|
640
|
-
for (let [activityId, paths] of Object.entries(this.config.consumes)) {
|
|
641
|
-
if (activityId === '$job') {
|
|
642
|
-
for (const path of paths) {
|
|
643
|
-
consumes[jobSymbolHashName].push(path);
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
else {
|
|
647
|
-
if (activityId === '$self') {
|
|
648
|
-
activityId = this.metadata.aid;
|
|
649
|
-
}
|
|
650
|
-
if (!consumes[activityId]) {
|
|
651
|
-
consumes[activityId] = [];
|
|
652
|
-
}
|
|
653
|
-
for (const path of paths) {
|
|
654
|
-
consumes[activityId].push(`${activityId}/${path}`);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
telemetry_1.TelemetryService.addTargetTelemetryPaths(consumes, this.config, this.metadata, this.leg);
|
|
659
|
-
const { dad, jid } = this.context.metadata;
|
|
660
|
-
const dIds = collator_1.CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], dad || '');
|
|
661
|
-
//`state` is a unidimensional hash; context is a tree
|
|
662
|
-
const [state, _status] = await this.store.getState(jid, consumes, dIds);
|
|
663
|
-
this.context = (0, utils_1.restoreHierarchy)(state);
|
|
664
|
-
this.assertGenerationalId(this.context?.metadata?.gid, gid);
|
|
665
|
-
this.initDimensionalAddress(dad);
|
|
666
|
-
this.initSelf(this.context);
|
|
667
|
-
this.initPolicies(this.context);
|
|
668
|
-
}
|
|
669
|
-
/**
|
|
670
|
-
* if the job is created/deleted/created with the same key,
|
|
671
|
-
* the 'gid' ensures no stale messages (such as sleep delays)
|
|
672
|
-
* enter the workstream. Any message with a mismatched gid
|
|
673
|
-
* belongs to a prior job and can safely be ignored/dropped.
|
|
674
|
-
*/
|
|
675
|
-
assertGenerationalId(jobGID, msgGID) {
|
|
676
|
-
if (msgGID !== jobGID) {
|
|
677
|
-
throw new errors_1.GenerationalError(jobGID, msgGID, this.context?.metadata?.jid ?? '', this.context?.metadata?.aid ?? '', this.context?.metadata?.dad ?? '');
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
initDimensionalAddress(dad) {
|
|
681
|
-
this.metadata.dad = dad;
|
|
682
|
-
}
|
|
683
|
-
initSelf(context) {
|
|
684
|
-
const activityId = this.metadata.aid;
|
|
685
|
-
if (!context[activityId]) {
|
|
686
|
-
context[activityId] = {};
|
|
687
|
-
}
|
|
688
|
-
const self = context[activityId];
|
|
689
|
-
if (!self.output) {
|
|
690
|
-
self.output = {};
|
|
691
|
-
}
|
|
692
|
-
if (!self.input) {
|
|
693
|
-
self.input = {};
|
|
694
|
-
}
|
|
695
|
-
if (!self.hook) {
|
|
696
|
-
self.hook = {};
|
|
697
|
-
}
|
|
698
|
-
if (!self.output.metadata) {
|
|
699
|
-
self.output.metadata = {};
|
|
700
|
-
}
|
|
701
|
-
//prebind the updated timestamp (mappings need the time)
|
|
702
|
-
self.output.metadata.au = (0, utils_1.formatISODate)(new Date());
|
|
703
|
-
context['$self'] = self;
|
|
704
|
-
context['$job'] = context; //NEVER call STRINGIFY! (now circular)
|
|
705
|
-
return context;
|
|
706
|
-
}
|
|
707
|
-
initPolicies(context) {
|
|
708
|
-
const expire = pipe_1.Pipe.resolve(this.config.expire ?? enums_1.HMSH_EXPIRE_DURATION, context);
|
|
709
|
-
context.metadata.expire = expire;
|
|
710
|
-
if (this.config.persistent != undefined) {
|
|
711
|
-
const persistent = pipe_1.Pipe.resolve(this.config.persistent ?? false, context);
|
|
712
|
-
context.metadata.persistent = persistent;
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
bindActivityData(type) {
|
|
716
|
-
this.context[this.metadata.aid][type].data = this.data;
|
|
717
|
-
}
|
|
718
|
-
resolveDad() {
|
|
719
|
-
let dad = this.metadata.dad;
|
|
720
|
-
if (this.adjacentIndex > 0) {
|
|
721
|
-
//if adjacent index > 0 the activity is cycling; replace last index with cycle index
|
|
722
|
-
dad = `${dad.substring(0, dad.lastIndexOf(','))},${this.adjacentIndex}`;
|
|
723
|
-
}
|
|
724
|
-
return dad;
|
|
725
|
-
}
|
|
726
|
-
resolveAdjacentDad() {
|
|
727
|
-
//concat self and child dimension (all children (leg 1) begin life at 0)
|
|
728
|
-
return `${this.resolveDad()}${collator_1.CollatorService.getDimensionalSeed(0)}`;
|
|
729
|
-
}
|
|
730
|
-
async filterAdjacent() {
|
|
731
|
-
const adjacencyList = [];
|
|
732
|
-
const transitions = await this.store.getTransitions(await this.engine.getVID());
|
|
733
|
-
const transition = transitions[`.${this.metadata.aid}`];
|
|
734
|
-
//resolve the dimensional address for adjacent children
|
|
735
|
-
const adjacentDad = this.resolveAdjacentDad();
|
|
736
|
-
if (transition) {
|
|
737
|
-
for (const toActivityId in transition) {
|
|
738
|
-
const transitionRule = transition[toActivityId];
|
|
739
|
-
if (mapper_1.MapperService.evaluate(transitionRule, this.context, this.code)) {
|
|
740
|
-
adjacencyList.push({
|
|
741
|
-
metadata: {
|
|
742
|
-
guid: (0, utils_1.guid)(),
|
|
743
|
-
jid: this.context.metadata.jid,
|
|
744
|
-
gid: this.context.metadata.gid,
|
|
745
|
-
dad: adjacentDad,
|
|
746
|
-
aid: toActivityId,
|
|
747
|
-
spn: this.context['$self'].output.metadata?.l2s,
|
|
748
|
-
trc: this.context.metadata.trc,
|
|
749
|
-
},
|
|
750
|
-
type: stream_1.StreamDataType.TRANSITION,
|
|
751
|
-
data: {},
|
|
752
|
-
});
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
return adjacencyList;
|
|
757
|
-
}
|
|
758
|
-
isJobComplete(jobStatus) {
|
|
759
|
-
return jobStatus <= 0;
|
|
760
|
-
}
|
|
761
|
-
shouldEmit() {
|
|
762
|
-
if (this.config.emit) {
|
|
763
|
-
return pipe_1.Pipe.resolve(this.config.emit, this.context) === true;
|
|
764
|
-
}
|
|
765
|
-
return false;
|
|
766
|
-
}
|
|
767
|
-
/**
|
|
768
|
-
* emits the job completed event while leaving the job active, allowing
|
|
769
|
-
* a `main` thread to exit while other threads continue to run.
|
|
770
|
-
* @private
|
|
771
|
-
*/
|
|
772
|
-
shouldPersistJob() {
|
|
773
|
-
if (this.config.persist !== undefined) {
|
|
774
|
-
return pipe_1.Pipe.resolve(this.config.persist, this.context) === true;
|
|
775
|
-
}
|
|
776
|
-
return false;
|
|
777
|
-
}
|
|
778
|
-
/**
|
|
779
|
-
* A job with a vale < -100_000_000 is considered interrupted,
|
|
780
|
-
* as the interruption event decrements the job status by 1billion.
|
|
781
|
-
*/
|
|
782
|
-
jobWasInterrupted(jobStatus) {
|
|
783
|
-
return jobStatus < -100000000;
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
exports.Activity = Activity;
|