@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.
Files changed (45) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/README.md +37 -1
  3. package/bin/runner.js +7 -1
  4. package/bin/server.js +13 -0
  5. package/examples/echo/.idea/echo.iml +9 -0
  6. package/examples/echo/.idea/misc.xml +6 -0
  7. package/examples/echo/.idea/modules.xml +8 -0
  8. package/examples/echo/.idea/vcs.xml +7 -0
  9. package/examples/echo/.swls/config.json +1 -0
  10. package/examples/echo/package-lock.json +27 -29
  11. package/examples/echo/pipeline.ttl +0 -1
  12. package/examples/echo/processors.ttl +1 -1
  13. package/examples/echo/remote_pipeline.ttl +18 -0
  14. package/examples/echo/server.ttl +5 -0
  15. package/examples/echo/untitled:/types/MyType.ttl +0 -0
  16. package/file:/home/silvius/Projects/mumo-pipeline/ldes/http_3A_2F_2Fdata.mumo.be_2Fstreams_2Fnodes_2Fdefault/root/index.trig +3 -0
  17. package/ldes/http_3A_2F_2Fdata.mumo.be_2Fstreams_2Fnodes_2Fdefault/root/index.trig +3 -0
  18. package/lib/client.d.ts +2 -1
  19. package/lib/client.js +70 -22
  20. package/lib/index.d.ts +2 -0
  21. package/lib/index.js +3 -1
  22. package/lib/jsonld.d.ts +17 -0
  23. package/lib/jsonld.js +135 -0
  24. package/lib/reader.d.ts +4 -1
  25. package/lib/reader.js +11 -3
  26. package/lib/runner.d.ts +6 -1
  27. package/lib/runner.js +43 -15
  28. package/lib/server.d.ts +9 -0
  29. package/lib/server.js +459 -0
  30. package/lib/state.d.ts +32 -0
  31. package/lib/state.js +71 -0
  32. package/lib/testUtils.d.ts +24 -0
  33. package/lib/testUtils.js +150 -0
  34. package/lib/tsconfig.tsbuildinfo +1 -1
  35. package/lib/writer.d.ts +5 -1
  36. package/lib/writer.js +26 -10
  37. package/minimal.ttl +99 -0
  38. package/package.json +12 -11
  39. package/src/client.ts +99 -24
  40. package/src/index.ts +2 -0
  41. package/src/reader.ts +11 -1
  42. package/src/runner.ts +58 -11
  43. package/src/server.ts +545 -0
  44. package/src/state.ts +105 -0
  45. 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,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
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=