@hotmeshio/hotmesh 0.0.1
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/LICENSE +214 -0
- package/README.md +241 -0
- package/build/index.d.ts +4 -0
- package/build/index.js +7 -0
- package/build/modules/errors.d.ts +28 -0
- package/build/modules/errors.js +50 -0
- package/build/modules/key.d.ts +75 -0
- package/build/modules/key.js +116 -0
- package/build/modules/utils.d.ts +34 -0
- package/build/modules/utils.js +173 -0
- package/build/package.json +73 -0
- package/build/services/activities/activity.d.ts +59 -0
- package/build/services/activities/activity.js +396 -0
- package/build/services/activities/await.d.ts +16 -0
- package/build/services/activities/await.js +143 -0
- package/build/services/activities/emit.d.ts +9 -0
- package/build/services/activities/emit.js +13 -0
- package/build/services/activities/index.d.ts +15 -0
- package/build/services/activities/index.js +16 -0
- package/build/services/activities/iterate.d.ts +9 -0
- package/build/services/activities/iterate.js +13 -0
- package/build/services/activities/trigger.d.ts +22 -0
- package/build/services/activities/trigger.js +161 -0
- package/build/services/activities/worker.d.ts +17 -0
- package/build/services/activities/worker.js +164 -0
- package/build/services/collator/index.d.ts +54 -0
- package/build/services/collator/index.js +171 -0
- package/build/services/compiler/deployer.d.ts +35 -0
- package/build/services/compiler/deployer.js +412 -0
- package/build/services/compiler/index.d.ts +30 -0
- package/build/services/compiler/index.js +111 -0
- package/build/services/compiler/validator.d.ts +32 -0
- package/build/services/compiler/validator.js +134 -0
- package/build/services/connector/clients/ioredis.d.ts +13 -0
- package/build/services/connector/clients/ioredis.js +50 -0
- package/build/services/connector/clients/redis.d.ts +13 -0
- package/build/services/connector/clients/redis.js +62 -0
- package/build/services/connector/index.d.ts +5 -0
- package/build/services/connector/index.js +31 -0
- package/build/services/dimension/index.d.ts +29 -0
- package/build/services/dimension/index.js +35 -0
- package/build/services/durable/asyncLocalStorage.d.ts +3 -0
- package/build/services/durable/asyncLocalStorage.js +5 -0
- package/build/services/durable/client.d.ts +15 -0
- package/build/services/durable/client.js +108 -0
- package/build/services/durable/connection.d.ts +4 -0
- package/build/services/durable/connection.js +51 -0
- package/build/services/durable/factory.d.ts +3 -0
- package/build/services/durable/factory.js +123 -0
- package/build/services/durable/handle.d.ts +8 -0
- package/build/services/durable/handle.js +38 -0
- package/build/services/durable/index.d.ts +57 -0
- package/build/services/durable/index.js +58 -0
- package/build/services/durable/native.d.ts +4 -0
- package/build/services/durable/native.js +47 -0
- package/build/services/durable/worker.d.ts +36 -0
- package/build/services/durable/worker.js +266 -0
- package/build/services/durable/workflow.d.ts +6 -0
- package/build/services/durable/workflow.js +135 -0
- package/build/services/engine/index.d.ts +82 -0
- package/build/services/engine/index.js +511 -0
- package/build/services/hotmesh/index.d.ts +45 -0
- package/build/services/hotmesh/index.js +134 -0
- package/build/services/logger/index.d.ts +17 -0
- package/build/services/logger/index.js +73 -0
- package/build/services/mapper/index.d.ts +24 -0
- package/build/services/mapper/index.js +72 -0
- package/build/services/pipe/functions/array.d.ts +24 -0
- package/build/services/pipe/functions/array.js +69 -0
- package/build/services/pipe/functions/bitwise.d.ts +9 -0
- package/build/services/pipe/functions/bitwise.js +24 -0
- package/build/services/pipe/functions/conditional.d.ts +10 -0
- package/build/services/pipe/functions/conditional.js +27 -0
- package/build/services/pipe/functions/date.d.ts +57 -0
- package/build/services/pipe/functions/date.js +167 -0
- package/build/services/pipe/functions/index.d.ts +25 -0
- package/build/services/pipe/functions/index.js +26 -0
- package/build/services/pipe/functions/json.d.ts +5 -0
- package/build/services/pipe/functions/json.js +12 -0
- package/build/services/pipe/functions/math.d.ts +38 -0
- package/build/services/pipe/functions/math.js +111 -0
- package/build/services/pipe/functions/number.d.ts +25 -0
- package/build/services/pipe/functions/number.js +133 -0
- package/build/services/pipe/functions/object.d.ts +22 -0
- package/build/services/pipe/functions/object.js +63 -0
- package/build/services/pipe/functions/string.d.ts +23 -0
- package/build/services/pipe/functions/string.js +69 -0
- package/build/services/pipe/functions/symbol.d.ts +12 -0
- package/build/services/pipe/functions/symbol.js +33 -0
- package/build/services/pipe/functions/unary.d.ts +7 -0
- package/build/services/pipe/functions/unary.js +18 -0
- package/build/services/pipe/index.d.ts +30 -0
- package/build/services/pipe/index.js +128 -0
- package/build/services/quorum/index.d.ts +34 -0
- package/build/services/quorum/index.js +147 -0
- package/build/services/reporter/index.d.ts +47 -0
- package/build/services/reporter/index.js +330 -0
- package/build/services/serializer/index.d.ts +36 -0
- package/build/services/serializer/index.js +222 -0
- package/build/services/signaler/store.d.ts +15 -0
- package/build/services/signaler/store.js +53 -0
- package/build/services/signaler/stream.d.ts +43 -0
- package/build/services/signaler/stream.js +317 -0
- package/build/services/store/cache.d.ts +66 -0
- package/build/services/store/cache.js +127 -0
- package/build/services/store/clients/ioredis.d.ts +27 -0
- package/build/services/store/clients/ioredis.js +96 -0
- package/build/services/store/clients/redis.d.ts +29 -0
- package/build/services/store/clients/redis.js +143 -0
- package/build/services/store/index.d.ts +88 -0
- package/build/services/store/index.js +657 -0
- package/build/services/stream/clients/ioredis.d.ts +23 -0
- package/build/services/stream/clients/ioredis.js +115 -0
- package/build/services/stream/clients/redis.d.ts +23 -0
- package/build/services/stream/clients/redis.js +119 -0
- package/build/services/stream/index.d.ts +21 -0
- package/build/services/stream/index.js +9 -0
- package/build/services/sub/clients/ioredis.d.ts +20 -0
- package/build/services/sub/clients/ioredis.js +72 -0
- package/build/services/sub/clients/redis.d.ts +20 -0
- package/build/services/sub/clients/redis.js +63 -0
- package/build/services/sub/index.d.ts +18 -0
- package/build/services/sub/index.js +9 -0
- package/build/services/task/index.d.ts +18 -0
- package/build/services/task/index.js +73 -0
- package/build/services/telemetry/index.d.ts +49 -0
- package/build/services/telemetry/index.js +223 -0
- package/build/services/worker/index.d.ts +30 -0
- package/build/services/worker/index.js +105 -0
- package/build/types/activity.d.ts +86 -0
- package/build/types/activity.js +2 -0
- package/build/types/app.d.ts +16 -0
- package/build/types/app.js +2 -0
- package/build/types/async.d.ts +5 -0
- package/build/types/async.js +2 -0
- package/build/types/cache.d.ts +1 -0
- package/build/types/cache.js +2 -0
- package/build/types/collator.d.ts +8 -0
- package/build/types/collator.js +11 -0
- package/build/types/durable.d.ts +59 -0
- package/build/types/durable.js +2 -0
- package/build/types/hook.d.ts +31 -0
- package/build/types/hook.js +9 -0
- package/build/types/hotmesh.d.ts +82 -0
- package/build/types/hotmesh.js +2 -0
- package/build/types/index.d.ts +20 -0
- package/build/types/index.js +21 -0
- package/build/types/ioredisclient.d.ts +5 -0
- package/build/types/ioredisclient.js +5 -0
- package/build/types/job.d.ts +50 -0
- package/build/types/job.js +2 -0
- package/build/types/logger.d.ts +6 -0
- package/build/types/logger.js +2 -0
- package/build/types/map.d.ts +4 -0
- package/build/types/map.js +2 -0
- package/build/types/pipe.d.ts +4 -0
- package/build/types/pipe.js +2 -0
- package/build/types/quorum.d.ts +46 -0
- package/build/types/quorum.js +2 -0
- package/build/types/redis.d.ts +8 -0
- package/build/types/redis.js +2 -0
- package/build/types/redisclient.d.ts +25 -0
- package/build/types/redisclient.js +2 -0
- package/build/types/serializer.d.ts +33 -0
- package/build/types/serializer.js +2 -0
- package/build/types/stats.d.ts +83 -0
- package/build/types/stats.js +2 -0
- package/build/types/stream.d.ts +67 -0
- package/build/types/stream.js +25 -0
- package/build/types/telemetry.d.ts +1 -0
- package/build/types/telemetry.js +11 -0
- package/build/types/transition.d.ts +17 -0
- package/build/types/transition.js +2 -0
- package/index.ts +5 -0
- package/modules/errors.ts +55 -0
- package/modules/key.ts +129 -0
- package/modules/utils.ts +170 -0
- package/package.json +73 -0
- package/services/activities/activity.ts +473 -0
- package/services/activities/await.ts +172 -0
- package/services/activities/emit.ts +25 -0
- package/services/activities/index.ts +15 -0
- package/services/activities/iterate.ts +26 -0
- package/services/activities/trigger.ts +196 -0
- package/services/activities/worker.ts +190 -0
- package/services/collator/README.md +102 -0
- package/services/collator/index.ts +182 -0
- package/services/compiler/deployer.ts +432 -0
- package/services/compiler/index.ts +98 -0
- package/services/compiler/validator.ts +154 -0
- package/services/connector/clients/ioredis.ts +57 -0
- package/services/connector/clients/redis.ts +72 -0
- package/services/connector/index.ts +44 -0
- package/services/dimension/README.md +73 -0
- package/services/dimension/index.ts +39 -0
- package/services/durable/asyncLocalStorage.ts +3 -0
- package/services/durable/client.ts +116 -0
- package/services/durable/connection.ts +50 -0
- package/services/durable/factory.ts +124 -0
- package/services/durable/handle.ts +43 -0
- package/services/durable/index.ts +60 -0
- package/services/durable/native.ts +46 -0
- package/services/durable/worker.ts +254 -0
- package/services/durable/workflow.ts +136 -0
- package/services/engine/index.ts +615 -0
- package/services/hotmesh/index.ts +182 -0
- package/services/logger/index.ts +79 -0
- package/services/mapper/index.ts +84 -0
- package/services/pipe/functions/array.ts +87 -0
- package/services/pipe/functions/bitwise.ts +27 -0
- package/services/pipe/functions/conditional.ts +31 -0
- package/services/pipe/functions/date.ts +214 -0
- package/services/pipe/functions/index.ts +25 -0
- package/services/pipe/functions/json.ts +11 -0
- package/services/pipe/functions/math.ts +143 -0
- package/services/pipe/functions/number.ts +150 -0
- package/services/pipe/functions/object.ts +79 -0
- package/services/pipe/functions/string.ts +86 -0
- package/services/pipe/functions/symbol.ts +39 -0
- package/services/pipe/functions/unary.ts +19 -0
- package/services/pipe/index.ts +138 -0
- package/services/quorum/index.ts +200 -0
- package/services/reporter/index.ts +379 -0
- package/services/serializer/README.md +10 -0
- package/services/serializer/index.ts +243 -0
- package/services/signaler/store.ts +61 -0
- package/services/signaler/stream.ts +354 -0
- package/services/store/cache.ts +172 -0
- package/services/store/clients/ioredis.ts +123 -0
- package/services/store/clients/redis.ts +169 -0
- package/services/store/index.ts +757 -0
- package/services/stream/clients/ioredis.ts +148 -0
- package/services/stream/clients/redis.ts +144 -0
- package/services/stream/index.ts +57 -0
- package/services/sub/clients/ioredis.ts +83 -0
- package/services/sub/clients/redis.ts +74 -0
- package/services/sub/index.ts +25 -0
- package/services/task/index.ts +86 -0
- package/services/telemetry/index.ts +267 -0
- package/services/worker/index.ts +165 -0
- package/types/activity.ts +115 -0
- package/types/app.ts +20 -0
- package/types/async.ts +7 -0
- package/types/cache.ts +1 -0
- package/types/collator.ts +9 -0
- package/types/durable.ts +81 -0
- package/types/hook.ts +32 -0
- package/types/hotmesh.ts +102 -0
- package/types/index.ts +138 -0
- package/types/ioredisclient.ts +10 -0
- package/types/job.ts +59 -0
- package/types/logger.ts +6 -0
- package/types/map.ts +5 -0
- package/types/ms.d.ts +7 -0
- package/types/pipe.ts +7 -0
- package/types/quorum.ts +59 -0
- package/types/redis.ts +27 -0
- package/types/redisclient.ts +29 -0
- package/types/serializer.ts +38 -0
- package/types/stats.ts +100 -0
- package/types/stream.ts +75 -0
- package/types/telemetry.ts +15 -0
- package/types/transition.ts +20 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Trigger = void 0;
|
|
4
|
+
const nanoid_1 = require("nanoid");
|
|
5
|
+
const errors_1 = require("../../modules/errors");
|
|
6
|
+
const utils_1 = require("../../modules/utils");
|
|
7
|
+
const activity_1 = require("./activity");
|
|
8
|
+
const collator_1 = require("../collator");
|
|
9
|
+
const dimension_1 = require("../dimension");
|
|
10
|
+
const pipe_1 = require("../pipe");
|
|
11
|
+
const reporter_1 = require("../reporter");
|
|
12
|
+
const serializer_1 = require("../serializer");
|
|
13
|
+
const telemetry_1 = require("../telemetry");
|
|
14
|
+
class Trigger extends activity_1.Activity {
|
|
15
|
+
constructor(config, data, metadata, hook, engine, context) {
|
|
16
|
+
super(config, data, metadata, hook, engine, context);
|
|
17
|
+
}
|
|
18
|
+
async process() {
|
|
19
|
+
this.logger.debug('trigger-process', { subscribes: this.config.subscribes });
|
|
20
|
+
let telemetry;
|
|
21
|
+
try {
|
|
22
|
+
this.setLeg(2);
|
|
23
|
+
await this.getState();
|
|
24
|
+
telemetry = new telemetry_1.TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
|
|
25
|
+
telemetry.startJobSpan();
|
|
26
|
+
telemetry.startActivitySpan(this.leg);
|
|
27
|
+
this.mapJobData();
|
|
28
|
+
await this.setStateNX();
|
|
29
|
+
this.adjacencyList = await this.filterAdjacent();
|
|
30
|
+
await this.setStatus(this.adjacencyList.length);
|
|
31
|
+
const multi = this.store.getMulti();
|
|
32
|
+
await this.setState(multi);
|
|
33
|
+
await this.setStats(multi);
|
|
34
|
+
await multi.exec();
|
|
35
|
+
telemetry.mapActivityAttributes();
|
|
36
|
+
const jobStatus = Number(this.context.metadata.js);
|
|
37
|
+
telemetry.setJobAttributes({ 'app.job.jss': jobStatus });
|
|
38
|
+
const attrs = { 'app.job.jss': jobStatus };
|
|
39
|
+
const messageIds = await this.transition(this.adjacencyList, jobStatus);
|
|
40
|
+
if (messageIds.length) {
|
|
41
|
+
attrs['app.activity.mids'] = messageIds.join(',');
|
|
42
|
+
}
|
|
43
|
+
telemetry.setActivityAttributes(attrs);
|
|
44
|
+
return this.context.metadata.jid;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error instanceof errors_1.DuplicateJobError) {
|
|
48
|
+
this.logger.error('duplicate-job-error', error);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.logger.error('trigger-process-error', error);
|
|
52
|
+
}
|
|
53
|
+
telemetry.setActivityError(error.message);
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
telemetry.endJobSpan();
|
|
58
|
+
telemetry.endActivitySpan();
|
|
59
|
+
this.logger.debug('trigger-process-end', { subscribes: this.config.subscribes, jid: this.context.metadata.jid });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async setStatus(amount) {
|
|
63
|
+
this.context.metadata.js = amount;
|
|
64
|
+
}
|
|
65
|
+
createInputContext() {
|
|
66
|
+
const input = {
|
|
67
|
+
[this.metadata.aid]: {
|
|
68
|
+
input: { data: this.data }
|
|
69
|
+
},
|
|
70
|
+
'$self': {
|
|
71
|
+
input: { data: this.data },
|
|
72
|
+
output: { data: this.data }
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
return input;
|
|
76
|
+
}
|
|
77
|
+
async getState() {
|
|
78
|
+
const inputContext = this.createInputContext();
|
|
79
|
+
const jobId = this.resolveJobId(inputContext);
|
|
80
|
+
const jobKey = this.resolveJobKey(inputContext);
|
|
81
|
+
const utc = (0, utils_1.formatISODate)(new Date());
|
|
82
|
+
const { id, version } = await this.engine.getVID();
|
|
83
|
+
this.initDimensionalAddress(dimension_1.DimensionService.getSeed());
|
|
84
|
+
const activityMetadata = {
|
|
85
|
+
...this.metadata,
|
|
86
|
+
jid: jobId,
|
|
87
|
+
key: jobKey,
|
|
88
|
+
as: collator_1.CollatorService.getTriggerSeed(),
|
|
89
|
+
};
|
|
90
|
+
this.context = {
|
|
91
|
+
metadata: {
|
|
92
|
+
...this.metadata,
|
|
93
|
+
ngn: this.context.metadata.ngn,
|
|
94
|
+
pj: this.context.metadata.pj,
|
|
95
|
+
pd: this.context.metadata.pd,
|
|
96
|
+
pa: this.context.metadata.pa,
|
|
97
|
+
app: id,
|
|
98
|
+
vrs: version,
|
|
99
|
+
tpc: this.config.subscribes,
|
|
100
|
+
trc: this.context.metadata.trc,
|
|
101
|
+
spn: this.context.metadata.spn,
|
|
102
|
+
jid: jobId,
|
|
103
|
+
dad: dimension_1.DimensionService.getSeed(),
|
|
104
|
+
key: jobKey,
|
|
105
|
+
jc: utc,
|
|
106
|
+
ju: utc,
|
|
107
|
+
ts: (0, utils_1.getTimeSeries)(this.resolveGranularity()),
|
|
108
|
+
js: 0,
|
|
109
|
+
},
|
|
110
|
+
data: {},
|
|
111
|
+
[this.metadata.aid]: {
|
|
112
|
+
input: {
|
|
113
|
+
data: this.data,
|
|
114
|
+
metadata: activityMetadata
|
|
115
|
+
},
|
|
116
|
+
output: {
|
|
117
|
+
data: this.data,
|
|
118
|
+
metadata: activityMetadata
|
|
119
|
+
},
|
|
120
|
+
settings: { data: {} },
|
|
121
|
+
errors: { data: {} },
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
this.context['$self'] = this.context[this.metadata.aid];
|
|
125
|
+
}
|
|
126
|
+
bindJobMetadataPaths() {
|
|
127
|
+
return serializer_1.MDATA_SYMBOLS.JOB.KEYS.map((key) => `metadata/${key}`);
|
|
128
|
+
}
|
|
129
|
+
bindActivityMetadataPaths() {
|
|
130
|
+
return serializer_1.MDATA_SYMBOLS.ACTIVITY.KEYS.map((key) => `output/metadata/${key}`);
|
|
131
|
+
}
|
|
132
|
+
resolveGranularity() {
|
|
133
|
+
return reporter_1.ReporterService.DEFAULT_GRANULARITY;
|
|
134
|
+
}
|
|
135
|
+
getJobStatus() {
|
|
136
|
+
return this.context.metadata.js;
|
|
137
|
+
}
|
|
138
|
+
resolveJobId(context) {
|
|
139
|
+
const jobId = this.config.stats?.id;
|
|
140
|
+
return jobId ? pipe_1.Pipe.resolve(jobId, context) : (0, nanoid_1.nanoid)();
|
|
141
|
+
}
|
|
142
|
+
resolveJobKey(context) {
|
|
143
|
+
const jobKey = this.config.stats?.key;
|
|
144
|
+
return jobKey ? pipe_1.Pipe.resolve(jobKey, context) : '';
|
|
145
|
+
}
|
|
146
|
+
async setStateNX() {
|
|
147
|
+
const jobId = this.context.metadata.jid;
|
|
148
|
+
if (!await this.store.setStateNX(jobId, this.engine.appId)) {
|
|
149
|
+
throw new errors_1.DuplicateJobError(jobId);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async setStats(multi) {
|
|
153
|
+
const md = this.context.metadata;
|
|
154
|
+
if (this.config.stats?.measures) {
|
|
155
|
+
const config = await this.engine.getVID();
|
|
156
|
+
const reporter = new reporter_1.ReporterService(config, this.store, this.logger);
|
|
157
|
+
await this.store.setStats(md.key, md.jid, md.ts, reporter.resolveTriggerStatistics(this.config, this.context), config, multi);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.Trigger = Trigger;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Activity } from './activity';
|
|
2
|
+
import { EngineService } from '../engine';
|
|
3
|
+
import { ActivityData, ActivityMetadata, ActivityType, WorkerActivity } from '../../types/activity';
|
|
4
|
+
import { JobState } from '../../types/job';
|
|
5
|
+
import { StreamCode, StreamStatus } from '../../types/stream';
|
|
6
|
+
import { TelemetryService } from '../telemetry';
|
|
7
|
+
declare class Worker extends Activity {
|
|
8
|
+
config: WorkerActivity;
|
|
9
|
+
constructor(config: ActivityType, data: ActivityData, metadata: ActivityMetadata, hook: ActivityData | null, engine: EngineService, context?: JobState);
|
|
10
|
+
process(): Promise<string>;
|
|
11
|
+
execActivity(): Promise<string>;
|
|
12
|
+
processEvent(status?: StreamStatus, code?: StreamCode): Promise<void>;
|
|
13
|
+
processPending(telemetry: TelemetryService): Promise<void>;
|
|
14
|
+
processSuccess(telemetry: TelemetryService): Promise<void>;
|
|
15
|
+
processError(telemetry: TelemetryService): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export { Worker };
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Worker = void 0;
|
|
4
|
+
const errors_1 = require("../../modules/errors");
|
|
5
|
+
const activity_1 = require("./activity");
|
|
6
|
+
const collator_1 = require("../collator");
|
|
7
|
+
const stream_1 = require("../../types/stream");
|
|
8
|
+
const telemetry_1 = require("../telemetry");
|
|
9
|
+
class Worker extends activity_1.Activity {
|
|
10
|
+
constructor(config, data, metadata, hook, engine, context) {
|
|
11
|
+
super(config, data, metadata, hook, engine, context);
|
|
12
|
+
}
|
|
13
|
+
//******** INITIAL ENTRY POINT (A) ********//
|
|
14
|
+
async process() {
|
|
15
|
+
this.logger.debug('worker-process', { jid: this.context.metadata.jid, aid: this.metadata.aid });
|
|
16
|
+
let telemetry;
|
|
17
|
+
try {
|
|
18
|
+
this.setLeg(1);
|
|
19
|
+
await collator_1.CollatorService.notarizeEntry(this);
|
|
20
|
+
await this.getState();
|
|
21
|
+
telemetry = new telemetry_1.TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
|
|
22
|
+
telemetry.startActivitySpan(this.leg);
|
|
23
|
+
this.mapInputData();
|
|
24
|
+
const multi = this.store.getMulti();
|
|
25
|
+
//await this.registerTimeout();
|
|
26
|
+
await collator_1.CollatorService.authorizeReentry(this, multi);
|
|
27
|
+
await this.setState(multi);
|
|
28
|
+
await this.setStatus(0, multi);
|
|
29
|
+
const multiResponse = await multi.exec();
|
|
30
|
+
telemetry.mapActivityAttributes();
|
|
31
|
+
const jobStatus = this.resolveStatus(multiResponse);
|
|
32
|
+
const messageId = await this.execActivity();
|
|
33
|
+
telemetry.setActivityAttributes({
|
|
34
|
+
'app.activity.mid': messageId,
|
|
35
|
+
'app.job.jss': jobStatus
|
|
36
|
+
});
|
|
37
|
+
//TODO: UPDATE ACTIVITY STATE (LEG 1 EXIT)
|
|
38
|
+
return this.context.metadata.aid;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (error instanceof errors_1.GetStateError) {
|
|
42
|
+
this.logger.error('worker-get-state-error', error);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
this.logger.error('worker-process-error', error);
|
|
46
|
+
}
|
|
47
|
+
telemetry.setActivityError(error.message);
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
telemetry.endActivitySpan();
|
|
52
|
+
this.logger.debug('worker-process-end', { jid: this.context.metadata.jid, aid: this.metadata.aid });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async execActivity() {
|
|
56
|
+
const streamData = {
|
|
57
|
+
metadata: {
|
|
58
|
+
jid: this.context.metadata.jid,
|
|
59
|
+
dad: this.metadata.dad,
|
|
60
|
+
aid: this.metadata.aid,
|
|
61
|
+
topic: this.config.subtype,
|
|
62
|
+
spn: this.context['$self'].output.metadata.l1s,
|
|
63
|
+
trc: this.context.metadata.trc,
|
|
64
|
+
},
|
|
65
|
+
data: this.context.data
|
|
66
|
+
};
|
|
67
|
+
if (this.config.retry) {
|
|
68
|
+
streamData.policies = {
|
|
69
|
+
retry: this.config.retry
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return (await this.engine.streamSignaler?.publishMessage(this.config.subtype, streamData));
|
|
73
|
+
}
|
|
74
|
+
//******** SIGNAL RE-ENTRY POINT (DUPLEX LEG 2 of 2) ********//
|
|
75
|
+
async processEvent(status = stream_1.StreamStatus.SUCCESS, code = 200) {
|
|
76
|
+
this.setLeg(2);
|
|
77
|
+
const jid = this.context.metadata.jid;
|
|
78
|
+
const aid = this.metadata.aid;
|
|
79
|
+
this.status = status;
|
|
80
|
+
this.code = code;
|
|
81
|
+
this.logger.debug('worker-process-event', { topic: this.config.subtype, jid, aid, status, code });
|
|
82
|
+
let telemetry;
|
|
83
|
+
try {
|
|
84
|
+
await this.getState();
|
|
85
|
+
const aState = await collator_1.CollatorService.notarizeReentry(this);
|
|
86
|
+
this.adjacentIndex = collator_1.CollatorService.getDimensionalIndex(aState);
|
|
87
|
+
telemetry = new telemetry_1.TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
|
|
88
|
+
let isComplete = collator_1.CollatorService.isActivityComplete(this.context.metadata.js);
|
|
89
|
+
if (isComplete) {
|
|
90
|
+
this.logger.warn('worker-process-event-duplicate', { jid, aid });
|
|
91
|
+
this.logger.debug('worker-process-event-duplicate-resolution', { resolution: 'Increase HotMesh config `reclaimDelay` timeout.' });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
telemetry.startActivitySpan(this.leg);
|
|
95
|
+
if (status === stream_1.StreamStatus.PENDING) {
|
|
96
|
+
await this.processPending(telemetry);
|
|
97
|
+
}
|
|
98
|
+
else if (status === stream_1.StreamStatus.SUCCESS) {
|
|
99
|
+
await this.processSuccess(telemetry);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
await this.processError(telemetry);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
this.logger.error('worker-process-event-error', error);
|
|
107
|
+
telemetry.setActivityError(error.message);
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
telemetry.endActivitySpan();
|
|
112
|
+
this.logger.debug('worker-process-event-end', { jid, aid });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async processPending(telemetry) {
|
|
116
|
+
this.bindActivityData('output');
|
|
117
|
+
this.adjacencyList = await this.filterAdjacent();
|
|
118
|
+
this.mapJobData();
|
|
119
|
+
const multi = this.store.getMulti();
|
|
120
|
+
await this.setState(multi);
|
|
121
|
+
await collator_1.CollatorService.notarizeContinuation(this, multi);
|
|
122
|
+
await this.setStatus(0, multi);
|
|
123
|
+
const multiResponse = await multi.exec();
|
|
124
|
+
telemetry.mapActivityAttributes();
|
|
125
|
+
const jobStatus = this.resolveStatus(multiResponse);
|
|
126
|
+
telemetry.setActivityAttributes({ 'app.job.jss': jobStatus });
|
|
127
|
+
}
|
|
128
|
+
async processSuccess(telemetry) {
|
|
129
|
+
this.bindActivityData('output');
|
|
130
|
+
this.adjacencyList = await this.filterAdjacent();
|
|
131
|
+
this.mapJobData();
|
|
132
|
+
const multi = this.store.getMulti();
|
|
133
|
+
await this.setState(multi);
|
|
134
|
+
await collator_1.CollatorService.notarizeCompletion(this, multi);
|
|
135
|
+
await this.setStatus(this.adjacencyList.length - 1, multi);
|
|
136
|
+
const multiResponse = await multi.exec();
|
|
137
|
+
telemetry.mapActivityAttributes();
|
|
138
|
+
const jobStatus = this.resolveStatus(multiResponse);
|
|
139
|
+
const attrs = { 'app.job.jss': jobStatus };
|
|
140
|
+
const messageIds = await this.transition(this.adjacencyList, jobStatus);
|
|
141
|
+
if (messageIds.length) {
|
|
142
|
+
attrs['app.activity.mids'] = messageIds.join(',');
|
|
143
|
+
}
|
|
144
|
+
telemetry.setActivityAttributes(attrs);
|
|
145
|
+
}
|
|
146
|
+
async processError(telemetry) {
|
|
147
|
+
this.bindActivityError(this.data);
|
|
148
|
+
this.adjacencyList = await this.filterAdjacent();
|
|
149
|
+
const multi = this.store.getMulti();
|
|
150
|
+
await this.setState(multi);
|
|
151
|
+
await collator_1.CollatorService.notarizeCompletion(this, multi);
|
|
152
|
+
await this.setStatus(this.adjacencyList.length - 1, multi);
|
|
153
|
+
const multiResponse = await multi.exec();
|
|
154
|
+
telemetry.mapActivityAttributes();
|
|
155
|
+
const jobStatus = this.resolveStatus(multiResponse);
|
|
156
|
+
const attrs = { 'app.job.jss': jobStatus };
|
|
157
|
+
const messageIds = await this.transition(this.adjacencyList, jobStatus);
|
|
158
|
+
if (messageIds.length) {
|
|
159
|
+
attrs['app.activity.mids'] = messageIds.join(',');
|
|
160
|
+
}
|
|
161
|
+
telemetry.setActivityAttributes(attrs);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.Worker = Worker;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { RedisMulti } from "../../types/redis";
|
|
2
|
+
import { CollationStage } from "../../types/collator";
|
|
3
|
+
import { ActivityDuplex } from "../../types/activity";
|
|
4
|
+
import { HotMeshGraph } from "../../types/hotmesh";
|
|
5
|
+
import { Activity } from "../activities/activity";
|
|
6
|
+
declare class CollatorService {
|
|
7
|
+
static targetLength: number;
|
|
8
|
+
static notarizeEntry(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
9
|
+
static authorizeReentry(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
10
|
+
static notarizeEarlyCompletion(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
11
|
+
static notarizeReentry(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
12
|
+
static notarizeContinuation(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
13
|
+
static notarizeCompletion(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
14
|
+
static getDigitAtIndex(num: number, targetDigitIndex: number): number | null;
|
|
15
|
+
static getDimensionalIndex(num: number): number | null;
|
|
16
|
+
static isDuplicate(num: number, targetDigitIndex: number): boolean;
|
|
17
|
+
static isInactive(num: number): boolean;
|
|
18
|
+
static isPrimed(amount: number, leg: ActivityDuplex): boolean;
|
|
19
|
+
static verifyInteger(amount: number, leg: ActivityDuplex, stage: CollationStage): void;
|
|
20
|
+
/**
|
|
21
|
+
* All non-trigger activities are assigned a status seed by their parent
|
|
22
|
+
*/
|
|
23
|
+
static getSeed(): string;
|
|
24
|
+
/**
|
|
25
|
+
* All trigger activities are assigned a status seed in a completed state
|
|
26
|
+
*/
|
|
27
|
+
static getTriggerSeed(): string;
|
|
28
|
+
/**
|
|
29
|
+
* entry point for compiler-type activities. This is called by the compiler
|
|
30
|
+
* to bind the sorted activity IDs to the trigger activity. These are then used
|
|
31
|
+
* at runtime by the activities to track job/activity status.
|
|
32
|
+
* @param graphs
|
|
33
|
+
*/
|
|
34
|
+
static compile(graphs: HotMeshGraph[]): void;
|
|
35
|
+
/**
|
|
36
|
+
* binds the ancestor array to each activity.
|
|
37
|
+
* Used in conjunction with the dimensional
|
|
38
|
+
* address (dad). If dad is `,0,1,0,0` and the
|
|
39
|
+
* ancestor array is `['t1', 'a1', 'a2']` for
|
|
40
|
+
* activity 'a3', then the SAVED DAD
|
|
41
|
+
* will always have the trailing
|
|
42
|
+
* 0's removed. This ensures that the addressing
|
|
43
|
+
* remains consistent even if the graph changes.
|
|
44
|
+
* id DAD SAVED DAD
|
|
45
|
+
* * t1 => ,0 => [empty]
|
|
46
|
+
* * a1 => ,0,1 => ,0,1
|
|
47
|
+
* * a2 => ,0,1,0 => ,0,1
|
|
48
|
+
* * a3 => ,0,1,0,0 => ,0,1
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
static bindAncestorArray(graphs: HotMeshGraph[]): void;
|
|
52
|
+
static isActivityComplete(status: number): boolean;
|
|
53
|
+
}
|
|
54
|
+
export { CollatorService };
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CollatorService = void 0;
|
|
4
|
+
const errors_1 = require("../../modules/errors");
|
|
5
|
+
const collator_1 = require("../../types/collator");
|
|
6
|
+
class CollatorService {
|
|
7
|
+
static async notarizeEntry(activity, multi) {
|
|
8
|
+
//decrement by -100_000_000_000_000
|
|
9
|
+
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, -100000000000000, multi);
|
|
10
|
+
this.verifyInteger(amount, 1, 'enter');
|
|
11
|
+
return amount;
|
|
12
|
+
}
|
|
13
|
+
;
|
|
14
|
+
static async authorizeReentry(activity, multi) {
|
|
15
|
+
//set second digit to 8, allowing for re-entry
|
|
16
|
+
//decrement by -10_000_000_000_000
|
|
17
|
+
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, -10000000000000, multi);
|
|
18
|
+
//this.verifyInteger(amount, 1, 'exit');
|
|
19
|
+
return amount;
|
|
20
|
+
}
|
|
21
|
+
static async notarizeEarlyCompletion(activity, multi) {
|
|
22
|
+
//initialize both `possible` (1m) and `actualized` (1) zero dimension, while decrementing the 2nd and 3rd digits to deactivate the activity
|
|
23
|
+
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1000001 - 11000000000000, multi);
|
|
24
|
+
}
|
|
25
|
+
;
|
|
26
|
+
static async notarizeReentry(activity, multi) {
|
|
27
|
+
//increment by 1_000_000 (indicates re-entry and is used to drive the 'dimensional address' for adjacent activities (minus 1))
|
|
28
|
+
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1000000, multi);
|
|
29
|
+
this.verifyInteger(amount, 2, 'enter');
|
|
30
|
+
return amount;
|
|
31
|
+
}
|
|
32
|
+
;
|
|
33
|
+
static async notarizeContinuation(activity, multi) {
|
|
34
|
+
//keep open; actualize the leg2 dimension (+1)
|
|
35
|
+
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1, multi);
|
|
36
|
+
}
|
|
37
|
+
;
|
|
38
|
+
static async notarizeCompletion(activity, multi) {
|
|
39
|
+
//close out; actualize leg2 dimension (+1) and decrement the 3rd digit (-1_000_000_000_000)
|
|
40
|
+
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1 - 1000000000000, multi);
|
|
41
|
+
}
|
|
42
|
+
;
|
|
43
|
+
static getDigitAtIndex(num, targetDigitIndex) {
|
|
44
|
+
const numStr = num.toString();
|
|
45
|
+
if (targetDigitIndex < 0 || targetDigitIndex >= numStr.length) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const digit = parseInt(numStr[targetDigitIndex], 10);
|
|
49
|
+
return digit;
|
|
50
|
+
}
|
|
51
|
+
static getDimensionalIndex(num) {
|
|
52
|
+
const numStr = num.toString();
|
|
53
|
+
if (numStr.length < 9) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const extractedStr = numStr.substring(3, 9);
|
|
57
|
+
const extractedInt = parseInt(extractedStr, 10);
|
|
58
|
+
return extractedInt - 1;
|
|
59
|
+
}
|
|
60
|
+
static isDuplicate(num, targetDigitIndex) {
|
|
61
|
+
return this.getDigitAtIndex(num, targetDigitIndex) < 8;
|
|
62
|
+
}
|
|
63
|
+
static isInactive(num) {
|
|
64
|
+
return this.getDigitAtIndex(num, 2) < 9;
|
|
65
|
+
}
|
|
66
|
+
static isPrimed(amount, leg) {
|
|
67
|
+
//activity entry is not allowed if paths not properly pre-set
|
|
68
|
+
if (leg == 1) {
|
|
69
|
+
return amount != -100000000000000;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
return this.getDigitAtIndex(amount, 0) < 9 &&
|
|
73
|
+
this.getDigitAtIndex(amount, 1) < 9;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
static verifyInteger(amount, leg, stage) {
|
|
77
|
+
let faultType;
|
|
78
|
+
if (leg === 1 && stage === 'enter') {
|
|
79
|
+
if (!this.isPrimed(amount, 1)) {
|
|
80
|
+
faultType = collator_1.CollationFaultType.MISSING;
|
|
81
|
+
}
|
|
82
|
+
else if (this.isDuplicate(amount, 0)) {
|
|
83
|
+
faultType = collator_1.CollationFaultType.DUPLICATE;
|
|
84
|
+
}
|
|
85
|
+
else if (amount != 899000000000000) {
|
|
86
|
+
faultType = collator_1.CollationFaultType.INVALID;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else if (leg === 1 && stage === 'exit') {
|
|
90
|
+
if (amount === -10000000000000) {
|
|
91
|
+
faultType = collator_1.CollationFaultType.MISSING;
|
|
92
|
+
}
|
|
93
|
+
else if (this.isDuplicate(amount, 1)) {
|
|
94
|
+
faultType = collator_1.CollationFaultType.DUPLICATE;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (leg === 2 && stage === 'enter') {
|
|
98
|
+
if (!this.isPrimed(amount, 2)) {
|
|
99
|
+
faultType = collator_1.CollationFaultType.FORBIDDEN;
|
|
100
|
+
}
|
|
101
|
+
else if (this.isInactive(amount)) {
|
|
102
|
+
faultType = collator_1.CollationFaultType.INACTIVE;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (faultType) {
|
|
106
|
+
throw new errors_1.CollationError(amount, leg, stage, faultType);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* All non-trigger activities are assigned a status seed by their parent
|
|
111
|
+
*/
|
|
112
|
+
static getSeed() {
|
|
113
|
+
return '999000000000000';
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* All trigger activities are assigned a status seed in a completed state
|
|
117
|
+
*/
|
|
118
|
+
static getTriggerSeed() {
|
|
119
|
+
return '888000001000001';
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* entry point for compiler-type activities. This is called by the compiler
|
|
123
|
+
* to bind the sorted activity IDs to the trigger activity. These are then used
|
|
124
|
+
* at runtime by the activities to track job/activity status.
|
|
125
|
+
* @param graphs
|
|
126
|
+
*/
|
|
127
|
+
static compile(graphs) {
|
|
128
|
+
CollatorService.bindAncestorArray(graphs);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* binds the ancestor array to each activity.
|
|
132
|
+
* Used in conjunction with the dimensional
|
|
133
|
+
* address (dad). If dad is `,0,1,0,0` and the
|
|
134
|
+
* ancestor array is `['t1', 'a1', 'a2']` for
|
|
135
|
+
* activity 'a3', then the SAVED DAD
|
|
136
|
+
* will always have the trailing
|
|
137
|
+
* 0's removed. This ensures that the addressing
|
|
138
|
+
* remains consistent even if the graph changes.
|
|
139
|
+
* id DAD SAVED DAD
|
|
140
|
+
* * t1 => ,0 => [empty]
|
|
141
|
+
* * a1 => ,0,1 => ,0,1
|
|
142
|
+
* * a2 => ,0,1,0 => ,0,1
|
|
143
|
+
* * a3 => ,0,1,0,0 => ,0,1
|
|
144
|
+
*
|
|
145
|
+
*/
|
|
146
|
+
static bindAncestorArray(graphs) {
|
|
147
|
+
graphs.forEach((graph) => {
|
|
148
|
+
const ancestors = {};
|
|
149
|
+
const startingNode = Object.keys(graph.activities).find((activity) => graph.activities[activity].type === 'trigger');
|
|
150
|
+
if (!startingNode) {
|
|
151
|
+
throw new Error('collator-trigger-activity-not-found');
|
|
152
|
+
}
|
|
153
|
+
const dfs = (node, parentList) => {
|
|
154
|
+
ancestors[node] = parentList;
|
|
155
|
+
graph.activities[node]['ancestors'] = parentList;
|
|
156
|
+
const transitions = graph.transitions?.[node] || [];
|
|
157
|
+
transitions.forEach((transition) => {
|
|
158
|
+
dfs(transition.to, [...parentList, node]);
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
// Start the DFS traversal
|
|
162
|
+
dfs(startingNode, []);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
static isActivityComplete(status) {
|
|
166
|
+
return (status - 0) <= 0;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
exports.CollatorService = CollatorService;
|
|
170
|
+
//max int digit count that supports `hincrby`
|
|
171
|
+
CollatorService.targetLength = 15;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { StoreService } from '../store';
|
|
2
|
+
import { HotMeshGraph, HotMeshManifest } from "../../types/hotmesh";
|
|
3
|
+
import { RedisClient, RedisMulti } from "../../types/redis";
|
|
4
|
+
import { Symbols } from "../../types/serializer";
|
|
5
|
+
declare class Deployer {
|
|
6
|
+
manifest: HotMeshManifest | null;
|
|
7
|
+
store: StoreService<RedisClient, RedisMulti> | null;
|
|
8
|
+
constructor(manifest: HotMeshManifest);
|
|
9
|
+
deploy(store: StoreService<RedisClient, RedisMulti>): Promise<void>;
|
|
10
|
+
getVID(): {
|
|
11
|
+
id: string;
|
|
12
|
+
version: string;
|
|
13
|
+
};
|
|
14
|
+
generateSymKeys(): Promise<void>;
|
|
15
|
+
bindSymbols(startIndex: number, maxIndex: number, existingSymbols: Symbols, prefix: string, produces: string[]): Symbols;
|
|
16
|
+
copyJobSchemas(): void;
|
|
17
|
+
bindBackRefs(): void;
|
|
18
|
+
convertTopicsToTypes(): void;
|
|
19
|
+
bindParents(): Promise<void>;
|
|
20
|
+
collectValues(schema: Record<string, any>, values: Set<string>): void;
|
|
21
|
+
traverse(obj: any, values: Set<string>): void;
|
|
22
|
+
generateSymVals(): Promise<void>;
|
|
23
|
+
resolveJobMapsPaths(): void;
|
|
24
|
+
resolveMappingDependencies(): void;
|
|
25
|
+
groupMappingRules(rules: string[]): Record<string, string[]>;
|
|
26
|
+
resolveMappableValue(mappable: string): [string, string];
|
|
27
|
+
deployActivitySchemas(): Promise<void>;
|
|
28
|
+
deploySubscriptions(): Promise<void>;
|
|
29
|
+
findTrigger(graph: HotMeshGraph): [string, Record<string, any>] | null;
|
|
30
|
+
deployTransitions(): Promise<void>;
|
|
31
|
+
deployHookPatterns(): Promise<void>;
|
|
32
|
+
deployConsumerGroups(): Promise<void>;
|
|
33
|
+
deployConsumerGroup(stream: string, group: string): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export { Deployer };
|