@rdfc/js-runner 3.0.3 → 3.0.4-remote
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/.claude/settings.local.json +12 -0
- package/README.md +37 -1
- package/bin/runner.js +7 -1
- package/bin/server.js +13 -0
- package/examples/echo/.idea/echo.iml +9 -0
- package/examples/echo/.idea/misc.xml +6 -0
- package/examples/echo/.idea/modules.xml +8 -0
- package/examples/echo/.idea/vcs.xml +7 -0
- package/examples/echo/.swls/config.json +1 -0
- package/examples/echo/package-lock.json +27 -29
- package/examples/echo/pipeline.ttl +0 -1
- package/examples/echo/processors.ttl +1 -1
- package/examples/echo/remote_pipeline.ttl +18 -0
- package/examples/echo/server.ttl +5 -0
- package/examples/echo/untitled:/types/MyType.ttl +0 -0
- package/file:/home/silvius/Projects/mumo-pipeline/ldes/http_3A_2F_2Fdata.mumo.be_2Fstreams_2Fnodes_2Fdefault/root/index.trig +3 -0
- package/ldes/http_3A_2F_2Fdata.mumo.be_2Fstreams_2Fnodes_2Fdefault/root/index.trig +3 -0
- package/lib/client.d.ts +2 -1
- package/lib/client.js +70 -22
- package/lib/index.d.ts +2 -0
- package/lib/index.js +3 -1
- package/lib/jsonld.d.ts +17 -0
- package/lib/jsonld.js +135 -0
- package/lib/reader.d.ts +4 -1
- package/lib/reader.js +11 -3
- package/lib/runner.d.ts +6 -1
- package/lib/runner.js +43 -15
- package/lib/server.d.ts +9 -0
- package/lib/server.js +459 -0
- package/lib/state.d.ts +32 -0
- package/lib/state.js +71 -0
- package/lib/testUtils.d.ts +24 -0
- package/lib/testUtils.js +150 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/writer.d.ts +5 -1
- package/lib/writer.js +26 -10
- package/minimal.ttl +99 -0
- package/package.json +12 -11
- package/src/client.ts +99 -24
- package/src/index.ts +2 -0
- package/src/reader.ts +11 -1
- package/src/runner.ts +58 -11
- package/src/server.ts +545 -0
- package/src/state.ts +105 -0
- package/src/writer.ts +36 -12
package/lib/server.js
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { resolve, relative } from 'node:path';
|
|
4
|
+
import { Parser } from 'n3';
|
|
5
|
+
import { extractShapes } from 'rdf-lens';
|
|
6
|
+
import { start } from './client.js';
|
|
7
|
+
import { State } from './state.js';
|
|
8
|
+
const RDFC = 'https://w3id.org/rdf-connect#';
|
|
9
|
+
const OWL_IMPORTS = 'http://www.w3.org/2002/07/owl#imports';
|
|
10
|
+
const RDF_TYPE = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
|
|
11
|
+
const RDFS_LABEL = 'http://www.w3.org/2000/01/rdf-schema#label';
|
|
12
|
+
const RDFS_COMMENT = 'http://www.w3.org/2000/01/rdf-schema#comment';
|
|
13
|
+
const CONFIG_SHAPE_TTL = `
|
|
14
|
+
@prefix rdfc: <https://w3id.org/rdf-connect#>.
|
|
15
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
|
|
16
|
+
@prefix sh: <http://www.w3.org/ns/shacl#>.
|
|
17
|
+
|
|
18
|
+
[] a sh:NodeShape;
|
|
19
|
+
sh:targetClass rdfc:JsRunnerServer;
|
|
20
|
+
sh:property [
|
|
21
|
+
sh:path rdfc:port;
|
|
22
|
+
sh:name "port";
|
|
23
|
+
sh:maxCount 1;
|
|
24
|
+
sh:datatype xsd:integer;
|
|
25
|
+
], [
|
|
26
|
+
sh:path rdfc:processorConfig;
|
|
27
|
+
sh:name "processorConfigs";
|
|
28
|
+
sh:datatype xsd:string;
|
|
29
|
+
].
|
|
30
|
+
`;
|
|
31
|
+
const { lenses } = extractShapes(new Parser().parse(CONFIG_SHAPE_TTL));
|
|
32
|
+
function readBody(req) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
let body = '';
|
|
35
|
+
req.on('data', (chunk) => (body += chunk));
|
|
36
|
+
req.on('end', () => resolve(body));
|
|
37
|
+
req.on('error', reject);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
export async function parseServerConfig(configPath) {
|
|
41
|
+
const absConfig = resolve(configPath);
|
|
42
|
+
const content = await readFile(absConfig, { encoding: 'utf8' });
|
|
43
|
+
const quads = new Parser({ baseIRI: 'file://' + absConfig }).parse(content);
|
|
44
|
+
const serverSubject = quads.find((q) => q.predicate.value === RDF_TYPE &&
|
|
45
|
+
q.object.value === RDFC + 'JsRunnerServer')?.subject;
|
|
46
|
+
if (!serverSubject) {
|
|
47
|
+
throw new Error(`No rdfc:JsRunnerServer found in ${absConfig}`);
|
|
48
|
+
}
|
|
49
|
+
const config = lenses[RDFC + 'JsRunnerServer'].execute({
|
|
50
|
+
id: serverSubject,
|
|
51
|
+
quads,
|
|
52
|
+
});
|
|
53
|
+
const port = config.port ?? 3000;
|
|
54
|
+
const processorPaths = (config.processorConfigs ?? []).map((val) => val.startsWith('file://') ? val.slice('file://'.length) : val);
|
|
55
|
+
return { port, processorPaths };
|
|
56
|
+
}
|
|
57
|
+
export async function buildWhitelist(processorPaths) {
|
|
58
|
+
const whitelist = new Set();
|
|
59
|
+
const done = new Set();
|
|
60
|
+
const todo = [...processorPaths];
|
|
61
|
+
while (todo.length > 0) {
|
|
62
|
+
const filePath = todo.pop();
|
|
63
|
+
if (done.has(filePath))
|
|
64
|
+
continue;
|
|
65
|
+
done.add(filePath);
|
|
66
|
+
whitelist.add(filePath);
|
|
67
|
+
let content;
|
|
68
|
+
try {
|
|
69
|
+
content = await readFile(filePath, { encoding: 'utf8' });
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const baseIRI = 'file://' + filePath;
|
|
75
|
+
const quads = new Parser({ baseIRI }).parse(content);
|
|
76
|
+
for (const quad of quads) {
|
|
77
|
+
if (quad.subject.value === baseIRI &&
|
|
78
|
+
quad.predicate.value === OWL_IMPORTS) {
|
|
79
|
+
const importVal = quad.object.value;
|
|
80
|
+
if (importVal.startsWith('file://')) {
|
|
81
|
+
todo.push(importVal.slice('file://'.length));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return whitelist;
|
|
87
|
+
}
|
|
88
|
+
async function extractProcessorDescriptions(processorPaths) {
|
|
89
|
+
const descriptions = [];
|
|
90
|
+
for (const filePath of processorPaths) {
|
|
91
|
+
let content;
|
|
92
|
+
try {
|
|
93
|
+
content = await readFile(filePath, { encoding: 'utf8' });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const baseIRI = 'file://' + filePath;
|
|
99
|
+
const quads = new Parser({ baseIRI }).parse(content);
|
|
100
|
+
const seen = new Set();
|
|
101
|
+
for (const quad of quads) {
|
|
102
|
+
if (quad.predicate.value !== RDFC + 'jsImplementationOf')
|
|
103
|
+
continue;
|
|
104
|
+
const uri = quad.subject.value;
|
|
105
|
+
if (seen.has(uri))
|
|
106
|
+
continue;
|
|
107
|
+
seen.add(uri);
|
|
108
|
+
const labelQuad = quads.find((q) => q.subject.value === uri && q.predicate.value === RDFS_LABEL);
|
|
109
|
+
const commentQuad = quads.find((q) => q.subject.value === uri && q.predicate.value === RDFS_COMMENT);
|
|
110
|
+
descriptions.push({
|
|
111
|
+
uri,
|
|
112
|
+
label: labelQuad?.object.value,
|
|
113
|
+
comment: commentQuad?.object.value,
|
|
114
|
+
sourceFile: filePath,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return descriptions;
|
|
119
|
+
}
|
|
120
|
+
export async function generateIndexTtl(processorPaths, cwd) {
|
|
121
|
+
const descriptions = await extractProcessorDescriptions(processorPaths);
|
|
122
|
+
const lines = `
|
|
123
|
+
@prefix prov: <http://www.w3.org/ns/prov#>.
|
|
124
|
+
@prefix sds: <https://w3id.org/sds#>.
|
|
125
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
|
|
126
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#>.
|
|
127
|
+
@prefix rdfl: <https://w3id.org/rdf-lens/ontology#>.
|
|
128
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
|
|
129
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
|
|
130
|
+
@prefix sh: <http://www.w3.org/ns/shacl#>.
|
|
131
|
+
@prefix rdfc: <https://w3id.org/rdf-connect#>.
|
|
132
|
+
|
|
133
|
+
sds:Activity rdfs:subClassOf prov:Activity.
|
|
134
|
+
rdfc:Processor rdfs:subClassOf sds:Activity.
|
|
135
|
+
sds:implementationOf rdfs:subPropertyOf rdfs:subClassOf.
|
|
136
|
+
rdfc:jsImplementationOf rdfs:subPropertyOf sds:implementationOf.
|
|
137
|
+
[ ] a sh:NodeShape;
|
|
138
|
+
sh:targetSubjectsOf rdfc:jsImplementationOf;
|
|
139
|
+
sh:property [
|
|
140
|
+
sh:path rdfc:entrypoint;
|
|
141
|
+
sh:name "location";
|
|
142
|
+
sh:minCount 1;
|
|
143
|
+
sh:maxCount 1;
|
|
144
|
+
sh:datatype xsd:string;
|
|
145
|
+
], [
|
|
146
|
+
sh:path rdfc:file;
|
|
147
|
+
sh:name "file";
|
|
148
|
+
sh:minCount 1;
|
|
149
|
+
sh:maxCount 1;
|
|
150
|
+
sh:datatype xsd:string;
|
|
151
|
+
], [
|
|
152
|
+
sh:path rdfc:class;
|
|
153
|
+
sh:name "clazz";
|
|
154
|
+
sh:maxCount 1;
|
|
155
|
+
sh:datatype xsd:string;
|
|
156
|
+
].
|
|
157
|
+
|
|
158
|
+
<runner> a rdfc:HttpRunner;
|
|
159
|
+
rdfc:handlesSubjectsOf rdfc:jsImplementationOf;
|
|
160
|
+
rdfc:endpoint <./connect>.
|
|
161
|
+
`.split('\n');
|
|
162
|
+
for (const desc of descriptions) {
|
|
163
|
+
const relPath = relative(cwd, desc.sourceFile);
|
|
164
|
+
lines.push(`<${desc.uri}> a rdfc:Processor;`);
|
|
165
|
+
if (desc.label) {
|
|
166
|
+
lines.push(` rdfs:label "${desc.label.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}";`);
|
|
167
|
+
}
|
|
168
|
+
if (desc.comment) {
|
|
169
|
+
lines.push(` rdfs:comment "${desc.comment.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}";`);
|
|
170
|
+
}
|
|
171
|
+
lines.push(` rdfs:isDefinedBy <${relPath}>.`);
|
|
172
|
+
lines.push('');
|
|
173
|
+
}
|
|
174
|
+
return lines.join('\n');
|
|
175
|
+
}
|
|
176
|
+
function dashboardHtml() {
|
|
177
|
+
return `<!DOCTYPE html>
|
|
178
|
+
<html lang="en">
|
|
179
|
+
<head>
|
|
180
|
+
<meta charset="UTF-8">
|
|
181
|
+
<title>js-runner dashboard</title>
|
|
182
|
+
<style>
|
|
183
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
184
|
+
body { font-family: 'Courier New', monospace; background: #111; color: #ccc; padding: 1.5rem; }
|
|
185
|
+
h1 { color: #7ec8e3; margin-bottom: 1rem; font-size: 1.4rem; }
|
|
186
|
+
#updated { font-size: 0.75rem; color: #555; margin-bottom: 1.5rem; }
|
|
187
|
+
.runner { border: 1px solid #333; border-radius: 6px; padding: 1rem; margin-bottom: 1.25rem; }
|
|
188
|
+
.runner-meta { display: flex; flex-wrap: wrap; gap: 1.5rem; margin-bottom: 0.75rem; font-size: 0.85rem; }
|
|
189
|
+
.runner-meta span { color: #888; }
|
|
190
|
+
.runner-meta strong { color: #aaa; }
|
|
191
|
+
.status { font-weight: bold; }
|
|
192
|
+
.s-connecting { color: #f0a500; }
|
|
193
|
+
.s-running { color: #4caf50; }
|
|
194
|
+
.s-done { color: #666; }
|
|
195
|
+
.s-error { color: #f44336; }
|
|
196
|
+
.g-IDLE { color: #888; }
|
|
197
|
+
.g-CONNECTING { color: #f0a500; }
|
|
198
|
+
.g-READY { color: #4caf50; }
|
|
199
|
+
.g-TRANSIENT_FAILURE { color: #f44336; }
|
|
200
|
+
.g-SHUTDOWN { color: #666; }
|
|
201
|
+
table { width: 100%; border-collapse: collapse; font-size: 0.8rem; }
|
|
202
|
+
th, td { border: 1px solid #2a2a2a; padding: 0.4rem 0.8rem; text-align: left; white-space: nowrap; }
|
|
203
|
+
th { background: #1a1a1a; color: #7ec8e3; }
|
|
204
|
+
td.uri { color: #aaa; max-width: 28rem; overflow: hidden; text-overflow: ellipsis; }
|
|
205
|
+
td.role-reader { color: #81c784; }
|
|
206
|
+
td.role-writer { color: #64b5f6; }
|
|
207
|
+
.empty { color: #555; font-size: 0.8rem; padding-top: 0.5rem; }
|
|
208
|
+
.no-runners { color: #555; }
|
|
209
|
+
</style>
|
|
210
|
+
</head>
|
|
211
|
+
<body>
|
|
212
|
+
<h1>js-runner dashboard</h1>
|
|
213
|
+
<div id="updated"></div>
|
|
214
|
+
<div id="content"><p class="no-runners">Loading…</p></div>
|
|
215
|
+
<script>
|
|
216
|
+
const throughputState = {}
|
|
217
|
+
|
|
218
|
+
function esc(s) {
|
|
219
|
+
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
|
|
220
|
+
}
|
|
221
|
+
function fmtBytes(b) {
|
|
222
|
+
if (b < 1024) return b + ' B'
|
|
223
|
+
if (b < 1048576) return (b/1024).toFixed(1) + ' KB'
|
|
224
|
+
return (b/1048576).toFixed(1) + ' MB'
|
|
225
|
+
}
|
|
226
|
+
function fmtAge(ms) {
|
|
227
|
+
if (!ms) return '-'
|
|
228
|
+
const s = Math.floor((Date.now() - ms) / 1000)
|
|
229
|
+
if (s < 5) return 'just now'
|
|
230
|
+
if (s < 60) return s + 's ago'
|
|
231
|
+
if (s < 3600) return Math.floor(s/60) + 'm ago'
|
|
232
|
+
return Math.floor(s/3600) + 'h ago'
|
|
233
|
+
}
|
|
234
|
+
function fmtDuration(ms) {
|
|
235
|
+
const s = Math.floor((Date.now() - ms) / 1000)
|
|
236
|
+
if (s < 60) return s + 's'
|
|
237
|
+
if (s < 3600) return Math.floor(s/60) + 'm ' + (s%60) + 's'
|
|
238
|
+
return Math.floor(s/3600) + 'h ' + Math.floor((s%3600)/60) + 'm'
|
|
239
|
+
}
|
|
240
|
+
function pct(arr, p) {
|
|
241
|
+
if (!arr || !arr.length) return '-'
|
|
242
|
+
const s = [...arr].sort((a,b) => a-b)
|
|
243
|
+
return s[Math.floor(p/100*s.length)] + 'ms'
|
|
244
|
+
}
|
|
245
|
+
function avg(arr) {
|
|
246
|
+
if (!arr || !arr.length) return '-'
|
|
247
|
+
return (arr.reduce((a,b)=>a+b,0)/arr.length).toFixed(1) + 'ms'
|
|
248
|
+
}
|
|
249
|
+
function throughput(key, count) {
|
|
250
|
+
const now = Date.now()
|
|
251
|
+
if (!throughputState[key]) { throughputState[key] = {count, time: now}; return '-' }
|
|
252
|
+
const dt = (now - throughputState[key].time) / 1000
|
|
253
|
+
const dc = count - throughputState[key].count
|
|
254
|
+
throughputState[key] = {count, time: now}
|
|
255
|
+
if (dt < 0.1) return '-'
|
|
256
|
+
return (dc / dt).toFixed(1) + '/s'
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function renderChannels(runnerId, channels) {
|
|
260
|
+
const entries = Object.values(channels)
|
|
261
|
+
if (!entries.length) return '<p class="empty">No channels yet.</p>'
|
|
262
|
+
return \`<table>
|
|
263
|
+
<thead>
|
|
264
|
+
<tr>
|
|
265
|
+
<th>Channel URI</th>
|
|
266
|
+
<th>Role</th>
|
|
267
|
+
<th>Messages</th>
|
|
268
|
+
<th>Throughput</th>
|
|
269
|
+
<th>Bytes</th>
|
|
270
|
+
<th>Last msg</th>
|
|
271
|
+
<th>Avg latency</th>
|
|
272
|
+
<th>p50</th>
|
|
273
|
+
<th>p99</th>
|
|
274
|
+
</tr>
|
|
275
|
+
</thead>
|
|
276
|
+
<tbody>
|
|
277
|
+
\${entries.map(ch => {
|
|
278
|
+
const key = runnerId + ':' + ch.uri
|
|
279
|
+
const tp = throughput(key, ch.messageCount)
|
|
280
|
+
return \`<tr>
|
|
281
|
+
<td class="uri" title="\${esc(ch.uri)}">\${esc(ch.uri)}</td>
|
|
282
|
+
<td class="role-\${ch.role}">\${ch.role}</td>
|
|
283
|
+
<td>\${ch.messageCount}</td>
|
|
284
|
+
<td>\${tp}</td>
|
|
285
|
+
<td>\${fmtBytes(ch.bytesTotal)}</td>
|
|
286
|
+
<td>\${fmtAge(ch.lastMessageAt)}</td>
|
|
287
|
+
<td>\${ch.role === 'writer' ? avg(ch.latenciesMs) : '-'}</td>
|
|
288
|
+
<td>\${ch.role === 'writer' ? pct(ch.latenciesMs, 50) : '-'}</td>
|
|
289
|
+
<td>\${ch.role === 'writer' ? pct(ch.latenciesMs, 99) : '-'}</td>
|
|
290
|
+
</tr>\`
|
|
291
|
+
}).join('')}
|
|
292
|
+
</tbody>
|
|
293
|
+
</table>\`
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function render(runners) {
|
|
297
|
+
const el = document.getElementById('content')
|
|
298
|
+
if (!runners.length) {
|
|
299
|
+
el.innerHTML = '<p class="no-runners">No runners registered yet.</p>'
|
|
300
|
+
return
|
|
301
|
+
}
|
|
302
|
+
el.innerHTML = runners.map(r => \`
|
|
303
|
+
<div class="runner">
|
|
304
|
+
<div class="runner-meta">
|
|
305
|
+
<span><strong>URI:</strong> \${esc(r.uri)}</span>
|
|
306
|
+
<span><strong>Host:</strong> \${esc(r.host)}</span>
|
|
307
|
+
<span><strong>Status:</strong> <span class="status s-\${r.status}">\${r.status}</span></span>
|
|
308
|
+
<span><strong>gRPC:</strong> <span class="g-\${r.grpcState}">\${r.grpcState}</span></span>
|
|
309
|
+
<span><strong>Uptime:</strong> \${fmtDuration(r.connectedAt)}</span>
|
|
310
|
+
</div>
|
|
311
|
+
\${renderChannels(r.id, r.channels)}
|
|
312
|
+
</div>
|
|
313
|
+
\`).join('')
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function refresh() {
|
|
317
|
+
try {
|
|
318
|
+
const res = await fetch('/api/state')
|
|
319
|
+
const data = await res.json()
|
|
320
|
+
render(data)
|
|
321
|
+
document.getElementById('updated').textContent =
|
|
322
|
+
'Last updated: ' + new Date().toLocaleTimeString()
|
|
323
|
+
} catch (e) {
|
|
324
|
+
document.getElementById('content').innerHTML =
|
|
325
|
+
'<p style="color:#f44336">Failed to fetch state: ' + esc(e.message) + '</p>'
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
refresh()
|
|
330
|
+
setInterval(refresh, 2000)
|
|
331
|
+
</script>
|
|
332
|
+
</body>
|
|
333
|
+
</html>`;
|
|
334
|
+
}
|
|
335
|
+
export async function serve(configPath) {
|
|
336
|
+
const absConfig = resolve(configPath);
|
|
337
|
+
const { port, processorPaths } = await parseServerConfig(absConfig);
|
|
338
|
+
const whitelist = await buildWhitelist(processorPaths);
|
|
339
|
+
const cwd = process.cwd();
|
|
340
|
+
const indexTtl = await generateIndexTtl(processorPaths, cwd);
|
|
341
|
+
const state = new State();
|
|
342
|
+
const activeConnections = new Set();
|
|
343
|
+
const shutdown = (signal) => {
|
|
344
|
+
console.log(`\nReceived ${signal}, closing ${activeConnections.size} active gRPC connection(s)...`);
|
|
345
|
+
for (const ctrl of activeConnections)
|
|
346
|
+
ctrl.abort();
|
|
347
|
+
server.close(() => process.exit(0));
|
|
348
|
+
};
|
|
349
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
350
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
351
|
+
const server = createServer(async (req, res) => {
|
|
352
|
+
const method = req.method ?? 'GET';
|
|
353
|
+
const url = req.url ?? '/';
|
|
354
|
+
if (method === 'GET' && url === '/health') {
|
|
355
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
356
|
+
res.end(JSON.stringify({
|
|
357
|
+
status: 'ok',
|
|
358
|
+
runners: state.snapshot().length,
|
|
359
|
+
activeConnections: activeConnections.size,
|
|
360
|
+
}));
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
if (method === 'GET' && url === '/api/state') {
|
|
364
|
+
res.writeHead(200, {
|
|
365
|
+
'Content-Type': 'application/json',
|
|
366
|
+
'Cache-Control': 'no-store',
|
|
367
|
+
});
|
|
368
|
+
res.end(JSON.stringify(state.snapshot()));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (method === 'GET' && url === '/dashboard') {
|
|
372
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
373
|
+
res.end(dashboardHtml());
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (method === 'GET' && url === '/') {
|
|
377
|
+
res.writeHead(200, { 'Content-Type': 'text/turtle' });
|
|
378
|
+
res.end(indexTtl);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (method === 'GET') {
|
|
382
|
+
const reqPath = url.startsWith('/') ? url.slice(1) : url;
|
|
383
|
+
const absPath = resolve(cwd, reqPath);
|
|
384
|
+
if (!whitelist.has(absPath)) {
|
|
385
|
+
res.writeHead(403, { 'Content-Type': 'text/plain' });
|
|
386
|
+
res.end('Forbidden');
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
let content;
|
|
390
|
+
try {
|
|
391
|
+
content = await readFile(absPath, { encoding: 'utf8' });
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
395
|
+
res.end('Not found');
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
res.writeHead(200, { 'Content-Type': 'text/turtle' });
|
|
399
|
+
res.end(content);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (method === 'POST' && url === '/connect') {
|
|
403
|
+
let body;
|
|
404
|
+
try {
|
|
405
|
+
body = await readBody(req);
|
|
406
|
+
}
|
|
407
|
+
catch {
|
|
408
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
409
|
+
res.end('Failed to read request body');
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
let parsed;
|
|
413
|
+
try {
|
|
414
|
+
parsed = JSON.parse(body);
|
|
415
|
+
}
|
|
416
|
+
catch {
|
|
417
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
418
|
+
res.end('Invalid JSON');
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const { host, uri } = parsed;
|
|
422
|
+
if (typeof host !== 'string' || typeof uri !== 'string') {
|
|
423
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
424
|
+
res.end('Missing host or uri');
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const runnerId = state.registerRunner(host, uri);
|
|
428
|
+
const ctrl = new AbortController();
|
|
429
|
+
activeConnections.add(ctrl);
|
|
430
|
+
start(host, uri, absConfig, ctrl.signal, state, runnerId)
|
|
431
|
+
.catch((err) => {
|
|
432
|
+
console.error('gRPC connection error:', err);
|
|
433
|
+
state.markError(runnerId);
|
|
434
|
+
})
|
|
435
|
+
.finally(() => {
|
|
436
|
+
activeConnections.delete(ctrl);
|
|
437
|
+
state.deregisterRunner(runnerId);
|
|
438
|
+
});
|
|
439
|
+
res.writeHead(202, {
|
|
440
|
+
'Content-Type': 'application/json',
|
|
441
|
+
Location: '/dashboard',
|
|
442
|
+
});
|
|
443
|
+
res.end(JSON.stringify({ runnerId }));
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
447
|
+
res.end('Not found');
|
|
448
|
+
});
|
|
449
|
+
await new Promise((resolve) => {
|
|
450
|
+
server.listen(port, () => {
|
|
451
|
+
console.log(`js-runner server listening on port ${port}`);
|
|
452
|
+
console.log(` Dashboard: http://localhost:${port}/dashboard`);
|
|
453
|
+
console.log(` Health: http://localhost:${port}/health`);
|
|
454
|
+
console.log(` State API: http://localhost:${port}/api/state`);
|
|
455
|
+
resolve();
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3NlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsWUFBWSxFQUFtQixNQUFNLFdBQVcsQ0FBQTtBQUN6RCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sa0JBQWtCLENBQUE7QUFDM0MsT0FBTyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFDN0MsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLElBQUksQ0FBQTtBQUMzQixPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sVUFBVSxDQUFBO0FBQ3hDLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxhQUFhLENBQUE7QUFDbkMsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLFlBQVksQ0FBQTtBQUVsQyxNQUFNLElBQUksR0FBRywrQkFBK0IsQ0FBQTtBQUM1QyxNQUFNLFdBQVcsR0FBRyx1Q0FBdUMsQ0FBQTtBQUMzRCxNQUFNLFFBQVEsR0FBRyxpREFBaUQsQ0FBQTtBQUNsRSxNQUFNLFVBQVUsR0FBRyw0Q0FBNEMsQ0FBQTtBQUMvRCxNQUFNLFlBQVksR0FBRyw4Q0FBOEMsQ0FBQTtBQUduRSxNQUFNLGdCQUFnQixHQUFHOzs7Ozs7Ozs7Ozs7Ozs7OztDQWlCeEIsQ0FBQTtBQUVELE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxhQUFhLENBQUMsSUFBSSxNQUFNLEVBQUUsQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxDQUFBO0FBY3RFLFNBQVMsUUFBUSxDQUFDLEdBQW9CO0lBQ3BDLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSSxJQUFJLEdBQUcsRUFBRSxDQUFBO1FBQ2IsR0FBRyxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUMsSUFBSSxJQUFJLEtB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QUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsR0FBRyxFQUFFO1lBQ3ZCLE9BQU8sQ0FBQyxHQUFHLENBQUMsc0NBQXNDLElBQUksRUFBRSxDQUFDLENBQUE7WUFDekQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQ0FBaUMsSUFBSSxZQUFZLENBQUMsQ0FBQTtZQUM5RCxPQUFPLENBQUMsR0FBRyxDQUFDLGlDQUFpQyxJQUFJLFNBQVMsQ0FBQyxDQUFBO1lBQzNELE9BQU8sQ0FBQyxHQUFHLENBQUMsaUNBQWlDLElBQUksWUFBWSxDQUFDLENBQUE7WUFDOUQsT0FBTyxFQUFFLENBQUE7UUFDWCxDQUFDLENBQUMsQ0FBQTtJQUNKLENBQUMsQ0FBQyxDQUFBO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNyZWF0ZVNlcnZlciwgSW5jb21pbmdNZXNzYWdlIH0gZnJvbSAnbm9kZTpodHRwJ1xuaW1wb3J0IHsgcmVhZEZpbGUgfSBmcm9tICdub2RlOmZzL3Byb21pc2VzJ1xuaW1wb3J0IHsgcmVzb2x2ZSwgcmVsYXRpdmUgfSBmcm9tICdub2RlOnBhdGgnXG5pbXBvcnQgeyBQYXJzZXIgfSBmcm9tICduMydcbmltcG9ydCB7IGV4dHJhY3RTaGFwZXMgfSBmcm9tICdyZGYtbGVucydcbmltcG9ydCB7IHN0YXJ0IH0gZnJvbSAnLi9jbGllbnQuanMnXG5pbXBvcnQgeyBTdGF0ZSB9IGZyb20gJy4vc3RhdGUuanMnXG5cbmNvbnN0IFJERkMgPSAnaHR0cHM6Ly93M2lkLm9yZy9yZGYtY29ubmVjdCMnXG5jb25zdCBPV0xfSU1QT1JUUyA9ICdodHRwOi8vd3d3LnczLm9yZy8yMDAyLzA3L293bCNpbXBvcnRzJ1xuY29uc3QgUkRGX1RZUEUgPSAnaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zI3R5cGUnXG5jb25zdCBSREZTX0xBQkVMID0gJ2h0dHA6Ly93d3cudzMub3JnLzIwMDAvMDEvcmRmLXNjaGVtYSNsYWJlbCdcbmNvbnN0IFJERlNfQ09NTUVOVCA9ICdodHRwOi8vd3d3LnczLm9yZy8yMDAwLzAxL3JkZi1zY2hlbWEjY29tbWVudCdcblxuLy8gU0hBQ0wgc2hhcGUgZm9yIHJkZmM6SnNSdW5uZXJTZXJ2ZXIgY29uZmlnIGZpbGVzXG5jb25zdCBDT05GSUdfU0hBUEVfVFRMID0gYFxuQHByZWZpeCByZGZjOiA8aHR0cHM6Ly93M2lkLm9yZy9yZGYtY29ubmVjdCM+LlxuQHByZWZpeCB4c2Q6IDxodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSM+LlxuQHByZWZpeCBzaDogPGh0dHA6Ly93d3cudzMub3JnL25zL3NoYWNsIz4uXG5cbltdIGEgc2g6Tm9kZVNoYXBlO1xuICBzaDp0YXJnZXRDbGFzcyByZGZjOkpzUnVubmVyU2VydmVyO1xuICBzaDpwcm9wZXJ0eSBbXG4gICAgc2g6cGF0aCByZGZjOnBvcnQ7XG4gICAgc2g6bmFtZSBcInBvcnRcIjtcbiAgICBzaDptYXhDb3VudCAxO1xuICAgIHNoOmRhdGF0eXBlIHhzZDppbnRlZ2VyO1xuICBdLCBbXG4gICAgc2g6cGF0aCByZGZjOnByb2Nlc3NvckNvbmZpZztcbiAgICBzaDpuYW1lIFwicHJvY2Vzc29yQ29uZmlnc1wiO1xuICAgIHNoOmRhdGF0eXBlIHhzZDpzdHJpbmc7XG4gIF0uXG5gXG5cbmNvbnN0IHsgbGVuc2VzIH0gPSBleHRyYWN0U2hhcGVzKG5ldyBQYXJzZXIoKS5wYXJzZShDT05GSUdfU0hBUEVfVFRMKSlcblxuaW50ZXJmYWNlIFNlcnZlckNvbmZpZyB7XG4gIHBvcnQ6IG51bWJlclxuICBwcm9jZXNzb3JQYXRoczogc3RyaW5nW11cbn1cblxuaW50ZXJmYWNlIFByb2Nlc3NvckRlc2NyaXB0aW9uIHtcbiAgdXJpOiBzdHJpbmdcbiAgbGFiZWw/OiBzdHJpbmdcbiAgY29tbWVudD86IHN0cmluZ1xuICBzb3VyY2VGaWxlOiBzdHJpbmdcbn1cblxuZnVuY3Rpb24gcmVhZEJvZHkocmVxOiBJbmNvbWluZ01lc3NhZ2UpOiBQcm9taXNlPHN0cmluZz4ge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIGxldCBib2R5ID0gJydcbiAgICByZXEub24oJ2RhdGEnLCAoY2h1bmspID0+IChib2R5ICs9IGNodW5rKSlcbiAgICByZXEub24oJ2VuZCcsICgpID0+IHJlc29sdmUoYm9keSkpXG4gICAgcmVxLm9uKCdlcnJvcicsIHJlamVjdClcbiAgfSlcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBhcnNlU2VydmVyQ29uZmlnKFxuICBjb25maWdQYXRoOiBzdHJpbmcsXG4pOiBQcm9taXNlPFNlcnZlckNvbmZpZz4ge1xuICBjb25zdCBhYnNDb25maWcgPSByZXNvbHZlKGNvbmZpZ1BhdGgpXG4gIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCByZWFkRmlsZShhYnNDb25maWcsIHsgZW5jb2Rpbmc6ICd1dGY4JyB9KVxuICBjb25zdCBxdWFkcyA9IG5ldyBQYXJzZXIoeyBiYXNlSVJJOiAnZmlsZTovLycgKyBhYnNDb25maWcgfSkucGFyc2UoY29udGVudClcblxuICBjb25zdCBzZXJ2ZXJTdWJqZWN0ID0gcXVhZHMuZmluZChcbiAgICAocSkgPT5cbiAgICAgIHEucHJlZGljYXRlLnZhbHVlID09PSBSREZfVFlQRSAmJlxuICAgICAgcS5vYmplY3QudmFsdWUgPT09IFJERkMgKyAnSnNSdW5uZXJTZXJ2ZXInLFxuICApPy5zdWJqZWN0XG5cbiAgaWYgKCFzZXJ2ZXJTdWJqZWN0KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBObyByZGZjOkpzUnVubmVyU2VydmVyIGZvdW5kIGluICR7YWJzQ29uZmlnfWApXG4gIH1cblxuICBjb25zdCBjb25maWcgPSBsZW5zZXNbUkRGQyArICdKc1J1bm5lclNlcnZlciddLmV4ZWN1dGUoe1xuICAgIGlkOiBzZXJ2ZXJTdWJqZWN0LFxuICAgIHF1YWRzLFxuICB9KSBhcyB7IHBvcnQ/OiBudW1iZXI7IHByb2Nlc3NvckNvbmZpZ3M/OiBzdHJpbmdbXSB9XG5cbiAgY29uc3QgcG9ydCA9IGNvbmZpZy5wb3J0ID8/IDMwMDBcbiAgY29uc3QgcHJvY2Vzc29yUGF0aHMgPSAoY29uZmlnLnByb2Nlc3NvckNvbmZpZ3MgPz8gW10pLm1hcCgodmFsKSA9PlxuICAgIHZhbC5zdGFydHNXaXRoKCdmaWxlOi8vJykgPyB2YWwuc2xpY2UoJ2ZpbGU6Ly8nLmxlbmd0aCkgOiB2YWwsXG4gIClcblxuICByZXR1cm4geyBwb3J0LCBwcm9jZXNzb3JQYXRocyB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBidWlsZFdoaXRlbGlzdChcbiAgcHJvY2Vzc29yUGF0aHM6IHN0cmluZ1tdLFxuKTogUHJvbWlzZTxTZXQ8c3RyaW5nPj4ge1xuICBjb25zdCB3aGl0ZWxpc3QgPSBuZXcgU2V0PHN0cmluZz4oKVxuICBjb25zdCBkb25lID0gbmV3IFNldDxzdHJpbmc+KClcbiAgY29uc3QgdG9kbzogc3RyaW5nW10gPSBbLi4ucHJvY2Vzc29yUGF0aHNdXG5cbiAgd2hpbGUgKHRvZG8ubGVuZ3RoID4gMCkge1xuICAgIGNvbnN0IGZpbGVQYXRoID0gdG9kby5wb3AoKSFcbiAgICBpZiAoZG9uZS5oYXMoZmlsZVBhdGgpKSBjb250aW51ZVxuICAgIGRvbmUuYWRkKGZpbGVQYXRoKVxuICAgIHdoaXRlbGlzdC5hZGQoZmlsZVBhdGgpXG5cbiAgICBsZXQgY29udGVudDogc3RyaW5nXG4gICAgdHJ5IHtcbiAgICAgIGNvbnRlbnQgPSBhd2FpdCByZWFkRmlsZShmaWxlUGF0aCwgeyBlbmNvZGluZzogJ3V0ZjgnIH0pXG4gICAgfSBjYXRjaCB7XG4gICAgICBjb250aW51ZVxuICAgIH1cblxuICAgIGNvbnN0IGJhc2VJUkkgPSAnZmlsZTovLycgKyBmaWxlUGF0aFxuICAgIGNvbnN0IHF1YWRzID0gbmV3IFBhcnNlcih7IGJhc2VJUkkgfSkucGFyc2UoY29udGVudClcblxuICAgIGZvciAoY29uc3QgcXVhZCBvZiBxdWFkcykge1xuICAgICAgaWYgKFxuICAgICAgICBxdWFkLnN1YmplY3QudmFsdWUgPT09IGJhc2VJUkkgJiZcbiAgICAgICAgcXVhZC5wcmVkaWNhdGUudmFsdWUgPT09IE9XTF9JTVBPUlRTXG4gICAgICApIHtcbiAgICAgICAgY29uc3QgaW1wb3J0VmFsID0gcXVhZC5vYmplY3QudmFsdWVcbiAgICAgICAgaWYgKGltcG9ydFZhbC5zdGFydHNXaXRoKCdmaWxlOi8vJykpIHtcbiAgICAgICAgICB0b2RvLnB1c2goaW1wb3J0VmFsLnNsaWNlKCdmaWxlOi8vJy5sZW5ndGgpKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHdoaXRlbGlzdFxufVxuXG5hc3luYyBmdW5jdGlvbiBleHRyYWN0UHJvY2Vzc29yRGVzY3JpcHRpb25zKFxuICBwcm9jZXNzb3JQYXRoczogc3RyaW5nW10sXG4pOiBQcm9taXNlPFByb2Nlc3NvckRlc2NyaXB0aW9uW10+IHtcbiAgY29uc3QgZGVzY3JpcHRpb25zOiBQcm9jZXNzb3JEZXNjcmlwdGlvbltdID0gW11cblxuICBmb3IgKGNvbnN0IGZpbGVQYXRoIG9mIHByb2Nlc3NvclBhdGhzKSB7XG4gICAgbGV0IGNvbnRlbnQ6IHN0cmluZ1xuICAgIHRyeSB7XG4gICAgICBjb250ZW50ID0gYXdhaXQgcmVhZEZpbGUoZmlsZVBhdGgsIHsgZW5jb2Rpbmc6ICd1dGY4JyB9KVxuICAgIH0gY2F0Y2gge1xuICAgICAgY29udGludWVcbiAgICB9XG5cbiAgICBjb25zdCBiYXNlSVJJID0gJ2ZpbGU6Ly8nICsgZmlsZVBhdGhcbiAgICBjb25zdCBxdWFkcyA9IG5ldyBQYXJzZXIoeyBiYXNlSVJJIH0pLnBhcnNlKGNvbnRlbnQpXG5cbiAgICBjb25zdCBzZWVuID0gbmV3IFNldDxzdHJpbmc+KClcbiAgICBmb3IgKGNvbnN0IHF1YWQgb2YgcXVhZHMpIHtcbiAgICAgIGlmIChxdWFkLnByZWRpY2F0ZS52YWx1ZSAhPT0gUkRGQyArICdqc0ltcGxlbWVudGF0aW9uT2YnKSBjb250aW51ZVxuICAgICAgY29uc3QgdXJpID0gcXVhZC5zdWJqZWN0LnZhbHVlXG4gICAgICBpZiAoc2Vlbi5oYXModXJpKSkgY29udGludWVcbiAgICAgIHNlZW4uYWRkKHVyaSlcblxuICAgICAgY29uc3QgbGFiZWxRdWFkID0gcXVhZHMuZmluZChcbiAgICAgICAgKHEpID0+IHEuc3ViamVjdC52YWx1ZSA9PT0gdXJpICYmIHEucHJlZGljYXRlLnZhbHVlID09PSBSREZTX0xBQkVMLFxuICAgICAgKVxuICAgICAgY29uc3QgY29tbWVudFF1YWQgPSBxdWFkcy5maW5kKFxuICAgICAgICAocSkgPT4gcS5zdWJqZWN0LnZhbHVlID09PSB1cmkgJiYgcS5wcmVkaWNhdGUudmFsdWUgPT09IFJERlNfQ09NTUVOVCxcbiAgICAgIClcblxuICAgICAgZGVzY3JpcHRpb25zLnB1c2goe1xuICAgICAgICB1cmksXG4gICAgICAgIGxhYmVsOiBsYWJlbFF1YWQ/Lm9iamVjdC52YWx1ZSxcbiAgICAgICAgY29tbWVudDogY29tbWVudFF1YWQ/Lm9iamVjdC52YWx1ZSxcbiAgICAgICAgc291cmNlRmlsZTogZmlsZVBhdGgsXG4gICAgICB9KVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBkZXNjcmlwdGlvbnNcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdlbmVyYXRlSW5kZXhUdGwoXG4gIHByb2Nlc3NvclBhdGhzOiBzdHJpbmdbXSxcbiAgY3dkOiBzdHJpbmcsXG4pOiBQcm9taXNlPHN0cmluZz4ge1xuICBjb25zdCBkZXNjcmlwdGlvbnMgPSBhd2FpdCBleHRyYWN0UHJvY2Vzc29yRGVzY3JpcHRpb25zKHByb2Nlc3NvclBhdGhzKVxuXG4gIGNvbnN0IGxpbmVzID0gYFxuQHByZWZpeCBwcm92OiA8aHR0cDovL3d3dy53My5vcmcvbnMvcHJvdiM+LlxuQHByZWZpeCBzZHM6IDxodHRwczovL3czaWQub3JnL3NkcyM+LlxuQHByZWZpeCByZGZzOiA8aHR0cDovL3d3dy53My5vcmcvMjAwMC8wMS9yZGYtc2NoZW1hIz4uXG5AcHJlZml4IG93bDogPGh0dHA6Ly93d3cudzMub3JnLzIwMDIvMDcvb3dsIz4uXG5AcHJlZml4IHJkZmw6IDxodHRwczovL3czaWQub3JnL3JkZi1sZW5zL29udG9sb2d5Iz4uXG5AcHJlZml4IHJkZjogPGh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyM+LlxuQHByZWZpeCB4c2Q6IDxodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSM+LlxuQHByZWZpeCBzaDogPGh0dHA6Ly93d3cudzMub3JnL25zL3NoYWNsIz4uXG5AcHJlZml4IHJkZmM6IDxodHRwczovL3czaWQub3JnL3JkZi1jb25uZWN0Iz4uXG5cbnNkczpBY3Rpdml0eSByZGZzOnN1YkNsYXNzT2YgcHJvdjpBY3Rpdml0eS5cbnJkZmM6UHJvY2Vzc29yIHJkZnM6c3ViQ2xhc3NPZiBzZHM6QWN0aXZpdHkuXG5zZHM6aW1wbGVtZW50YXRpb25PZiByZGZzOnN1YlByb3BlcnR5T2YgcmRmczpzdWJDbGFzc09mLlxucmRmYzpqc0ltcGxlbWVudGF0aW9uT2YgcmRmczpzdWJQcm9wZXJ0eU9mIHNkczppbXBsZW1lbnRhdGlvbk9mLlxuWyBdIGEgc2g6Tm9kZVNoYXBlO1xuICBzaDp0YXJnZXRTdWJqZWN0c09mIHJkZmM6anNJbXBsZW1lbnRhdGlvbk9mO1xuICBzaDpwcm9wZXJ0eSBbXG4gICAgc2g6cGF0aCByZGZjOmVudHJ5cG9pbnQ7XG4gICAgc2g6bmFtZSBcImxvY2F0aW9uXCI7XG4gICAgc2g6bWluQ291bnQgMTtcbiAgICBzaDptYXhDb3VudCAxO1xuICAgIHNoOmRhdGF0eXBlIHhzZDpzdHJpbmc7XG4gIF0sIFtcbiAgICBzaDpwYXRoIHJkZmM6ZmlsZTtcbiAgICBzaDpuYW1lIFwiZmlsZVwiO1xuICAgIHNoOm1pbkNvdW50IDE7XG4gICAgc2g6bWF4Q291bnQgMTtcbiAgICBzaDpkYXRhdHlwZSB4c2Q6c3RyaW5nO1xuICBdLCBbXG4gICAgc2g6cGF0aCByZGZjOmNsYXNzO1xuICAgIHNoOm5hbWUgXCJjbGF6elwiO1xuICAgIHNoOm1heENvdW50IDE7XG4gICAgc2g6ZGF0YXR5cGUgeHNkOnN0cmluZztcbiAgXS5cblxuPHJ1bm5lcj4gYSByZGZjOkh0dHBSdW5uZXI7XG4gIHJkZmM6aGFuZGxlc1N1YmplY3RzT2YgcmRmYzpqc0ltcGxlbWVudGF0aW9uT2Y7XG4gIHJkZmM6ZW5kcG9pbnQgPC4vY29ubmVjdD4uXG5gLnNwbGl0KCdcXG4nKVxuXG4gIGZvciAoY29uc3QgZGVzYyBvZiBkZXNjcmlwdGlvbnMpIHtcbiAgICBjb25zdCByZWxQYXRoID0gcmVsYXRpdmUoY3dkLCBkZXNjLnNvdXJjZUZpbGUpXG4gICAgbGluZXMucHVzaChgPCR7ZGVzYy51cml9PiBhIHJkZmM6UHJvY2Vzc29yO2ApXG4gICAgaWYgKGRlc2MubGFiZWwpIHtcbiAgICAgIGxpbmVzLnB1c2goXG4gICAgICAgIGAgIHJkZnM6bGFiZWwgXCIke2Rlc2MubGFiZWwucmVwbGFjZSgvXFxcXC9nLCAnXFxcXFxcXFwnKS5yZXBsYWNlKC9cIi9nLCAnXFxcXFwiJyl9XCI7YCxcbiAgICAgIClcbiAgICB9XG4gICAgaWYgKGRlc2MuY29tbWVudCkge1xuICAgICAgbGluZXMucHVzaChcbiAgICAgICAgYCAgcmRmczpjb21tZW50IFwiJHtkZXNjLmNvbW1lbnQucmVwbGFjZSgvXFxcXC9nLCAnXFxcXFxcXFwnKS5yZXBsYWNlKC9cIi9nLCAnXFxcXFwiJyl9XCI7YCxcbiAgICAgIClcbiAgICB9XG4gICAgbGluZXMucHVzaChgICByZGZzOmlzRGVmaW5lZEJ5IDwke3JlbFBhdGh9Pi5gKVxuICAgIGxpbmVzLnB1c2goJycpXG4gIH1cblxuICByZXR1cm4gbGluZXMuam9pbignXFxuJylcbn1cblxuZnVuY3Rpb24gZGFzaGJvYXJkSHRtbCgpOiBzdHJpbmcge1xuICByZXR1cm4gYDwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG48aGVhZD5cbiAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gIDx0aXRsZT5qcy1ydW5uZXIgZGFzaGJvYXJkPC90aXRsZT5cbiAgPHN0eWxlPlxuICAgICogeyBib3gtc2l6aW5nOiBib3JkZXItYm94OyBtYXJnaW46IDA7IHBhZGRpbmc6IDA7IH1cbiAgICBib2R5IHsgZm9udC1mYW1pbHk6ICdDb3VyaWVyIE5ldycsIG1vbm9zcGFjZTsgYmFja2dyb3VuZDogIzExMTsgY29sb3I6ICNjY2M7IHBhZGRpbmc6IDEuNXJlbTsgfVxuICAgIGgxIHsgY29sb3I6ICM3ZWM4ZTM7IG1hcmdpbi1ib3R0b206IDFyZW07IGZvbnQtc2l6ZTogMS40cmVtOyB9XG4gICAgI3VwZGF0ZWQgeyBmb250LXNpemU6IDAuNzVyZW07IGNvbG9yOiAjNTU1OyBtYXJnaW4tYm90dG9tOiAxLjVyZW07IH1cbiAgICAucnVubmVyIHsgYm9yZGVyOiAxcHggc29saWQgIzMzMzsgYm9yZGVyLXJhZGl1czogNnB4OyBwYWRkaW5nOiAxcmVtOyBtYXJnaW4tYm90dG9tOiAxLjI1cmVtOyB9XG4gICAgLnJ1bm5lci1tZXRhIHsgZGlzcGxheTogZmxleDsgZmxleC13cmFwOiB3cmFwOyBnYXA6IDEuNXJlbTsgbWFyZ2luLWJvdHRvbTogMC43NXJlbTsgZm9udC1zaXplOiAwLjg1cmVtOyB9XG4gICAgLnJ1bm5lci1tZXRhIHNwYW4geyBjb2xvcjogIzg4ODsgfVxuICAgIC5ydW5uZXItbWV0YSBzdHJvbmcgeyBjb2xvcjogI2FhYTsgfVxuICAgIC5zdGF0dXMgeyBmb250LXdlaWdodDogYm9sZDsgfVxuICAgIC5zLWNvbm5lY3RpbmcgeyBjb2xvcjogI2YwYTUwMDsgfVxuICAgIC5zLXJ1bm5pbmcgICAgeyBjb2xvcjogIzRjYWY1MDsgfVxuICAgIC5zLWRvbmUgICAgICAgeyBjb2xvcjogIzY2NjsgfVxuICAgIC5zLWVycm9yICAgICAgeyBjb2xvcjogI2Y0NDMzNjsgfVxuICAgIC5nLUlETEUgICAgICAgICAgICAgIHsgY29sb3I6ICM4ODg7IH1cbiAgICAuZy1DT05ORUNUSU5HICAgICAgICB7IGNvbG9yOiAjZjBhNTAwOyB9XG4gICAgLmctUkVBRFkgICAgICAgICAgICAgeyBjb2xvcjogIzRjYWY1MDsgfVxuICAgIC5nLVRSQU5TSUVOVF9GQUlMVVJFIHsgY29sb3I6ICNmNDQzMzY7IH1cbiAgICAuZy1TSFVURE9XTiAgICAgICAgICB7IGNvbG9yOiAjNjY2OyB9XG4gICAgdGFibGUgeyB3aWR0aDogMTAwJTsgYm9yZGVyLWNvbGxhcHNlOiBjb2xsYXBzZTsgZm9udC1zaXplOiAwLjhyZW07IH1cbiAgICB0aCwgdGQgeyBib3JkZXI6IDFweCBzb2xpZCAjMmEyYTJhOyBwYWRkaW5nOiAwLjRyZW0gMC44cmVtOyB0ZXh0LWFsaWduOiBsZWZ0OyB3aGl0ZS1zcGFjZTogbm93cmFwOyB9XG4gICAgdGggeyBiYWNrZ3JvdW5kOiAjMWExYTFhOyBjb2xvcjogIzdlYzhlMzsgfVxuICAgIHRkLnVyaSB7IGNvbG9yOiAjYWFhOyBtYXgtd2lkdGg6IDI4cmVtOyBvdmVyZmxvdzogaGlkZGVuOyB0ZXh0LW92ZXJmbG93OiBlbGxpcHNpczsgfVxuICAgIHRkLnJvbGUtcmVhZGVyIHsgY29sb3I6ICM4MWM3ODQ7IH1cbiAgICB0ZC5yb2xlLXdyaXRlciB7IGNvbG9yOiAjNjRiNWY2OyB9XG4gICAgLmVtcHR5IHsgY29sb3I6ICM1NTU7IGZvbnQtc2l6ZTogMC44cmVtOyBwYWRkaW5nLXRvcDogMC41cmVtOyB9XG4gICAgLm5vLXJ1bm5lcnMgeyBjb2xvcjogIzU1NTsgfVxuICA8L3N0eWxlPlxuPC9oZWFkPlxuPGJvZHk+XG4gIDxoMT5qcy1ydW5uZXIgZGFzaGJvYXJkPC9oMT5cbiAgPGRpdiBpZD1cInVwZGF0ZWRcIj48L2Rpdj5cbiAgPGRpdiBpZD1cImNvbnRlbnRcIj48cCBjbGFzcz1cIm5vLXJ1bm5lcnNcIj5Mb2FkaW5n4oCmPC9wPjwvZGl2PlxuICA8c2NyaXB0PlxuICAgIGNvbnN0IHRocm91Z2hwdXRTdGF0ZSA9IHt9XG5cbiAgICBmdW5jdGlvbiBlc2Mocykge1xuICAgICAgcmV0dXJuIFN0cmluZyhzKS5yZXBsYWNlKC8mL2csJyZhbXA7JykucmVwbGFjZSgvPC9nLCcmbHQ7JykucmVwbGFjZSgvPi9nLCcmZ3Q7JylcbiAgICB9XG4gICAgZnVuY3Rpb24gZm10Qnl0ZXMoYikge1xuICAgICAgaWYgKGIgPCAxMDI0KSByZXR1cm4gYiArICcgQidcbiAgICAgIGlmIChiIDwgMTA0ODU3NikgcmV0dXJuIChiLzEwMjQpLnRvRml4ZWQoMSkgKyAnIEtCJ1xuICAgICAgcmV0dXJuIChiLzEwNDg1NzYpLnRvRml4ZWQoMSkgKyAnIE1CJ1xuICAgIH1cbiAgICBmdW5jdGlvbiBmbXRBZ2UobXMpIHtcbiAgICAgIGlmICghbXMpIHJldHVybiAnLSdcbiAgICAgIGNvbnN0IHMgPSBNYXRoLmZsb29yKChEYXRlLm5vdygpIC0gbXMpIC8gMTAwMClcbiAgICAgIGlmIChzIDwgNSkgcmV0dXJuICdqdXN0IG5vdydcbiAgICAgIGlmIChzIDwgNjApIHJldHVybiBzICsgJ3MgYWdvJ1xuICAgICAgaWYgKHMgPCAzNjAwKSByZXR1cm4gTWF0aC5mbG9vcihzLzYwKSArICdtIGFnbydcbiAgICAgIHJldHVybiBNYXRoLmZsb29yKHMvMzYwMCkgKyAnaCBhZ28nXG4gICAgfVxuICAgIGZ1bmN0aW9uIGZtdER1cmF0aW9uKG1zKSB7XG4gICAgICBjb25zdCBzID0gTWF0aC5mbG9vcigoRGF0ZS5ub3coKSAtIG1zKSAvIDEwMDApXG4gICAgICBpZiAocyA8IDYwKSByZXR1cm4gcyArICdzJ1xuICAgICAgaWYgKHMgPCAzNjAwKSByZXR1cm4gTWF0aC5mbG9vcihzLzYwKSArICdtICcgKyAocyU2MCkgKyAncydcbiAgICAgIHJldHVybiBNYXRoLmZsb29yKHMvMzYwMCkgKyAnaCAnICsgTWF0aC5mbG9vcigocyUzNjAwKS82MCkgKyAnbSdcbiAgICB9XG4gICAgZnVuY3Rpb24gcGN0KGFyciwgcCkge1xuICAgICAgaWYgKCFhcnIgfHwgIWFyci5sZW5ndGgpIHJldHVybiAnLSdcbiAgICAgIGNvbnN0IHMgPSBbLi4uYXJyXS5zb3J0KChhLGIpID0+IGEtYilcbiAgICAgIHJldHVybiBzW01hdGguZmxvb3IocC8xMDAqcy5sZW5ndGgpXSArICdtcydcbiAgICB9XG4gICAgZnVuY3Rpb24gYXZnKGFycikge1xuICAgICAgaWYgKCFhcnIgfHwgIWFyci5sZW5ndGgpIHJldHVybiAnLSdcbiAgICAgIHJldHVybiAoYXJyLnJlZHVjZSgoYSxiKT0+YStiLDApL2Fyci5sZW5ndGgpLnRvRml4ZWQoMSkgKyAnbXMnXG4gICAgfVxuICAgIGZ1bmN0aW9uIHRocm91Z2hwdXQoa2V5LCBjb3VudCkge1xuICAgICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKVxuICAgICAgaWYgKCF0aHJvdWdocHV0U3RhdGVba2V5XSkgeyB0aHJvdWdocHV0U3RhdGVba2V5XSA9IHtjb3VudCwgdGltZTogbm93fTsgcmV0dXJuICctJyB9XG4gICAgICBjb25zdCBkdCA9IChub3cgLSB0aHJvdWdocHV0U3RhdGVba2V5XS50aW1lKSAvIDEwMDBcbiAgICAgIGNvbnN0IGRjID0gY291bnQgLSB0aHJvdWdocHV0U3RhdGVba2V5XS5jb3VudFxuICAgICAgdGhyb3VnaHB1dFN0YXRlW2tleV0gPSB7Y291bnQsIHRpbWU6IG5vd31cbiAgICAgIGlmIChkdCA8IDAuMSkgcmV0dXJuICctJ1xuICAgICAgcmV0dXJuIChkYyAvIGR0KS50b0ZpeGVkKDEpICsgJy9zJ1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIHJlbmRlckNoYW5uZWxzKHJ1bm5lcklkLCBjaGFubmVscykge1xuICAgICAgY29uc3QgZW50cmllcyA9IE9iamVjdC52YWx1ZXMoY2hhbm5lbHMpXG4gICAgICBpZiAoIWVudHJpZXMubGVuZ3RoKSByZXR1cm4gJzxwIGNsYXNzPVwiZW1wdHlcIj5ObyBjaGFubmVscyB5ZXQuPC9wPidcbiAgICAgIHJldHVybiBcXGA8dGFibGU+XG4gICAgICAgIDx0aGVhZD5cbiAgICAgICAgICA8dHI+XG4gICAgICAgICAgICA8dGg+Q2hhbm5lbCBVUkk8L3RoPlxuICAgICAgICAgICAgPHRoPlJvbGU8L3RoPlxuICAgICAgICAgICAgPHRoPk1lc3NhZ2VzPC90aD5cbiAgICAgICAgICAgIDx0aD5UaHJvdWdocHV0PC90aD5cbiAgICAgICAgICAgIDx0aD5CeXRlczwvdGg+XG4gICAgICAgICAgICA8dGg+TGFzdCBtc2c8L3RoPlxuICAgICAgICAgICAgPHRoPkF2ZyBsYXRlbmN5PC90aD5cbiAgICAgICAgICAgIDx0aD5wNTA8L3RoPlxuICAgICAgICAgICAgPHRoPnA5OTwvdGg+XG4gICAgICAgICAgPC90cj5cbiAgICAgICAgPC90aGVhZD5cbiAgICAgICAgPHRib2R5PlxuICAgICAgICAgIFxcJHtlbnRyaWVzLm1hcChjaCA9PiB7XG4gICAgICAgICAgICBjb25zdCBrZXkgPSBydW5uZXJJZCArICc6JyArIGNoLnVyaVxuICAgICAgICAgICAgY29uc3QgdHAgPSB0aHJvdWdocHV0KGtleSwgY2gubWVzc2FnZUNvdW50KVxuICAgICAgICAgICAgcmV0dXJuIFxcYDx0cj5cbiAgICAgICAgICAgICAgPHRkIGNsYXNzPVwidXJpXCIgdGl0bGU9XCJcXCR7ZXNjKGNoLnVyaSl9XCI+XFwke2VzYyhjaC51cmkpfTwvdGQ+XG4gICAgICAgICAgICAgIDx0ZCBjbGFzcz1cInJvbGUtXFwke2NoLnJvbGV9XCI+XFwke2NoLnJvbGV9PC90ZD5cbiAgICAgICAgICAgICAgPHRkPlxcJHtjaC5tZXNzYWdlQ291bnR9PC90ZD5cbiAgICAgICAgICAgICAgPHRkPlxcJHt0cH08L3RkPlxuICAgICAgICAgICAgICA8dGQ+XFwke2ZtdEJ5dGVzKGNoLmJ5dGVzVG90YWwpfTwvdGQ+XG4gICAgICAgICAgICAgIDx0ZD5cXCR7Zm10QWdlKGNoLmxhc3RNZXNzYWdlQXQpfTwvdGQ+XG4gICAgICAgICAgICAgIDx0ZD5cXCR7Y2gucm9sZSA9PT0gJ3dyaXRlcicgPyBhdmcoY2gubGF0ZW5jaWVzTXMpIDogJy0nfTwvdGQ+XG4gICAgICAgICAgICAgIDx0ZD5cXCR7Y2gucm9sZSA9PT0gJ3dyaXRlcicgPyBwY3QoY2gubGF0ZW5jaWVzTXMsIDUwKSA6ICctJ308L3RkPlxuICAgICAgICAgICAgICA8dGQ+XFwke2NoLnJvbGUgPT09ICd3cml0ZXInID8gcGN0KGNoLmxhdGVuY2llc01zLCA5OSkgOiAnLSd9PC90ZD5cbiAgICAgICAgICAgIDwvdHI+XFxgXG4gICAgICAgICAgfSkuam9pbignJyl9XG4gICAgICAgIDwvdGJvZHk+XG4gICAgICA8L3RhYmxlPlxcYFxuICAgIH1cblxuICAgIGZ1bmN0aW9uIHJlbmRlcihydW5uZXJzKSB7XG4gICAgICBjb25zdCBlbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjb250ZW50JylcbiAgICAgIGlmICghcnVubmVycy5sZW5ndGgpIHtcbiAgICAgICAgZWwuaW5uZXJIVE1MID0gJzxwIGNsYXNzPVwibm8tcnVubmVyc1wiPk5vIHJ1bm5lcnMgcmVnaXN0ZXJlZCB5ZXQuPC9wPidcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICBlbC5pbm5lckhUTUwgPSBydW5uZXJzLm1hcChyID0+IFxcYFxuICAgICAgICA8ZGl2IGNsYXNzPVwicnVubmVyXCI+XG4gICAgICAgICAgPGRpdiBjbGFzcz1cInJ1bm5lci1tZXRhXCI+XG4gICAgICAgICAgICA8c3Bhbj48c3Ryb25nPlVSSTo8L3N0cm9uZz4gXFwke2VzYyhyLnVyaSl9PC9zcGFuPlxuICAgICAgICAgICAgPHNwYW4+PHN0cm9uZz5Ib3N0Ojwvc3Ryb25nPiBcXCR7ZXNjKHIuaG9zdCl9PC9zcGFuPlxuICAgICAgICAgICAgPHNwYW4+PHN0cm9uZz5TdGF0dXM6PC9zdHJvbmc+IDxzcGFuIGNsYXNzPVwic3RhdHVzIHMtXFwke3Iuc3RhdHVzfVwiPlxcJHtyLnN0YXR1c308L3NwYW4+PC9zcGFuPlxuICAgICAgICAgICAgPHNwYW4+PHN0cm9uZz5nUlBDOjwvc3Ryb25nPiA8c3BhbiBjbGFzcz1cImctXFwke3IuZ3JwY1N0YXRlfVwiPlxcJHtyLmdycGNTdGF0ZX08L3NwYW4+PC9zcGFuPlxuICAgICAgICAgICAgPHNwYW4+PHN0cm9uZz5VcHRpbWU6PC9zdHJvbmc+IFxcJHtmbXREdXJhdGlvbihyLmNvbm5lY3RlZEF0KX08L3NwYW4+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgXFwke3JlbmRlckNoYW5uZWxzKHIuaWQsIHIuY2hhbm5lbHMpfVxuICAgICAgICA8L2Rpdj5cbiAgICAgIFxcYCkuam9pbignJylcbiAgICB9XG5cbiAgICBhc3luYyBmdW5jdGlvbiByZWZyZXNoKCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVzID0gYXdhaXQgZmV0Y2goJy9hcGkvc3RhdGUnKVxuICAgICAgICBjb25zdCBkYXRhID0gYXdhaXQgcmVzLmpzb24oKVxuICAgICAgICByZW5kZXIoZGF0YSlcbiAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3VwZGF0ZWQnKS50ZXh0Q29udGVudCA9XG4gICAgICAgICAgJ0xhc3QgdXBkYXRlZDogJyArIG5ldyBEYXRlKCkudG9Mb2NhbGVUaW1lU3RyaW5nKClcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NvbnRlbnQnKS5pbm5lckhUTUwgPVxuICAgICAgICAgICc8cCBzdHlsZT1cImNvbG9yOiNmNDQzMzZcIj5GYWlsZWQgdG8gZmV0Y2ggc3RhdGU6ICcgKyBlc2MoZS5tZXNzYWdlKSArICc8L3A+J1xuICAgICAgfVxuICAgIH1cblxuICAgIHJlZnJlc2goKVxuICAgIHNldEludGVydmFsKHJlZnJlc2gsIDIwMDApXG4gIDwvc2NyaXB0PlxuPC9ib2R5PlxuPC9odG1sPmBcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNlcnZlKGNvbmZpZ1BhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBhYnNDb25maWcgPSByZXNvbHZlKGNvbmZpZ1BhdGgpXG4gIGNvbnN0IHsgcG9ydCwgcHJvY2Vzc29yUGF0aHMgfSA9IGF3YWl0IHBhcnNlU2VydmVyQ29uZmlnKGFic0NvbmZpZylcbiAgY29uc3Qgd2hpdGVsaXN0ID0gYXdhaXQgYnVpbGRXaGl0ZWxpc3QocHJvY2Vzc29yUGF0aHMpXG4gIGNvbnN0IGN3ZCA9IHByb2Nlc3MuY3dkKClcbiAgY29uc3QgaW5kZXhUdGwgPSBhd2FpdCBnZW5lcmF0ZUluZGV4VHRsKHByb2Nlc3NvclBhdGhzLCBjd2QpXG4gIGNvbnN0IHN0YXRlID0gbmV3IFN0YXRlKClcblxuICBjb25zdCBhY3RpdmVDb25uZWN0aW9ucyA9IG5ldyBTZXQ8QWJvcnRDb250cm9sbGVyPigpXG5cbiAgY29uc3Qgc2h1dGRvd24gPSAoc2lnbmFsOiBzdHJpbmcpID0+IHtcbiAgICBjb25zb2xlLmxvZyhcbiAgICAgIGBcXG5SZWNlaXZlZCAke3NpZ25hbH0sIGNsb3NpbmcgJHthY3RpdmVDb25uZWN0aW9ucy5zaXplfSBhY3RpdmUgZ1JQQyBjb25uZWN0aW9uKHMpLi4uYCxcbiAgICApXG4gICAgZm9yIChjb25zdCBjdHJsIG9mIGFjdGl2ZUNvbm5lY3Rpb25zKSBjdHJsLmFib3J0KClcbiAgICBzZXJ2ZXIuY2xvc2UoKCkgPT4gcHJvY2Vzcy5leGl0KDApKVxuICB9XG5cbiAgcHJvY2Vzcy5vbignU0lHSU5UJywgKCkgPT4gc2h1dGRvd24oJ1NJR0lOVCcpKVxuICBwcm9jZXNzLm9uKCdTSUdURVJNJywgKCkgPT4gc2h1dGRvd24oJ1NJR1RFUk0nKSlcblxuICBjb25zdCBzZXJ2ZXIgPSBjcmVhdGVTZXJ2ZXIoYXN5bmMgKHJlcSwgcmVzKSA9PiB7XG4gICAgY29uc3QgbWV0aG9kID0gcmVxLm1ldGhvZCA/PyAnR0VUJ1xuICAgIGNvbnN0IHVybCA9IHJlcS51cmwgPz8gJy8nXG5cbiAgICAvLyAtLS0gSGVhbHRoIGNoZWNrIC0tLVxuICAgIGlmIChtZXRob2QgPT09ICdHRVQnICYmIHVybCA9PT0gJy9oZWFsdGgnKSB7XG4gICAgICByZXMud3JpdGVIZWFkKDIwMCwgeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nIH0pXG4gICAgICByZXMuZW5kKFxuICAgICAgICBKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgICAgc3RhdHVzOiAnb2snLFxuICAgICAgICAgIHJ1bm5lcnM6IHN0YXRlLnNuYXBzaG90KCkubGVuZ3RoLFxuICAgICAgICAgIGFjdGl2ZUNvbm5lY3Rpb25zOiBhY3RpdmVDb25uZWN0aW9ucy5zaXplLFxuICAgICAgICB9KSxcbiAgICAgIClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIC0tLSBTdGF0ZSBBUEkgKEpTT04pIC0tLVxuICAgIGlmIChtZXRob2QgPT09ICdHRVQnICYmIHVybCA9PT0gJy9hcGkvc3RhdGUnKSB7XG4gICAgICByZXMud3JpdGVIZWFkKDIwMCwge1xuICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFxuICAgICAgICAnQ2FjaGUtQ29udHJvbCc6ICduby1zdG9yZScsXG4gICAgICB9KVxuICAgICAgcmVzLmVuZChKU09OLnN0cmluZ2lmeShzdGF0ZS5zbmFwc2hvdCgpKSlcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIC0tLSBEYXNoYm9hcmQgKEhUTUwpIC0tLVxuICAgIGlmIChtZXRob2QgPT09ICdHRVQnICYmIHVybCA9PT0gJy9kYXNoYm9hcmQnKSB7XG4gICAgICByZXMud3JpdGVIZWFkKDIwMCwgeyAnQ29udGVudC1UeXBlJzogJ3RleHQvaHRtbDsgY2hhcnNldD11dGYtOCcgfSlcbiAgICAgIHJlcy5lbmQoZGFzaGJvYXJkSHRtbCgpKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gLS0tIEluZGV4IFR1cnRsZSAtLS1cbiAgICBpZiAobWV0aG9kID09PSAnR0VUJyAmJiB1cmwgPT09ICcvJykge1xuICAgICAgcmVzLndyaXRlSGVhZCgyMDAsIHsgJ0NvbnRlbnQtVHlwZSc6ICd0ZXh0L3R1cnRsZScgfSlcbiAgICAgIHJlcy5lbmQoaW5kZXhUdGwpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyAtLS0gV2hpdGVsaXN0ZWQgcHJvY2Vzc29yIGZpbGVzIC0tLVxuICAgIGlmIChtZXRob2QgPT09ICdHRVQnKSB7XG4gICAgICBjb25zdCByZXFQYXRoID0gdXJsLnN0YXJ0c1dpdGgoJy8nKSA/IHVybC5zbGljZSgxKSA6IHVybFxuICAgICAgY29uc3QgYWJzUGF0aCA9IHJlc29sdmUoY3dkLCByZXFQYXRoKVxuXG4gICAgICBpZiAoIXdoaXRlbGlzdC5oYXMoYWJzUGF0aCkpIHtcbiAgICAgICAgcmVzLndyaXRlSGVhZCg0MDMsIHsgJ0NvbnRlbnQtVHlwZSc6ICd0ZXh0L3BsYWluJyB9KVxuICAgICAgICByZXMuZW5kKCdGb3JiaWRkZW4nKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgbGV0IGNvbnRlbnQ6IHN0cmluZ1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29udGVudCA9IGF3YWl0IHJlYWRGaWxlKGFic1BhdGgsIHsgZW5jb2Rpbmc6ICd1dGY4JyB9KVxuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIHJlcy53cml0ZUhlYWQoNDA0LCB7ICdDb250ZW50LVR5cGUnOiAndGV4dC9wbGFpbicgfSlcbiAgICAgICAgcmVzLmVuZCgnTm90IGZvdW5kJylcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIHJlcy53cml0ZUhlYWQoMjAwLCB7ICdDb250ZW50LVR5cGUnOiAndGV4dC90dXJ0bGUnIH0pXG4gICAgICByZXMuZW5kKGNvbnRlbnQpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyAtLS0gQ29ubmVjdDogaW5zdGFudGlhdGUgYSBuZXcgcnVubmVyIC0tLVxuICAgIGlmIChtZXRob2QgPT09ICdQT1NUJyAmJiB1cmwgPT09ICcvY29ubmVjdCcpIHtcbiAgICAgIGxldCBib2R5OiBzdHJpbmdcbiAgICAgIHRyeSB7XG4gICAgICAgIGJvZHkgPSBhd2FpdCByZWFkQm9keShyZXEpXG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgcmVzLndyaXRlSGVhZCg0MDAsIHsgJ0NvbnRlbnQtVHlwZSc6ICd0ZXh0L3BsYWluJyB9KVxuICAgICAgICByZXMuZW5kKCdGYWlsZWQgdG8gcmVhZCByZXF1ZXN0IGJvZHknKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgbGV0IHBhcnNlZDogeyBob3N0Pzogc3RyaW5nOyB1cmk/OiBzdHJpbmcgfVxuICAgICAgdHJ5IHtcbiAgICAgICAgcGFyc2VkID0gSlNPTi5wYXJzZShib2R5KVxuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIHJlcy53cml0ZUhlYWQoNDAwLCB7ICdDb250ZW50LVR5cGUnOiAndGV4dC9wbGFpbicgfSlcbiAgICAgICAgcmVzLmVuZCgnSW52YWxpZCBKU09OJylcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHsgaG9zdCwgdXJpIH0gPSBwYXJzZWRcbiAgICAgIGlmICh0eXBlb2YgaG9zdCAhPT0gJ3N0cmluZycgfHwgdHlwZW9mIHVyaSAhPT0gJ3N0cmluZycpIHtcbiAgICAgICAgcmVzLndyaXRlSGVhZCg0MDAsIHsgJ0NvbnRlbnQtVHlwZSc6ICd0ZXh0L3BsYWluJyB9KVxuICAgICAgICByZXMuZW5kKCdNaXNzaW5nIGhvc3Qgb3IgdXJpJylcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHJ1bm5lcklkID0gc3RhdGUucmVnaXN0ZXJSdW5uZXIoaG9zdCwgdXJpKVxuICAgICAgY29uc3QgY3RybCA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKVxuICAgICAgYWN0aXZlQ29ubmVjdGlvbnMuYWRkKGN0cmwpXG5cbiAgICAgIHN0YXJ0KGhvc3QsIHVyaSwgYWJzQ29uZmlnLCBjdHJsLnNpZ25hbCwgc3RhdGUsIHJ1bm5lcklkKVxuICAgICAgICAuY2F0Y2goKGVycikgPT4ge1xuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ2dSUEMgY29ubmVjdGlvbiBlcnJvcjonLCBlcnIpXG4gICAgICAgICAgc3RhdGUubWFya0Vycm9yKHJ1bm5lcklkKVxuICAgICAgICB9KVxuICAgICAgICAuZmluYWxseSgoKSA9PiB7XG4gICAgICAgICAgYWN0aXZlQ29ubmVjdGlvbnMuZGVsZXRlKGN0cmwpXG4gICAgICAgICAgc3RhdGUuZGVyZWdpc3RlclJ1bm5lcihydW5uZXJJZClcbiAgICAgICAgfSlcblxuICAgICAgcmVzLndyaXRlSGVhZCgyMDIsIHtcbiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyxcbiAgICAgICAgTG9jYXRpb246ICcvZGFzaGJvYXJkJyxcbiAgICAgIH0pXG4gICAgICByZXMuZW5kKEpTT04uc3RyaW5naWZ5KHsgcnVubmVySWQgfSkpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICByZXMud3JpdGVIZWFkKDQwNCwgeyAnQ29udGVudC1UeXBlJzogJ3RleHQvcGxhaW4nIH0pXG4gICAgcmVzLmVuZCgnTm90IGZvdW5kJylcbiAgfSlcblxuICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSkgPT4ge1xuICAgIHNlcnZlci5saXN0ZW4ocG9ydCwgKCkgPT4ge1xuICAgICAgY29uc29sZS5sb2coYGpzLXJ1bm5lciBzZXJ2ZXIgbGlzdGVuaW5nIG9uIHBvcnQgJHtwb3J0fWApXG4gICAgICBjb25zb2xlLmxvZyhgICBEYXNoYm9hcmQ6IGh0dHA6Ly9sb2NhbGhvc3Q6JHtwb3J0fS9kYXNoYm9hcmRgKVxuICAgICAgY29uc29sZS5sb2coYCAgSGVhbHRoOiAgICBodHRwOi8vbG9jYWxob3N0OiR7cG9ydH0vaGVhbHRoYClcbiAgICAgIGNvbnNvbGUubG9nKGAgIFN0YXRlIEFQSTogaHR0cDovL2xvY2FsaG9zdDoke3BvcnR9L2FwaS9zdGF0ZWApXG4gICAgICByZXNvbHZlKClcbiAgICB9KVxuICB9KVxufVxuIl19
|
package/lib/state.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type RunnerStatus = 'connecting' | 'running' | 'done' | 'error';
|
|
2
|
+
export interface ChannelStats {
|
|
3
|
+
uri: string;
|
|
4
|
+
role: 'reader' | 'writer';
|
|
5
|
+
messageCount: number;
|
|
6
|
+
bytesTotal: number;
|
|
7
|
+
lastMessageAt: number | null;
|
|
8
|
+
latenciesMs: number[];
|
|
9
|
+
}
|
|
10
|
+
export interface RunnerStats {
|
|
11
|
+
id: string;
|
|
12
|
+
host: string;
|
|
13
|
+
uri: string;
|
|
14
|
+
connectedAt: number;
|
|
15
|
+
status: RunnerStatus;
|
|
16
|
+
grpcState: string;
|
|
17
|
+
channels: Record<string, ChannelStats>;
|
|
18
|
+
}
|
|
19
|
+
export interface ChannelTracker {
|
|
20
|
+
recordMessage(bytes: number, latencyMs?: number): void;
|
|
21
|
+
}
|
|
22
|
+
export declare class State {
|
|
23
|
+
private readonly runners;
|
|
24
|
+
private nextId;
|
|
25
|
+
registerRunner(host: string, uri: string): string;
|
|
26
|
+
setStatus(id: string, status: RunnerStatus): void;
|
|
27
|
+
setGrpcState(id: string, state: string): void;
|
|
28
|
+
deregisterRunner(id: string): void;
|
|
29
|
+
markError(id: string): void;
|
|
30
|
+
trackChannel(runnerId: string, uri: string, role: 'reader' | 'writer'): ChannelTracker;
|
|
31
|
+
snapshot(): RunnerStats[];
|
|
32
|
+
}
|
package/lib/state.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const MAX_LATENCY_SAMPLES = 100;
|
|
2
|
+
export class State {
|
|
3
|
+
runners = new Map();
|
|
4
|
+
nextId = 1;
|
|
5
|
+
registerRunner(host, uri) {
|
|
6
|
+
const id = String(this.nextId++);
|
|
7
|
+
this.runners.set(id, {
|
|
8
|
+
id,
|
|
9
|
+
host,
|
|
10
|
+
uri,
|
|
11
|
+
connectedAt: Date.now(),
|
|
12
|
+
status: 'connecting',
|
|
13
|
+
grpcState: 'IDLE',
|
|
14
|
+
channels: {},
|
|
15
|
+
});
|
|
16
|
+
return id;
|
|
17
|
+
}
|
|
18
|
+
setStatus(id, status) {
|
|
19
|
+
const r = this.runners.get(id);
|
|
20
|
+
if (r)
|
|
21
|
+
r.status = status;
|
|
22
|
+
}
|
|
23
|
+
setGrpcState(id, state) {
|
|
24
|
+
const r = this.runners.get(id);
|
|
25
|
+
if (r)
|
|
26
|
+
r.grpcState = state;
|
|
27
|
+
}
|
|
28
|
+
deregisterRunner(id) {
|
|
29
|
+
const r = this.runners.get(id);
|
|
30
|
+
if (r && r.status !== 'error')
|
|
31
|
+
r.status = 'done';
|
|
32
|
+
}
|
|
33
|
+
markError(id) {
|
|
34
|
+
const r = this.runners.get(id);
|
|
35
|
+
if (r)
|
|
36
|
+
r.status = 'error';
|
|
37
|
+
}
|
|
38
|
+
trackChannel(runnerId, uri, role) {
|
|
39
|
+
const runner = this.runners.get(runnerId);
|
|
40
|
+
if (!runner)
|
|
41
|
+
return { recordMessage() { } };
|
|
42
|
+
if (!runner.channels[uri]) {
|
|
43
|
+
runner.channels[uri] = {
|
|
44
|
+
uri,
|
|
45
|
+
role,
|
|
46
|
+
messageCount: 0,
|
|
47
|
+
bytesTotal: 0,
|
|
48
|
+
lastMessageAt: null,
|
|
49
|
+
latenciesMs: [],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const channel = runner.channels[uri];
|
|
53
|
+
return {
|
|
54
|
+
recordMessage(bytes, latencyMs) {
|
|
55
|
+
channel.messageCount++;
|
|
56
|
+
channel.bytesTotal += bytes;
|
|
57
|
+
channel.lastMessageAt = Date.now();
|
|
58
|
+
if (latencyMs !== undefined) {
|
|
59
|
+
channel.latenciesMs.push(latencyMs);
|
|
60
|
+
if (channel.latenciesMs.length > MAX_LATENCY_SAMPLES) {
|
|
61
|
+
channel.latenciesMs.shift();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
snapshot() {
|
|
68
|
+
return [...this.runners.values()];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc3RhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBMEJBLE1BQU0sbUJBQW1CLEdBQUcsR0FBRyxDQUFBO0FBRS9CLE1BQU0sT0FBTyxLQUFLO0lBQ0MsT0FBTyxHQUE2QixJQUFJLEdBQUcsRUFBRSxDQUFBO0lBQ3RELE1BQU0sR0FBRyxDQUFDLENBQUE7SUFFbEIsY0FBYyxDQUFDLElBQVksRUFBRSxHQUFXO1FBQ3RDLE1BQU0sRUFBRSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQTtRQUNoQyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUU7WUFDbkIsRUFBRTtZQUNGLElBQUk7WUFDSixHQUFHO1lBQ0gsV0FBVyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDdkIsTUFBTSxFQUFFLFlBQVk7WUFDcEIsU0FBUyxFQUFFLE1BQU07WUFDakIsUUFBUSxFQUFFLEVBQUU7U0FDYixDQUFDLENBQUE7UUFDRixPQUFPLEVBQUUsQ0FBQTtJQUNYLENBQUM7SUFFRCxTQUFTLENBQUMsRUFBVSxFQUFFLE1BQW9CO1FBQ3hDLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFBO1FBQzlCLElBQUksQ0FBQztZQUFFLENBQUMsQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFBO0lBQzFCLENBQUM7SUFFRCxZQUFZLENBQUMsRUFBVSxFQUFFLEtBQWE7UUFDcEMsTUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDOUIsSUFBSSxDQUFDO1lBQUUsQ0FBQyxDQUFDLFNBQVMsR0FBRyxLQUFLLENBQUE7SUFDNUIsQ0FBQztJQUVELGdCQUFnQixDQUFDLEVBQVU7UUFDekIsTUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDOUIsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLE1BQU0sS0FBSyxPQUFPO1lBQUUsQ0FBQyxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUE7SUFDbEQsQ0FBQztJQUVELFNBQVMsQ0FBQyxFQUFVO1FBQ2xCLE1BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFBO1FBQzlCLElBQUksQ0FBQztZQUFFLENBQUMsQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFBO0lBQzNCLENBQUM7SUFFRCxZQUFZLENBQ1YsUUFBZ0IsRUFDaEIsR0FBVyxFQUNYLElBQXlCO1FBRXpCLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFBO1FBQ3pDLElBQUksQ0FBQyxNQUFNO1lBQUUsT0FBTyxFQUFFLGFBQWEsS0FBSSxDQUFDLEVBQUUsQ0FBQTtRQUUxQyxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQzFCLE1BQU0sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEdBQUc7Z0JBQ3JCLEdBQUc7Z0JBQ0gsSUFBSTtnQkFDSixZQUFZLEVBQUUsQ0FBQztnQkFDZixVQUFVLEVBQUUsQ0FBQztnQkFDYixhQUFhLEVBQUUsSUFBSTtnQkFDbkIsV0FBVyxFQUFFLEVBQUU7YUFDaEIsQ0FBQTtRQUNILENBQUM7UUFFRCxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFBO1FBQ3BDLE9BQU87WUFDTCxhQUFhLENBQUMsS0FBYSxFQUFFLFNBQWtCO2dCQUM3QyxPQUFPLENBQUMsWUFBWSxFQUFFLENBQUE7Z0JBQ3RCLE9BQU8sQ0FBQyxVQUFVLElBQUksS0FBSyxDQUFBO2dCQUMzQixPQUFPLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQTtnQkFDbEMsSUFBSSxTQUFTLEtBQUssU0FBUyxFQUFFLENBQUM7b0JBQzVCLE9BQU8sQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFBO29CQUNuQyxJQUFJLE9BQU8sQ0FBQyxXQUFXLENBQUMsTUFBTSxHQUFHLG1CQUFtQixFQUFFLENBQUM7d0JBQ3JELE9BQU8sQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLENBQUE7b0JBQzdCLENBQUM7Z0JBQ0gsQ0FBQztZQUNILENBQUM7U0FDRixDQUFBO0lBQ0gsQ0FBQztJQUVELFFBQVE7UUFDTixPQUFPLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUE7SUFDbkMsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IHR5cGUgUnVubmVyU3RhdHVzID0gJ2Nvbm5lY3RpbmcnIHwgJ3J1bm5pbmcnIHwgJ2RvbmUnIHwgJ2Vycm9yJ1xuXG5leHBvcnQgaW50ZXJmYWNlIENoYW5uZWxTdGF0cyB7XG4gIHVyaTogc3RyaW5nXG4gIHJvbGU6ICdyZWFkZXInIHwgJ3dyaXRlcidcbiAgbWVzc2FnZUNvdW50OiBudW1iZXJcbiAgYnl0ZXNUb3RhbDogbnVtYmVyXG4gIGxhc3RNZXNzYWdlQXQ6IG51bWJlciB8IG51bGxcbiAgLyoqIExhc3QgTiByb3VuZC10cmlwIGxhdGVuY2llcyBpbiBtcyAod3JpdGVycyBvbmx5KSAqL1xuICBsYXRlbmNpZXNNczogbnVtYmVyW11cbn1cblxuZXhwb3J0IGludGVyZmFjZSBSdW5uZXJTdGF0cyB7XG4gIGlkOiBzdHJpbmdcbiAgaG9zdDogc3RyaW5nXG4gIHVyaTogc3RyaW5nXG4gIGNvbm5lY3RlZEF0OiBudW1iZXJcbiAgc3RhdHVzOiBSdW5uZXJTdGF0dXNcbiAgZ3JwY1N0YXRlOiBzdHJpbmdcbiAgY2hhbm5lbHM6IFJlY29yZDxzdHJpbmcsIENoYW5uZWxTdGF0cz5cbn1cblxuZXhwb3J0IGludGVyZmFjZSBDaGFubmVsVHJhY2tlciB7XG4gIHJlY29yZE1lc3NhZ2UoYnl0ZXM6IG51bWJlciwgbGF0ZW5jeU1zPzogbnVtYmVyKTogdm9pZFxufVxuXG5jb25zdCBNQVhfTEFURU5DWV9TQU1QTEVTID0gMTAwXG5cbmV4cG9ydCBjbGFzcyBTdGF0ZSB7XG4gIHByaXZhdGUgcmVhZG9ubHkgcnVubmVyczogTWFwPHN0cmluZywgUnVubmVyU3RhdHM+ID0gbmV3IE1hcCgpXG4gIHByaXZhdGUgbmV4dElkID0gMVxuXG4gIHJlZ2lzdGVyUnVubmVyKGhvc3Q6IHN0cmluZywgdXJpOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGNvbnN0IGlkID0gU3RyaW5nKHRoaXMubmV4dElkKyspXG4gICAgdGhpcy5ydW5uZXJzLnNldChpZCwge1xuICAgICAgaWQsXG4gICAgICBob3N0LFxuICAgICAgdXJpLFxuICAgICAgY29ubmVjdGVkQXQ6IERhdGUubm93KCksXG4gICAgICBzdGF0dXM6ICdjb25uZWN0aW5nJyxcbiAgICAgIGdycGNTdGF0ZTogJ0lETEUnLFxuICAgICAgY2hhbm5lbHM6IHt9LFxuICAgIH0pXG4gICAgcmV0dXJuIGlkXG4gIH1cblxuICBzZXRTdGF0dXMoaWQ6IHN0cmluZywgc3RhdHVzOiBSdW5uZXJTdGF0dXMpOiB2b2lkIHtcbiAgICBjb25zdCByID0gdGhpcy5ydW5uZXJzLmdldChpZClcbiAgICBpZiAocikgci5zdGF0dXMgPSBzdGF0dXNcbiAgfVxuXG4gIHNldEdycGNTdGF0ZShpZDogc3RyaW5nLCBzdGF0ZTogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3QgciA9IHRoaXMucnVubmVycy5nZXQoaWQpXG4gICAgaWYgKHIpIHIuZ3JwY1N0YXRlID0gc3RhdGVcbiAgfVxuXG4gIGRlcmVnaXN0ZXJSdW5uZXIoaWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IHIgPSB0aGlzLnJ1bm5lcnMuZ2V0KGlkKVxuICAgIGlmIChyICYmIHIuc3RhdHVzICE9PSAnZXJyb3InKSByLnN0YXR1cyA9ICdkb25lJ1xuICB9XG5cbiAgbWFya0Vycm9yKGlkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCByID0gdGhpcy5ydW5uZXJzLmdldChpZClcbiAgICBpZiAocikgci5zdGF0dXMgPSAnZXJyb3InXG4gIH1cblxuICB0cmFja0NoYW5uZWwoXG4gICAgcnVubmVySWQ6IHN0cmluZyxcbiAgICB1cmk6IHN0cmluZyxcbiAgICByb2xlOiAncmVhZGVyJyB8ICd3cml0ZXInLFxuICApOiBDaGFubmVsVHJhY2tlciB7XG4gICAgY29uc3QgcnVubmVyID0gdGhpcy5ydW5uZXJzLmdldChydW5uZXJJZClcbiAgICBpZiAoIXJ1bm5lcikgcmV0dXJuIHsgcmVjb3JkTWVzc2FnZSgpIHt9IH1cblxuICAgIGlmICghcnVubmVyLmNoYW5uZWxzW3VyaV0pIHtcbiAgICAgIHJ1bm5lci5jaGFubmVsc1t1cmldID0ge1xuICAgICAgICB1cmksXG4gICAgICAgIHJvbGUsXG4gICAgICAgIG1lc3NhZ2VDb3VudDogMCxcbiAgICAgICAgYnl0ZXNUb3RhbDogMCxcbiAgICAgICAgbGFzdE1lc3NhZ2VBdDogbnVsbCxcbiAgICAgICAgbGF0ZW5jaWVzTXM6IFtdLFxuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IGNoYW5uZWwgPSBydW5uZXIuY2hhbm5lbHNbdXJpXVxuICAgIHJldHVybiB7XG4gICAgICByZWNvcmRNZXNzYWdlKGJ5dGVzOiBudW1iZXIsIGxhdGVuY3lNcz86IG51bWJlcik6IHZvaWQge1xuICAgICAgICBjaGFubmVsLm1lc3NhZ2VDb3VudCsrXG4gICAgICAgIGNoYW5uZWwuYnl0ZXNUb3RhbCArPSBieXRlc1xuICAgICAgICBjaGFubmVsLmxhc3RNZXNzYWdlQXQgPSBEYXRlLm5vdygpXG4gICAgICAgIGlmIChsYXRlbmN5TXMgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIGNoYW5uZWwubGF0ZW5jaWVzTXMucHVzaChsYXRlbmN5TXMpXG4gICAgICAgICAgaWYgKGNoYW5uZWwubGF0ZW5jaWVzTXMubGVuZ3RoID4gTUFYX0xBVEVOQ1lfU0FNUExFUykge1xuICAgICAgICAgICAgY2hhbm5lbC5sYXRlbmNpZXNNcy5zaGlmdCgpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9LFxuICAgIH1cbiAgfVxuXG4gIHNuYXBzaG90KCk6IFJ1bm5lclN0YXRzW10ge1xuICAgIHJldHVybiBbLi4udGhpcy5ydW5uZXJzLnZhbHVlcygpXVxuICB9XG59XG4iXX0=
|