@workflow/world-local 5.0.0-beta.10 → 5.0.0-beta.12
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/dist/config.d.ts +23 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +19 -14
- package/dist/fs.d.ts +113 -2
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +321 -28
- package/dist/index.d.ts +16 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +97 -6
- package/dist/init.d.ts +91 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +268 -0
- package/dist/instrumentObject.d.ts +8 -0
- package/dist/instrumentObject.d.ts.map +1 -0
- package/dist/instrumentObject.js +66 -0
- package/dist/queue.d.ts +8 -1
- package/dist/queue.d.ts.map +1 -1
- package/dist/queue.js +195 -54
- package/dist/storage/events-storage.d.ts +7 -0
- package/dist/storage/events-storage.d.ts.map +1 -0
- package/dist/storage/events-storage.js +898 -0
- package/dist/storage/filters.d.ts +22 -0
- package/dist/storage/filters.d.ts.map +1 -0
- package/dist/storage/filters.js +33 -0
- package/dist/storage/helpers.d.ts +18 -0
- package/dist/storage/helpers.d.ts.map +1 -0
- package/dist/storage/helpers.js +44 -0
- package/dist/storage/hooks-storage.d.ts +12 -0
- package/dist/storage/hooks-storage.d.ts.map +1 -0
- package/dist/storage/hooks-storage.js +93 -0
- package/dist/storage/index.d.ts +21 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +30 -0
- package/dist/storage/legacy.d.ts +13 -0
- package/dist/storage/legacy.d.ts.map +1 -0
- package/dist/storage/legacy.js +78 -0
- package/dist/storage/runs-storage.d.ts +33 -0
- package/dist/storage/runs-storage.d.ts.map +1 -0
- package/dist/storage/runs-storage.js +130 -0
- package/dist/storage/steps-storage.d.ts +7 -0
- package/dist/storage/steps-storage.d.ts.map +1 -0
- package/dist/storage/steps-storage.js +52 -0
- package/dist/storage.d.ts +9 -2
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +9 -456
- package/dist/streamer.d.ts +4 -2
- package/dist/streamer.d.ts.map +1 -1
- package/dist/streamer.js +467 -107
- package/dist/telemetry.d.ts +28 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +71 -0
- package/dist/test-helpers.d.ts +54 -0
- package/dist/test-helpers.d.ts.map +1 -0
- package/dist/test-helpers.js +118 -0
- package/dist/util.js +1 -1
- package/package.json +12 -11
- package/dist/config.js.map +0 -1
- package/dist/fs.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/queue.js.map +0 -1
- package/dist/storage.js.map +0 -1
- package/dist/streamer.js.map +0 -1
- package/dist/util.js.map +0 -1
package/dist/streamer.js
CHANGED
|
@@ -1,131 +1,491 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { monotonicFactory } from 'ulid';
|
|
4
|
-
import {
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { assertSafeEntityId, readBuffer, readFirstByte, readJSONWithFallback, taggedPath, write, writeJSON, } from './fs.js';
|
|
5
7
|
// Create a monotonic ULID factory that ensures ULIDs are always increasing
|
|
6
8
|
// even when generated within the same millisecond
|
|
7
9
|
const monotonicUlid = monotonicFactory(() => Math.random());
|
|
10
|
+
// Schema for the run-to-streams mapping file
|
|
11
|
+
const RunStreamsSchema = z.object({
|
|
12
|
+
streams: z.array(z.string()),
|
|
13
|
+
});
|
|
14
|
+
const EOF_MARKER = 1;
|
|
15
|
+
function isEofByte(byte) {
|
|
16
|
+
return byte === EOF_MARKER;
|
|
17
|
+
}
|
|
8
18
|
export function serializeChunk(chunk) {
|
|
9
|
-
const eofByte = Buffer.from([chunk.eof ?
|
|
19
|
+
const eofByte = Buffer.from([chunk.eof ? EOF_MARKER : 0]);
|
|
10
20
|
return Buffer.concat([eofByte, chunk.chunk]);
|
|
11
21
|
}
|
|
22
|
+
/** Check only the EOF flag byte without copying chunk payload. */
|
|
23
|
+
export function isEofChunk(serialized) {
|
|
24
|
+
return isEofByte(serialized[0]);
|
|
25
|
+
}
|
|
12
26
|
export function deserializeChunk(serialized) {
|
|
13
|
-
const eof = serialized
|
|
14
|
-
|
|
27
|
+
const eof = isEofChunk(serialized);
|
|
28
|
+
// Create a copy instead of a view to prevent ArrayBuffer detachment
|
|
29
|
+
const chunk = Buffer.from(serialized.subarray(1));
|
|
15
30
|
return { eof, chunk };
|
|
16
31
|
}
|
|
17
|
-
|
|
32
|
+
async function listChunkEntries(chunksDir) {
|
|
33
|
+
try {
|
|
34
|
+
return await fs.readdir(chunksDir);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (error.code === 'ENOENT')
|
|
38
|
+
return [];
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function addChunkFilesByExtension(extMap, entries, sourceExtension, fileExtension = sourceExtension, include = () => true) {
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
if (!entry.endsWith(sourceExtension))
|
|
45
|
+
continue;
|
|
46
|
+
const file = entry.slice(0, -sourceExtension.length);
|
|
47
|
+
if (include(file))
|
|
48
|
+
extMap.set(file, fileExtension);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* List chunk files for a stream, sorted chronologically (ULID order).
|
|
53
|
+
* Returns both the sorted file names and a map of file → extension for
|
|
54
|
+
* resolving the full path. Handles tagged and legacy (.json) formats.
|
|
55
|
+
*/
|
|
56
|
+
async function listChunkFilesForStream(chunksDir, name, tag) {
|
|
57
|
+
// Name is used as a filename prefix below; validate it can't escape chunksDir.
|
|
58
|
+
assertSafeEntityId('streamName', name);
|
|
59
|
+
const entries = await listChunkEntries(chunksDir);
|
|
60
|
+
const extMap = new Map();
|
|
61
|
+
addChunkFilesByExtension(extMap, entries, '.json');
|
|
62
|
+
addChunkFilesByExtension(extMap, entries, '.bin', '.bin', tag ? (file) => !file.endsWith(`.${tag}`) : undefined);
|
|
63
|
+
if (tag) {
|
|
64
|
+
const taggedExtension = `.${tag}.bin`;
|
|
65
|
+
addChunkFilesByExtension(extMap, entries, taggedExtension);
|
|
66
|
+
}
|
|
67
|
+
const files = [...extMap.keys()]
|
|
68
|
+
.filter((file) => file.startsWith(`${name}-`))
|
|
69
|
+
.sort();
|
|
70
|
+
return { files, extMap };
|
|
71
|
+
}
|
|
72
|
+
export function createStreamer(basedir, tag) {
|
|
73
|
+
const tagSuffix = tag ? `.${tag}` : '';
|
|
18
74
|
const streamEmitter = new EventEmitter();
|
|
75
|
+
// Track which streams have already been registered for a run (in-memory cache)
|
|
76
|
+
const registeredStreams = new Set();
|
|
77
|
+
// Helper to record the runId <> streamId association
|
|
78
|
+
async function registerStreamForRun(runId, streamName) {
|
|
79
|
+
assertSafeEntityId('runId', runId);
|
|
80
|
+
assertSafeEntityId('streamName', streamName);
|
|
81
|
+
const cacheKey = `${runId}:${streamName}`;
|
|
82
|
+
if (registeredStreams.has(cacheKey)) {
|
|
83
|
+
return; // Already registered in this session
|
|
84
|
+
}
|
|
85
|
+
const runStreamsPath = taggedPath(basedir, 'streams/runs', runId, tag);
|
|
86
|
+
// Read existing streams for this run (try tagged first, fall back to untagged)
|
|
87
|
+
const existing = await readJSONWithFallback(basedir, 'streams/runs', runId, RunStreamsSchema, tag);
|
|
88
|
+
const streams = existing?.streams ?? [];
|
|
89
|
+
// Add stream if not already present
|
|
90
|
+
if (!streams.includes(streamName)) {
|
|
91
|
+
streams.push(streamName);
|
|
92
|
+
await writeJSON(runStreamsPath, { streams }, { overwrite: true });
|
|
93
|
+
}
|
|
94
|
+
registeredStreams.add(cacheKey);
|
|
95
|
+
}
|
|
96
|
+
// Helper to convert a chunk to a Buffer
|
|
97
|
+
function toBuffer(chunk) {
|
|
98
|
+
if (typeof chunk === 'string') {
|
|
99
|
+
return Buffer.from(new TextEncoder().encode(chunk));
|
|
100
|
+
}
|
|
101
|
+
else if (chunk instanceof Buffer) {
|
|
102
|
+
return chunk;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
return Buffer.from(chunk);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
19
108
|
return {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
// After disk reading is complete, deliver chunks immediately
|
|
74
|
-
controller.enqueue(event.chunkData);
|
|
75
|
-
}
|
|
109
|
+
streams: {
|
|
110
|
+
async write(_runId, name, chunk) {
|
|
111
|
+
// Generate ULID synchronously BEFORE any await to preserve call order.
|
|
112
|
+
// This ensures that chunks written in sequence maintain their order even
|
|
113
|
+
// when runId is a promise that multiple writes are waiting on.
|
|
114
|
+
const chunkId = `chnk_${monotonicUlid()}`;
|
|
115
|
+
// Await runId if it's a promise to ensure proper flushing
|
|
116
|
+
const runId = await _runId;
|
|
117
|
+
// Register this stream for the run
|
|
118
|
+
await registerStreamForRun(runId, name);
|
|
119
|
+
// Convert chunk to buffer for serialization
|
|
120
|
+
const chunkBuffer = toBuffer(chunk);
|
|
121
|
+
const serialized = serializeChunk({
|
|
122
|
+
chunk: chunkBuffer,
|
|
123
|
+
eof: false,
|
|
124
|
+
});
|
|
125
|
+
const chunkPath = path.join(basedir, 'streams', 'chunks', `${name}-${chunkId}${tagSuffix}.bin`);
|
|
126
|
+
await write(chunkPath, serialized);
|
|
127
|
+
// Emit real-time event with Uint8Array (create copy to prevent ArrayBuffer detachment)
|
|
128
|
+
const chunkData = Uint8Array.from(chunkBuffer);
|
|
129
|
+
streamEmitter.emit(`chunk:${name}`, {
|
|
130
|
+
streamName: name,
|
|
131
|
+
chunkData,
|
|
132
|
+
chunkId,
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
async writeMulti(_runId, name, chunks) {
|
|
136
|
+
if (chunks.length === 0)
|
|
137
|
+
return;
|
|
138
|
+
// Generate all ULIDs synchronously BEFORE any await to preserve call order.
|
|
139
|
+
// This ensures that chunks maintain their order even when runId is a promise.
|
|
140
|
+
const chunkIds = chunks.map(() => `chnk_${monotonicUlid()}`);
|
|
141
|
+
// Await runId if it's a promise
|
|
142
|
+
const runId = await _runId;
|
|
143
|
+
// Register this stream for the run
|
|
144
|
+
await registerStreamForRun(runId, name);
|
|
145
|
+
// Prepare chunk data for parallel writes
|
|
146
|
+
const chunkBuffers = chunks.map((chunk) => toBuffer(chunk));
|
|
147
|
+
// Write all chunks in parallel for efficiency, but track individual completion
|
|
148
|
+
const writePromises = chunkBuffers.map(async (chunkBuffer, i) => {
|
|
149
|
+
const chunkId = chunkIds[i];
|
|
150
|
+
const serialized = serializeChunk({
|
|
151
|
+
chunk: chunkBuffer,
|
|
152
|
+
eof: false,
|
|
153
|
+
});
|
|
154
|
+
const chunkPath = path.join(basedir, 'streams', 'chunks', `${name}-${chunkId}${tagSuffix}.bin`);
|
|
155
|
+
await write(chunkPath, serialized);
|
|
156
|
+
// Return data needed for event emission
|
|
157
|
+
return {
|
|
158
|
+
chunkId,
|
|
159
|
+
chunkData: Uint8Array.from(chunkBuffer),
|
|
76
160
|
};
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
161
|
+
});
|
|
162
|
+
// Emit events in order, waiting for each chunk's write to complete
|
|
163
|
+
// This ensures events are emitted in order while writes happen in parallel
|
|
164
|
+
for (const writePromise of writePromises) {
|
|
165
|
+
const { chunkId, chunkData } = await writePromise;
|
|
166
|
+
streamEmitter.emit(`chunk:${name}`, {
|
|
167
|
+
streamName: name,
|
|
168
|
+
chunkData,
|
|
169
|
+
chunkId,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
async close(_runId, name) {
|
|
174
|
+
// Generate ULID synchronously BEFORE any await to preserve call order.
|
|
175
|
+
const chunkId = `chnk_${monotonicUlid()}`;
|
|
176
|
+
// Await runId if it's a promise to ensure proper flushing
|
|
177
|
+
const runId = await _runId;
|
|
178
|
+
// Register this stream for the run (in case write wasn't called)
|
|
179
|
+
await registerStreamForRun(runId, name);
|
|
180
|
+
const chunkPath = path.join(basedir, 'streams', 'chunks', `${name}-${chunkId}${tagSuffix}.bin`);
|
|
181
|
+
await write(chunkPath, serializeChunk({ chunk: Buffer.from([]), eof: true }));
|
|
182
|
+
streamEmitter.emit(`close:${name}`, { streamName: name });
|
|
183
|
+
},
|
|
184
|
+
async list(runId) {
|
|
185
|
+
assertSafeEntityId('runId', runId);
|
|
186
|
+
const data = await readJSONWithFallback(basedir, 'streams/runs', runId, RunStreamsSchema, tag);
|
|
187
|
+
return data?.streams ?? [];
|
|
188
|
+
},
|
|
189
|
+
async getChunks(_runId, name, options) {
|
|
190
|
+
const limit = options?.limit ?? 100;
|
|
191
|
+
const chunksDir = path.join(basedir, 'streams', 'chunks');
|
|
192
|
+
const { files: chunkFiles, extMap: fileExtMap } = await listChunkFilesForStream(chunksDir, name, tag);
|
|
193
|
+
// Decode cursor
|
|
194
|
+
let startIndex = 0;
|
|
195
|
+
if (options?.cursor) {
|
|
196
|
+
try {
|
|
197
|
+
const decoded = JSON.parse(Buffer.from(options.cursor, 'base64').toString('utf-8'));
|
|
198
|
+
startIndex = decoded.i;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
startIndex = 0;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Walk from startIndex, reading only the files we need.
|
|
205
|
+
// Files before the cursor are skipped entirely.
|
|
206
|
+
let streamDone = false;
|
|
207
|
+
const resultChunks = [];
|
|
208
|
+
let dataIndex = 0; // running count of data (non-EOF) files seen
|
|
209
|
+
for (const file of chunkFiles) {
|
|
210
|
+
const ext = fileExtMap.get(file) ?? '.bin';
|
|
211
|
+
const filePath = path.join(chunksDir, `${file}${ext}`);
|
|
212
|
+
// Before the cursor: only need to check EOF (1 byte), skip content
|
|
213
|
+
if (dataIndex < startIndex) {
|
|
214
|
+
if (isEofByte(await readFirstByte(filePath))) {
|
|
215
|
+
streamDone = true;
|
|
105
216
|
break;
|
|
106
217
|
}
|
|
107
|
-
|
|
108
|
-
|
|
218
|
+
dataIndex++;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
// Collected enough data chunks — peek at the next file for EOF/hasMore
|
|
222
|
+
if (resultChunks.length >= limit) {
|
|
223
|
+
if (isEofByte(await readFirstByte(filePath))) {
|
|
224
|
+
streamDone = true;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
// More data files exist beyond this page
|
|
228
|
+
dataIndex++;
|
|
109
229
|
}
|
|
230
|
+
break;
|
|
110
231
|
}
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
controller.enqueue(buffered.chunkData);
|
|
232
|
+
// In the page window: deserialize fully
|
|
233
|
+
const chunk = deserializeChunk(await readBuffer(filePath));
|
|
234
|
+
if (chunk.eof) {
|
|
235
|
+
streamDone = true;
|
|
236
|
+
break;
|
|
117
237
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
238
|
+
resultChunks.push({
|
|
239
|
+
index: dataIndex,
|
|
240
|
+
data: Uint8Array.from(chunk.chunk),
|
|
241
|
+
});
|
|
242
|
+
dataIndex++;
|
|
243
|
+
}
|
|
244
|
+
// hasMore = we know there are data files beyond this page
|
|
245
|
+
const hasMore = !streamDone && dataIndex > startIndex + resultChunks.length;
|
|
246
|
+
const nextIndex = startIndex + resultChunks.length;
|
|
247
|
+
const nextCursor = hasMore
|
|
248
|
+
? Buffer.from(JSON.stringify({ i: nextIndex })).toString('base64')
|
|
249
|
+
: null;
|
|
250
|
+
return {
|
|
251
|
+
data: resultChunks,
|
|
252
|
+
cursor: nextCursor,
|
|
253
|
+
hasMore,
|
|
254
|
+
done: streamDone,
|
|
255
|
+
};
|
|
256
|
+
},
|
|
257
|
+
async getInfo(_runId, name) {
|
|
258
|
+
const chunksDir = path.join(basedir, 'streams', 'chunks');
|
|
259
|
+
const { files: chunkFiles, extMap: fileExtMap } = await listChunkFilesForStream(chunksDir, name, tag);
|
|
260
|
+
// Read only the EOF marker byte because metadata never needs payloads.
|
|
261
|
+
let streamDone = false;
|
|
262
|
+
let dataCount = 0;
|
|
263
|
+
for (const file of chunkFiles) {
|
|
264
|
+
const ext = fileExtMap.get(file) ?? '.bin';
|
|
265
|
+
if (isEofByte(await readFirstByte(path.join(chunksDir, `${file}${ext}`)))) {
|
|
266
|
+
streamDone = true;
|
|
267
|
+
break;
|
|
122
268
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
269
|
+
dataCount++;
|
|
270
|
+
}
|
|
271
|
+
return { tailIndex: dataCount - 1, done: streamDone };
|
|
272
|
+
},
|
|
273
|
+
async get(_runId, name, startIndex = 0) {
|
|
274
|
+
const chunksDir = path.join(basedir, 'streams', 'chunks');
|
|
275
|
+
let removeListeners = () => { };
|
|
276
|
+
let pollInterval = null;
|
|
277
|
+
return new ReadableStream({
|
|
278
|
+
async start(controller) {
|
|
279
|
+
// Track chunks delivered via events to prevent duplicates and maintain order.
|
|
280
|
+
const deliveredChunkIds = new Set();
|
|
281
|
+
// Buffer for chunks that arrive via events during disk reading
|
|
282
|
+
const bufferedEventChunks = [];
|
|
283
|
+
let isReadingFromDisk = true;
|
|
284
|
+
// Buffer close event if it arrives during disk reading
|
|
285
|
+
let pendingClose = false;
|
|
286
|
+
// Set when the controller is closed; guards against enqueue-after-close
|
|
287
|
+
// in the polling callback when closeListener fires mid-iteration.
|
|
288
|
+
let streamClosed = false;
|
|
289
|
+
const chunkListener = (event) => {
|
|
290
|
+
// Skip empty chunks to maintain consistency with disk reading behavior
|
|
291
|
+
if (event.chunkData.byteLength === 0) {
|
|
292
|
+
deliveredChunkIds.add(event.chunkId);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (isReadingFromDisk) {
|
|
296
|
+
deliveredChunkIds.add(event.chunkId);
|
|
297
|
+
// Buffer chunks that arrive during disk reading to maintain order
|
|
298
|
+
// Create a copy to prevent ArrayBuffer detachment when enqueued later
|
|
299
|
+
bufferedEventChunks.push({
|
|
300
|
+
chunkId: event.chunkId,
|
|
301
|
+
chunkData: Uint8Array.from(event.chunkData),
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
else if (!deliveredChunkIds.has(event.chunkId)) {
|
|
305
|
+
// Guard against duplicates: polling may have already claimed this
|
|
306
|
+
// chunk between its has() check and readBuffer() yield.
|
|
307
|
+
deliveredChunkIds.add(event.chunkId);
|
|
308
|
+
// After disk reading is complete, deliver chunks immediately
|
|
309
|
+
// Create a copy to prevent ArrayBuffer detachment
|
|
310
|
+
controller.enqueue(Uint8Array.from(event.chunkData));
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
const closeListener = () => {
|
|
314
|
+
// Buffer close event if disk reading is still in progress
|
|
315
|
+
if (isReadingFromDisk) {
|
|
316
|
+
pendingClose = true;
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
// Remove listeners before closing
|
|
320
|
+
streamClosed = true;
|
|
321
|
+
streamEmitter.off(`chunk:${name}`, chunkListener);
|
|
322
|
+
streamEmitter.off(`close:${name}`, closeListener);
|
|
323
|
+
if (pollInterval) {
|
|
324
|
+
clearInterval(pollInterval);
|
|
325
|
+
pollInterval = null;
|
|
326
|
+
}
|
|
327
|
+
try {
|
|
328
|
+
controller.close();
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
// Ignore if controller is already closed (e.g., from cancel() or EOF)
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
removeListeners = closeListener;
|
|
335
|
+
// Set up listeners FIRST to avoid missing events
|
|
336
|
+
streamEmitter.on(`chunk:${name}`, chunkListener);
|
|
337
|
+
streamEmitter.on(`close:${name}`, closeListener);
|
|
338
|
+
// Now load existing chunks from disk.
|
|
339
|
+
const { files: chunkFiles, extMap: fileExtMap } = await listChunkFilesForStream(chunksDir, name, tag);
|
|
340
|
+
// Resolve negative startIndex relative to the number of data chunks
|
|
341
|
+
// (excluding the trailing EOF marker chunk, if present).
|
|
342
|
+
let dataChunkCount = chunkFiles.length;
|
|
343
|
+
if (typeof startIndex === 'number' &&
|
|
344
|
+
startIndex < 0 &&
|
|
345
|
+
chunkFiles.length > 0) {
|
|
346
|
+
const lastFile = chunkFiles[chunkFiles.length - 1];
|
|
347
|
+
const lastExt = fileExtMap.get(lastFile) ?? '.bin';
|
|
348
|
+
if (isEofByte(await readFirstByte(path.join(chunksDir, `${lastFile}${lastExt}`)))) {
|
|
349
|
+
dataChunkCount--;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const resolvedStartIndex = typeof startIndex === 'number' && startIndex < 0
|
|
353
|
+
? Math.max(0, dataChunkCount + startIndex)
|
|
354
|
+
: startIndex;
|
|
355
|
+
// Process existing chunks, skipping any already delivered via events
|
|
356
|
+
let isComplete = false;
|
|
357
|
+
for (let i = resolvedStartIndex; i < chunkFiles.length; i++) {
|
|
358
|
+
const file = chunkFiles[i];
|
|
359
|
+
// Extract chunk ID from filename: "streamName-chunkId" or "streamName-chunkId.tag"
|
|
360
|
+
const rawChunkId = file.substring(name.length + 1);
|
|
361
|
+
// Strip tag suffix (e.g., "chnk_ULID.vitest-0" → "chnk_ULID")
|
|
362
|
+
const chunkId = tag
|
|
363
|
+
? rawChunkId.replace(`.${tag}`, '')
|
|
364
|
+
: rawChunkId;
|
|
365
|
+
// Skip if already delivered via event
|
|
366
|
+
if (deliveredChunkIds.has(chunkId)) {
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
const ext = fileExtMap.get(file) ?? '.bin';
|
|
370
|
+
const chunk = deserializeChunk(await readBuffer(path.join(chunksDir, `${file}${ext}`)));
|
|
371
|
+
if (chunk?.eof === true) {
|
|
372
|
+
isComplete = true;
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
// Track as handled so polling doesn't re-deliver
|
|
376
|
+
deliveredChunkIds.add(chunkId);
|
|
377
|
+
if (chunk.chunk.byteLength) {
|
|
378
|
+
// Create a copy to prevent ArrayBuffer detachment
|
|
379
|
+
controller.enqueue(Uint8Array.from(chunk.chunk));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// Finished reading from disk - now deliver buffered event chunks in chronological order
|
|
383
|
+
isReadingFromDisk = false;
|
|
384
|
+
// Sort buffered chunks by ULID (chronological order)
|
|
385
|
+
bufferedEventChunks.sort((a, b) => a.chunkId.localeCompare(b.chunkId));
|
|
386
|
+
for (const buffered of bufferedEventChunks) {
|
|
387
|
+
// Create a copy for defense in depth (already copied at storage, but be extra safe)
|
|
388
|
+
controller.enqueue(Uint8Array.from(buffered.chunkData));
|
|
389
|
+
}
|
|
390
|
+
if (isComplete) {
|
|
391
|
+
removeListeners();
|
|
392
|
+
try {
|
|
393
|
+
controller.close();
|
|
394
|
+
}
|
|
395
|
+
catch {
|
|
396
|
+
// Ignore if controller is already closed (e.g., from closeListener event)
|
|
397
|
+
}
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
// Process any pending close event that arrived during disk reading
|
|
401
|
+
if (pendingClose) {
|
|
402
|
+
streamEmitter.off(`chunk:${name}`, chunkListener);
|
|
403
|
+
streamEmitter.off(`close:${name}`, closeListener);
|
|
404
|
+
try {
|
|
405
|
+
controller.close();
|
|
406
|
+
}
|
|
407
|
+
catch {
|
|
408
|
+
// Ignore if controller is already closed
|
|
409
|
+
}
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
// Track pre-startIndex chunks so polling doesn't re-deliver them
|
|
413
|
+
for (let i = 0; i < resolvedStartIndex && i < chunkFiles.length; i++) {
|
|
414
|
+
const file = chunkFiles[i];
|
|
415
|
+
const rawChunkId = file.substring(name.length + 1);
|
|
416
|
+
const chunkId = tag
|
|
417
|
+
? rawChunkId.replace(`.${tag}`, '')
|
|
418
|
+
: rawChunkId;
|
|
419
|
+
deliveredChunkIds.add(chunkId);
|
|
420
|
+
}
|
|
421
|
+
// Start filesystem polling for cross-process streaming support.
|
|
422
|
+
// The EventEmitter only works in-process; when the writer is in a
|
|
423
|
+
// separate process (e.g. e2e test runner ↔ workbench app), polling
|
|
424
|
+
// the shared filesystem is the fallback delivery mechanism.
|
|
425
|
+
let isPolling = false;
|
|
426
|
+
pollInterval = setInterval(async () => {
|
|
427
|
+
if (isPolling)
|
|
428
|
+
return;
|
|
429
|
+
isPolling = true;
|
|
430
|
+
try {
|
|
431
|
+
const { files: currentFiles, extMap: currentExtMap } = await listChunkFilesForStream(chunksDir, name, tag);
|
|
432
|
+
for (const file of currentFiles) {
|
|
433
|
+
const rawChunkId = file.substring(name.length + 1);
|
|
434
|
+
const chunkId = tag
|
|
435
|
+
? rawChunkId.replace(`.${tag}`, '')
|
|
436
|
+
: rawChunkId;
|
|
437
|
+
if (deliveredChunkIds.has(chunkId))
|
|
438
|
+
continue;
|
|
439
|
+
deliveredChunkIds.add(chunkId);
|
|
440
|
+
const ext = currentExtMap.get(file) ?? '.bin';
|
|
441
|
+
const chunk = deserializeChunk(await readBuffer(path.join(chunksDir, `${file}${ext}`)));
|
|
442
|
+
if (chunk?.eof === true) {
|
|
443
|
+
streamClosed = true;
|
|
444
|
+
if (pollInterval) {
|
|
445
|
+
clearInterval(pollInterval);
|
|
446
|
+
pollInterval = null;
|
|
447
|
+
}
|
|
448
|
+
streamEmitter.off(`chunk:${name}`, chunkListener);
|
|
449
|
+
streamEmitter.off(`close:${name}`, closeListener);
|
|
450
|
+
try {
|
|
451
|
+
controller.close();
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
// Ignore if controller is already closed
|
|
455
|
+
}
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
// Guard against enqueue-after-close: closeListener may have
|
|
459
|
+
// fired between our readBuffer() yield and this point.
|
|
460
|
+
if (streamClosed)
|
|
461
|
+
return;
|
|
462
|
+
if (chunk.chunk.byteLength) {
|
|
463
|
+
controller.enqueue(Uint8Array.from(chunk.chunk));
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
catch (err) {
|
|
468
|
+
// Silently ignore transient filesystem errors (ENOENT, EACCES, etc.)
|
|
469
|
+
// Surface unexpected errors so bugs aren't hidden
|
|
470
|
+
if (!(err instanceof Error && 'code' in err)) {
|
|
471
|
+
console.error('[world-local] Unexpected polling error:', err);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
finally {
|
|
475
|
+
isPolling = false;
|
|
476
|
+
}
|
|
477
|
+
}, 100);
|
|
478
|
+
},
|
|
479
|
+
cancel() {
|
|
480
|
+
removeListeners();
|
|
481
|
+
if (pollInterval) {
|
|
482
|
+
clearInterval(pollInterval);
|
|
483
|
+
pollInterval = null;
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
});
|
|
487
|
+
},
|
|
128
488
|
},
|
|
129
489
|
};
|
|
130
490
|
}
|
|
131
|
-
//# sourceMappingURL=streamer.js.map
|
|
491
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RyZWFtZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc3RyZWFtZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUMzQyxPQUFPLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNsQyxPQUFPLElBQUksTUFBTSxXQUFXLENBQUM7QUFPN0IsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxDQUFDLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFDeEIsT0FBTyxFQUNMLGtCQUFrQixFQUNsQixVQUFVLEVBQ1YsYUFBYSxFQUNiLG9CQUFvQixFQUNwQixVQUFVLEVBQ1YsS0FBSyxFQUNMLFNBQVMsR0FDVixNQUFNLFNBQVMsQ0FBQztBQUVqQiwyRUFBMkU7QUFDM0Usa0RBQWtEO0FBQ2xELE1BQU0sYUFBYSxHQUFHLGdCQUFnQixDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0FBRTVELDZDQUE2QztBQUM3QyxNQUFNLGdCQUFnQixHQUFHLENBQUMsQ0FBQyxNQUFNLENBQUM7SUFDaEMsT0FBTyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDO0NBQzdCLENBQUMsQ0FBQztBQWNILE1BQU0sVUFBVSxHQUFHLENBQUMsQ0FBQztBQUVyQixTQUFTLFNBQVMsQ0FBQyxJQUF3QjtJQUN6QyxPQUFPLElBQUksS0FBSyxVQUFVLENBQUM7QUFDN0IsQ0FBQztBQUVELE1BQU0sVUFBVSxjQUFjLENBQUMsS0FBWTtJQUN6QyxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzFELE9BQU8sTUFBTSxD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LENBQUM7b0NBQVMsQ0FBQztnQ0FDVCxTQUFTLEdBQUcsS0FBSyxDQUFDOzRCQUNwQixDQUFDO3dCQUNILENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQztvQkFDVixDQUFDO29CQUVELE1BQU07d0JBQ0osZUFBZSxFQUFFLENBQUM7d0JBQ2xCLElBQUksWUFBWSxFQUFFLENBQUM7NEJBQ2pCLGFBQWEsQ0FBQyxZQUFZLENBQUMsQ0FBQzs0QkFDNUIsWUFBWSxHQUFHLElBQUksQ0FBQzt3QkFDdEIsQ0FBQztvQkFDSCxDQUFDO2lCQUNGLENBQUMsQ0FBQztZQUNMLENBQUM7U0FDRjtLQUNGLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRXZlbnRFbWl0dGVyIH0gZnJvbSAnbm9kZTpldmVudHMnO1xuaW1wb3J0IGZzIGZyb20gJ25vZGU6ZnMvcHJvbWlzZXMnO1xuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB0eXBlIHtcbiAgR2V0Q2h1bmtzT3B0aW9ucyxcbiAgU3RyZWFtQ2h1bmtzUmVzcG9uc2UsXG4gIFN0cmVhbWVyLFxuICBTdHJlYW1JbmZvUmVzcG9uc2UsXG59IGZyb20gJ0B3b3JrZmxvdy93b3JsZCc7XG5pbXBvcnQgeyBtb25vdG9uaWNGYWN0b3J5IH0gZnJvbSAndWxpZCc7XG5pbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcbmltcG9ydCB7XG4gIGFzc2VydFNhZmVFbnRpdHlJZCxcbiAgcmVhZEJ1ZmZlcixcbiAgcmVhZEZpcnN0Qnl0ZSxcbiAgcmVhZEpTT05XaXRoRmFsbGJhY2ssXG4gIHRhZ2dlZFBhdGgsXG4gIHdyaXRlLFxuICB3cml0ZUpTT04sXG59IGZyb20gJy4vZnMuanMnO1xuXG4vLyBDcmVhdGUgYSBtb25vdG9uaWMgVUxJRCBmYWN0b3J5IHRoYXQgZW5zdXJlcyBVTElEcyBhcmUgYWx3YXlzIGluY3JlYXNpbmdcbi8vIGV2ZW4gd2hlbiBnZW5lcmF0ZWQgd2l0aGluIHRoZSBzYW1lIG1pbGxpc2Vjb25kXG5jb25zdCBtb25vdG9uaWNVbGlkID0gbW9ub3RvbmljRmFjdG9yeSgoKSA9PiBNYXRoLnJhbmRvbSgpKTtcblxuLy8gU2NoZW1hIGZvciB0aGUgcnVuLXRvLXN0cmVhbXMgbWFwcGluZyBmaWxlXG5jb25zdCBSdW5TdHJlYW1zU2NoZW1hID0gei5vYmplY3Qoe1xuICBzdHJlYW1zOiB6LmFycmF5KHouc3RyaW5nKCkpLFxufSk7XG5cbi8qKlxuICogQSBjaHVuayBjb25zaXN0cyBvZiBhIGJvb2xlYW4gYGVvZmAgaW5kaWNhdGluZyBpZiBpdCdzIHRoZSBsYXN0IGNodW5rLFxuICogYW5kIGEgYGNodW5rYCB3aGljaCBpcyBhIEJ1ZmZlciBvZiBkYXRhLlxuICogVGhlIHNlcmlhbGl6ZWQgZm9ybWF0IGlzOlxuICogLSAxIGJ5dGUgZm9yIGBlb2ZgICgwIG9yIDEpXG4gKiAtIGFuZCB0aGUgcmVzdCBpcyB0aGUgY2h1bmsgZGF0YS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBDaHVuayB7XG4gIGVvZjogYm9vbGVhbjtcbiAgY2h1bms6IEJ1ZmZlcjtcbn1cblxuY29uc3QgRU9GX01BUktFUiA9IDE7XG5cbmZ1bmN0aW9uIGlzRW9mQnl0ZShieXRlOiBudW1iZXIgfCB1bmRlZmluZWQpOiBib29sZWFuIHtcbiAgcmV0dXJuIGJ5dGUgPT09IEVPRl9NQVJLRVI7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzZXJpYWxpemVDaHVuayhjaHVuazogQ2h1bmspIHtcbiAgY29uc3QgZW9mQnl0ZSA9IEJ1ZmZlci5mcm9tKFtjaHVuay5lb2YgPyBFT0ZfTUFSS0VSIDogMF0pO1xuICByZXR1cm4gQnVmZmVyLmNvbmNhdChbZW9mQnl0ZSwgY2h1bmsuY2h1bmtdKTtcbn1cblxuLyoqIENoZWNrIG9ubHkgdGhlIEVPRiBmbGFnIGJ5dGUgd2l0aG91dCBjb3B5aW5nIGNodW5rIHBheWxvYWQuICovXG5leHBvcnQgZnVuY3Rpb24gaXNFb2ZDaHVuayhzZXJpYWxpemVkOiBCdWZmZXIpOiBib29sZWFuIHtcbiAgcmV0dXJuIGlzRW9mQnl0ZShzZXJpYWxpemVkWzBdKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRlc2VyaWFsaXplQ2h1bmsoc2VyaWFsaXplZDogQnVmZmVyKSB7XG4gIGNvbnN0IGVvZiA9IGlzRW9mQ2h1bmsoc2VyaWFsaXplZCk7XG4gIC8vIENyZWF0ZSBhIGNvcHkgaW5zdGVhZCBvZiBhIHZpZXcgdG8gcHJldmVudCBBcnJheUJ1ZmZlciBkZXRhY2htZW50XG4gIGNvbnN0IGNodW5rID0gQnVmZmVyLmZyb20oc2VyaWFsaXplZC5zdWJhcnJheSgxKSk7XG4gIHJldHVybiB7IGVvZiwgY2h1bmsgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gbGlzdENodW5rRW50cmllcyhjaHVua3NEaXI6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gYXdhaXQgZnMucmVhZGRpcihjaHVua3NEaXIpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmICgoZXJyb3IgYXMgTm9kZUpTLkVycm5vRXhjZXB0aW9uKS5jb2RlID09PSAnRU5PRU5UJykgcmV0dXJuIFtdO1xuICAgIHRocm93IGVycm9yO1xuICB9XG59XG5cbmZ1bmN0aW9uIGFkZENodW5rRmlsZXNCeUV4dGVuc2lvbihcbiAgZXh0TWFwOiBNYXA8c3RyaW5nLCBzdHJpbmc+LFxuICBlbnRyaWVzOiBzdHJpbmdbXSxcbiAgc291cmNlRXh0ZW5zaW9uOiBzdHJpbmcsXG4gIGZpbGVFeHRlbnNpb24gPSBzb3VyY2VFeHRlbnNpb24sXG4gIGluY2x1ZGU6IChmaWxlOiBzdHJpbmcpID0+IGJvb2xlYW4gPSAoKSA9PiB0cnVlXG4pOiB2b2lkIHtcbiAgZm9yIChjb25zdCBlbnRyeSBvZiBlbnRyaWVzKSB7XG4gICAgaWYgKCFlbnRyeS5lbmRzV2l0aChzb3VyY2VFeHRlbnNpb24pKSBjb250aW51ZTtcbiAgICBjb25zdCBmaWxlID0gZW50cnkuc2xpY2UoMCwgLXNvdXJjZUV4dGVuc2lvbi5sZW5ndGgpO1xuICAgIGlmIChpbmNsdWRlKGZpbGUpKSBleHRNYXAuc2V0KGZpbGUsIGZpbGVFeHRlbnNpb24pO1xuICB9XG59XG5cbi8qKlxuICogTGlzdCBjaHVuayBmaWxlcyBmb3IgYSBzdHJlYW0sIHNvcnRlZCBjaHJvbm9sb2dpY2FsbHkgKFVMSUQgb3JkZXIpLlxuICogUmV0dXJucyBib3RoIHRoZSBzb3J0ZWQgZmlsZSBuYW1lcyBhbmQgYSBtYXAgb2YgZmlsZSDihpIgZXh0ZW5zaW9uIGZvclxuICogcmVzb2x2aW5nIHRoZSBmdWxsIHBhdGguIEhhbmRsZXMgdGFnZ2VkIGFuZCBsZWdhY3kgKC5qc29uKSBmb3JtYXRzLlxuICovXG5hc3luYyBmdW5jdGlvbiBsaXN0Q2h1bmtGaWxlc0ZvclN0cmVhbShcbiAgY2h1bmtzRGlyOiBzdHJpbmcsXG4gIG5hbWU6IHN0cmluZyxcbiAgdGFnPzogc3RyaW5nXG4pOiBQcm9taXNlPHsgZmlsZXM6IHN0cmluZ1tdOyBleHRNYXA6IE1hcDxzdHJpbmcsIHN0cmluZz4gfT4ge1xuICAvLyBOYW1lIGlzIHVzZWQgYXMgYSBmaWxlbmFtZSBwcmVmaXggYmVsb3c7IHZhbGlkYXRlIGl0IGNhbid0IGVzY2FwZSBjaHVua3NEaXIuXG4gIGFzc2VydFNhZmVFbnRpdHlJZCgnc3RyZWFtTmFtZScsIG5hbWUpO1xuICBjb25zdCBlbnRyaWVzID0gYXdhaXQgbGlzdENodW5rRW50cmllcyhjaHVua3NEaXIpO1xuICBjb25zdCBleHRNYXAgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nPigpO1xuICBhZGRDaHVua0ZpbGVzQnlFeHRlbnNpb24oZXh0TWFwLCBlbnRyaWVzLCAnLmpzb24nKTtcbiAgYWRkQ2h1bmtGaWxlc0J5RXh0ZW5zaW9uKFxuICAgIGV4dE1hcCxcbiAgICBlbnRyaWVzLFxuICAgICcuYmluJyxcbiAgICAnLmJpbicsXG4gICAgdGFnID8gKGZpbGUpID0+ICFmaWxlLmVuZHNXaXRoKGAuJHt0YWd9YCkgOiB1bmRlZmluZWRcbiAgKTtcblxuICBpZiAodGFnKSB7XG4gICAgY29uc3QgdGFnZ2VkRXh0ZW5zaW9uID0gYC4ke3RhZ30uYmluYDtcbiAgICBhZGRDaHVua0ZpbGVzQnlFeHRlbnNpb24oZXh0TWFwLCBlbnRyaWVzLCB0YWdnZWRFeHRlbnNpb24pO1xuICB9XG5cbiAgY29uc3QgZmlsZXMgPSBbLi4uZXh0TWFwLmtleXMoKV1cbiAgICAuZmlsdGVyKChmaWxlKSA9PiBmaWxlLnN0YXJ0c1dpdGgoYCR7bmFtZX0tYCkpXG4gICAgLnNvcnQoKTtcblxuICByZXR1cm4geyBmaWxlcywgZXh0TWFwIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVTdHJlYW1lcihiYXNlZGlyOiBzdHJpbmcsIHRhZz86IHN0cmluZyk6IFN0cmVhbWVyIHtcbiAgY29uc3QgdGFnU3VmZml4ID0gdGFnID8gYC4ke3RhZ31gIDogJyc7XG4gIGNvbnN0IHN0cmVhbUVtaXR0ZXIgPSBuZXcgRXZlbnRFbWl0dGVyPHtcbiAgICBba2V5OiBgY2h1bms6JHtzdHJpbmd9YF06IFtcbiAgICAgIHtcbiAgICAgICAgc3RyZWFtTmFtZTogc3RyaW5nO1xuICAgICAgICBjaHVua0RhdGE6IFVpbnQ4QXJyYXk7XG4gICAgICAgIGNodW5rSWQ6IHN0cmluZztcbiAgICAgIH0sXG4gICAgXTtcbiAgICBba2V5OiBgY2xvc2U6JHtzdHJpbmd9YF06IFtcbiAgICAgIHtcbiAgICAgICAgc3RyZWFtTmFtZTogc3RyaW5nO1xuICAgICAgfSxcbiAgICBdO1xuICB9PigpO1xuXG4gIC8vIFRyYWNrIHdoaWNoIHN0cmVhbXMgaGF2ZSBhbHJlYWR5IGJlZW4gcmVnaXN0ZXJlZCBmb3IgYSBydW4gKGluLW1lbW9yeSBjYWNoZSlcbiAgY29uc3QgcmVnaXN0ZXJlZFN0cmVhbXMgPSBuZXcgU2V0PHN0cmluZz4oKTtcblxuICAvLyBIZWxwZXIgdG8gcmVjb3JkIHRoZSBydW5JZCA8PiBzdHJlYW1JZCBhc3NvY2lhdGlvblxuICBhc3luYyBmdW5jdGlvbiByZWdpc3RlclN0cmVhbUZvclJ1bihcbiAgICBydW5JZDogc3RyaW5nLFxuICAgIHN0cmVhbU5hbWU6IHN0cmluZ1xuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhc3NlcnRTYWZlRW50aXR5SWQoJ3J1bklkJywgcnVuSWQpO1xuICAgIGFzc2VydFNhZmVFbnRpdHlJZCgnc3RyZWFtTmFtZScsIHN0cmVhbU5hbWUpO1xuICAgIGNvbnN0IGNhY2hlS2V5ID0gYCR7cnVuSWR9OiR7c3RyZWFtTmFtZX1gO1xuICAgIGlmIChyZWdpc3RlcmVkU3RyZWFtcy5oYXMoY2FjaGVLZXkpKSB7XG4gICAgICByZXR1cm47IC8vIEFscmVhZHkgcmVnaXN0ZXJlZCBpbiB0aGlzIHNlc3Npb25cbiAgICB9XG5cbiAgICBjb25zdCBydW5TdHJlYW1zUGF0aCA9IHRhZ2dlZFBhdGgoYmFzZWRpciwgJ3N0cmVhbXMvcnVucycsIHJ1bklkLCB0YWcpO1xuXG4gICAgLy8gUmVhZCBleGlzdGluZyBzdHJlYW1zIGZvciB0aGlzIHJ1biAodHJ5IHRhZ2dlZCBmaXJzdCwgZmFsbCBiYWNrIHRvIHVudGFnZ2VkKVxuICAgIGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgcmVhZEpTT05XaXRoRmFsbGJhY2soXG4gICAgICBiYXNlZGlyLFxuICAgICAgJ3N0cmVhbXMvcnVucycsXG4gICAgICBydW5JZCxcbiAgICAgIFJ1blN0cmVhbXNTY2hlbWEsXG4gICAgICB0YWdcbiAgICApO1xuICAgIGNvbnN0IHN0cmVhbXMgPSBleGlzdGluZz8uc3RyZWFtcyA/PyBbXTtcblxuICAgIC8vIEFkZCBzdHJlYW0gaWYgbm90IGFscmVhZHkgcHJlc2VudFxuICAgIGlmICghc3RyZWFtcy5pbmNsdWRlcyhzdHJlYW1OYW1lKSkge1xuICAgICAgc3RyZWFtcy5wdXNoKHN0cmVhbU5hbWUpO1xuICAgICAgYXdhaXQgd3JpdGVKU09OKHJ1blN0cmVhbXNQYXRoLCB7IHN0cmVhbXMgfSwgeyBvdmVyd3JpdGU6IHRydWUgfSk7XG4gICAgfVxuXG4gICAgcmVnaXN0ZXJlZFN0cmVhbXMuYWRkKGNhY2hlS2V5KTtcbiAgfVxuXG4gIC8vIEhlbHBlciB0byBjb252ZXJ0IGEgY2h1bmsgdG8gYSBCdWZmZXJcbiAgZnVuY3Rpb24gdG9CdWZmZXIoY2h1bms6IHN0cmluZyB8IFVpbnQ4QXJyYXkpOiBCdWZmZXIge1xuICAgIGlmICh0eXBlb2YgY2h1bmsgPT09ICdzdHJpbmcnKSB7XG4gICAgICByZXR1cm4gQnVmZmVyLmZyb20obmV3IFRleHRFbmNvZGVyKCkuZW5jb2RlKGNodW5rKSk7XG4gICAgfSBlbHNlIGlmIChjaHVuayBpbnN0YW5jZW9mIEJ1ZmZlcikge1xuICAgICAgcmV0dXJuIGNodW5rO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gQnVmZmVyLmZyb20oY2h1bmspO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB7XG4gICAgc3RyZWFtczoge1xuICAgICAgYXN5bmMgd3JpdGUoXG4gICAgICAgIF9ydW5JZDogc3RyaW5nIHwgUHJvbWlzZTxzdHJpbmc+LFxuICAgICAgICBuYW1lOiBzdHJpbmcsXG4gICAgICAgIGNodW5rOiBzdHJpbmcgfCBVaW50OEFycmF5XG4gICAgICApIHtcbiAgICAgICAgLy8gR2VuZXJhdGUgVUxJRCBzeW5jaHJvbm91c2x5IEJFRk9SRSBhbnkgYXdhaXQgdG8gcHJlc2VydmUgY2FsbCBvcmRlci5cbiAgICAgICAgLy8gVGhpcyBlbnN1cmVzIHRoYXQgY2h1bmtzIHdyaXR0ZW4gaW4gc2VxdWVuY2UgbWFpbnRhaW4gdGhlaXIgb3JkZXIgZXZlblxuICAgICAgICAvLyB3aGVuIHJ1bklkIGlzIGEgcHJvbWlzZSB0aGF0IG11bHRpcGxlIHdyaXRlcyBhcmUgd2FpdGluZyBvbi5cbiAgICAgICAgY29uc3QgY2h1bmtJZCA9IGBjaG5rXyR7bW9ub3RvbmljVWxpZCgpfWA7XG5cbiAgICAgICAgLy8gQXdhaXQgcnVuSWQgaWYgaXQncyBhIHByb21pc2UgdG8gZW5zdXJlIHByb3BlciBmbHVzaGluZ1xuICAgICAgICBjb25zdCBydW5JZCA9IGF3YWl0IF9ydW5JZDtcblxuICAgICAgICAvLyBSZWdpc3RlciB0aGlzIHN0cmVhbSBmb3IgdGhlIHJ1blxuICAgICAgICBhd2FpdCByZWdpc3RlclN0cmVhbUZvclJ1bihydW5JZCwgbmFtZSk7XG5cbiAgICAgICAgLy8gQ29udmVydCBjaHVuayB0byBidWZmZXIgZm9yIHNlcmlhbGl6YXRpb25cbiAgICAgICAgY29uc3QgY2h1bmtCdWZmZXIgPSB0b0J1ZmZlcihjaHVuayk7XG5cbiAgICAgICAgY29uc3Qgc2VyaWFsaXplZCA9IHNlcmlhbGl6ZUNodW5rKHtcbiAgICAgICAgICBjaHVuazogY2h1bmtCdWZmZXIsXG4gICAgICAgICAgZW9mOiBmYWxzZSxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgY29uc3QgY2h1bmtQYXRoID0gcGF0aC5qb2luKFxuICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgJ3N0cmVhbXMnLFxuICAgICAgICAgICdjaHVua3MnLFxuICAgICAgICAgIGAke25hbWV9LSR7Y2h1bmtJZH0ke3RhZ1N1ZmZpeH0uYmluYFxuICAgICAgICApO1xuXG4gICAgICAgIGF3YWl0IHdyaXRlKGNodW5rUGF0aCwgc2VyaWFsaXplZCk7XG5cbiAgICAgICAgLy8gRW1pdCByZWFsLXRpbWUgZXZlbnQgd2l0aCBVaW50OEFycmF5IChjcmVhdGUgY29weSB0byBwcmV2ZW50IEFycmF5QnVmZmVyIGRldGFjaG1lbnQpXG4gICAgICAgIGNvbnN0IGNodW5rRGF0YSA9IFVpbnQ4QXJyYXkuZnJvbShjaHVua0J1ZmZlcik7XG5cbiAgICAgICAgc3RyZWFtRW1pdHRlci5lbWl0KGBjaHVuazoke25hbWV9YCBhcyBjb25zdCwge1xuICAgICAgICAgIHN0cmVhbU5hbWU6IG5hbWUsXG4gICAgICAgICAgY2h1bmtEYXRhLFxuICAgICAgICAgIGNodW5rSWQsXG4gICAgICAgIH0pO1xuICAgICAgfSxcblxuICAgICAgYXN5bmMgd3JpdGVNdWx0aShcbiAgICAgICAgX3J1bklkOiBzdHJpbmcgfCBQcm9taXNlPHN0cmluZz4sXG4gICAgICAgIG5hbWU6IHN0cmluZyxcbiAgICAgICAgY2h1bmtzOiAoc3RyaW5nIHwgVWludDhBcnJheSlbXVxuICAgICAgKSB7XG4gICAgICAgIGlmIChjaHVua3MubGVuZ3RoID09PSAwKSByZXR1cm47XG5cbiAgICAgICAgLy8gR2VuZXJhdGUgYWxsIFVMSURzIHN5bmNocm9ub3VzbHkgQkVGT1JFIGFueSBhd2FpdCB0byBwcmVzZXJ2ZSBjYWxsIG9yZGVyLlxuICAgICAgICAvLyBUaGlzIGVuc3VyZXMgdGhhdCBjaHVua3MgbWFpbnRhaW4gdGhlaXIgb3JkZXIgZXZlbiB3aGVuIHJ1bklkIGlzIGEgcHJvbWlzZS5cbiAgICAgICAgY29uc3QgY2h1bmtJZHMgPSBjaHVua3MubWFwKCgpID0+IGBjaG5rXyR7bW9ub3RvbmljVWxpZCgpfWApO1xuXG4gICAgICAgIC8vIEF3YWl0IHJ1bklkIGlmIGl0J3MgYSBwcm9taXNlXG4gICAgICAgIGNvbnN0IHJ1bklkID0gYXdhaXQgX3J1bklkO1xuXG4gICAgICAgIC8vIFJlZ2lzdGVyIHRoaXMgc3RyZWFtIGZvciB0aGUgcnVuXG4gICAgICAgIGF3YWl0IHJlZ2lzdGVyU3RyZWFtRm9yUnVuKHJ1bklkLCBuYW1lKTtcblxuICAgICAgICAvLyBQcmVwYXJlIGNodW5rIGRhdGEgZm9yIHBhcmFsbGVsIHdyaXRlc1xuICAgICAgICBjb25zdCBjaHVua0J1ZmZlcnMgPSBjaHVua3MubWFwKChjaHVuaykgPT4gdG9CdWZmZXIoY2h1bmspKTtcblxuICAgICAgICAvLyBXcml0ZSBhbGwgY2h1bmtzIGluIHBhcmFsbGVsIGZvciBlZmZpY2llbmN5LCBidXQgdHJhY2sgaW5kaXZpZHVhbCBjb21wbGV0aW9uXG4gICAgICAgIGNvbnN0IHdyaXRlUHJvbWlzZXMgPSBjaHVua0J1ZmZlcnMubWFwKGFzeW5jIChjaHVua0J1ZmZlciwgaSkgPT4ge1xuICAgICAgICAgIGNvbnN0IGNodW5rSWQgPSBjaHVua0lkc1tpXTtcblxuICAgICAgICAgIGNvbnN0IHNlcmlhbGl6ZWQgPSBzZXJpYWxpemVDaHVuayh7XG4gICAgICAgICAgICBjaHVuazogY2h1bmtCdWZmZXIsXG4gICAgICAgICAgICBlb2Y6IGZhbHNlLFxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgY29uc3QgY2h1bmtQYXRoID0gcGF0aC5qb2luKFxuICAgICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAgICdzdHJlYW1zJyxcbiAgICAgICAgICAgICdjaHVua3MnLFxuICAgICAgICAgICAgYCR7bmFtZX0tJHtjaHVua0lkfSR7dGFnU3VmZml4fS5iaW5gXG4gICAgICAgICAgKTtcblxuICAgICAgICAgIGF3YWl0IHdyaXRlKGNodW5rUGF0aCwgc2VyaWFsaXplZCk7XG5cbiAgICAgICAgICAvLyBSZXR1cm4gZGF0YSBuZWVkZWQgZm9yIGV2ZW50IGVtaXNzaW9uXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGNodW5rSWQsXG4gICAgICAgICAgICBjaHVua0RhdGE6IFVpbnQ4QXJyYXkuZnJvbShjaHVua0J1ZmZlciksXG4gICAgICAgICAgfTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgLy8gRW1pdCBldmVudHMgaW4gb3JkZXIsIHdhaXRpbmcgZm9yIGVhY2ggY2h1bmsncyB3cml0ZSB0byBjb21wbGV0ZVxuICAgICAgICAvLyBUaGlzIGVuc3VyZXMgZXZlbnRzIGFyZSBlbWl0dGVkIGluIG9yZGVyIHdoaWxlIHdyaXRlcyBoYXBwZW4gaW4gcGFyYWxsZWxcbiAgICAgICAgZm9yIChjb25zdCB3cml0ZVByb21pc2Ugb2Ygd3JpdGVQcm9taXNlcykge1xuICAgICAgICAgIGNvbnN0IHsgY2h1bmtJZCwgY2h1bmtEYXRhIH0gPSBhd2FpdCB3cml0ZVByb21pc2U7XG5cbiAgICAgICAgICBzdHJlYW1FbWl0dGVyLmVtaXQoYGNodW5rOiR7bmFtZX1gIGFzIGNvbnN0LCB7XG4gICAgICAgICAgICBzdHJlYW1OYW1lOiBuYW1lLFxuICAgICAgICAgICAgY2h1bmtEYXRhLFxuICAgICAgICAgICAgY2h1bmtJZCxcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfSxcblxuICAgICAgYXN5bmMgY2xvc2UoX3J1bklkOiBzdHJpbmcgfCBQcm9taXNlPHN0cmluZz4sIG5hbWU6IHN0cmluZykge1xuICAgICAgICAvLyBHZW5lcmF0ZSBVTElEIHN5bmNocm9ub3VzbHkgQkVGT1JFIGFueSBhd2FpdCB0byBwcmVzZXJ2ZSBjYWxsIG9yZGVyLlxuICAgICAgICBjb25zdCBjaHVua0lkID0gYGNobmtfJHttb25vdG9uaWNVbGlkKCl9YDtcblxuICAgICAgICAvLyBBd2FpdCBydW5JZCBpZiBpdCdzIGEgcHJvbWlzZSB0byBlbnN1cmUgcHJvcGVyIGZsdXNoaW5nXG4gICAgICAgIGNvbnN0IHJ1bklkID0gYXdhaXQgX3J1bklkO1xuXG4gICAgICAgIC8vIFJlZ2lzdGVyIHRoaXMgc3RyZWFtIGZvciB0aGUgcnVuIChpbiBjYXNlIHdyaXRlIHdhc24ndCBjYWxsZWQpXG4gICAgICAgIGF3YWl0IHJlZ2lzdGVyU3RyZWFtRm9yUnVuKHJ1bklkLCBuYW1lKTtcbiAgICAgICAgY29uc3QgY2h1bmtQYXRoID0gcGF0aC5qb2luKFxuICAgICAgICAgIGJhc2VkaXIsXG4gICAgICAgICAgJ3N0cmVhbXMnLFxuICAgICAgICAgICdjaHVua3MnLFxuICAgICAgICAgIGAke25hbWV9LSR7Y2h1bmtJZH0ke3RhZ1N1ZmZpeH0uYmluYFxuICAgICAgICApO1xuXG4gICAgICAgIGF3YWl0IHdyaXRlKFxuICAgICAgICAgIGNodW5rUGF0aCxcbiAgICAgICAgICBzZXJpYWxpemVDaHVuayh7IGNodW5rOiBCdWZmZXIuZnJvbShbXSksIGVvZjogdHJ1ZSB9KVxuICAgICAgICApO1xuXG4gICAgICAgIHN0cmVhbUVtaXR0ZXIuZW1pdChgY2xvc2U6JHtuYW1lfWAgYXMgY29uc3QsIHsgc3RyZWFtTmFtZTogbmFtZSB9KTtcbiAgICAgIH0sXG5cbiAgICAgIGFzeW5jIGxpc3QocnVuSWQ6IHN0cmluZykge1xuICAgICAgICBhc3NlcnRTYWZlRW50aXR5SWQoJ3J1bklkJywgcnVuSWQpO1xuICAgICAgICBjb25zdCBkYXRhID0gYXdhaXQgcmVhZEpTT05XaXRoRmFsbGJhY2soXG4gICAgICAgICAgYmFzZWRpcixcbiAgICAgICAgICAnc3RyZWFtcy9ydW5zJyxcbiAgICAgICAgICBydW5JZCxcbiAgICAgICAgICBSdW5TdHJlYW1zU2NoZW1hLFxuICAgICAgICAgIHRhZ1xuICAgICAgICApO1xuICAgICAgICByZXR1cm4gZGF0YT8uc3RyZWFtcyA/PyBbXTtcbiAgICAgIH0sXG5cbiAgICAgIGFzeW5jIGdldENodW5rcyhcbiAgICAgICAgX3J1bklkOiBzdHJpbmcsXG4gICAgICAgIG5hbWU6IHN0cmluZyxcbiAgICAgICAgb3B0aW9ucz86IEdldENodW5rc09wdGlvbnNcbiAgICAgICk6IFByb21pc2U8U3RyZWFtQ2h1bmtzUmVzcG9uc2U+IHtcbiAgICAgICAgY29uc3QgbGltaXQgPSBvcHRpb25zPy5saW1pdCA/PyAxMDA7XG4gICAgICAgIGNvbnN0IGNodW5rc0RpciA9IHBhdGguam9pbihiYXNlZGlyLCAnc3RyZWFtcycsICdjaHVua3MnKTtcbiAgICAgICAgY29uc3QgeyBmaWxlczogY2h1bmtGaWxlcywgZXh0TWFwOiBmaWxlRXh0TWFwIH0gPVxuICAgICAgICAgIGF3YWl0IGxpc3RDaHVua0ZpbGVzRm9yU3RyZWFtKGNodW5rc0RpciwgbmFtZSwgdGFnKTtcblxuICAgICAgICAvLyBEZWNvZGUgY3Vyc29yXG4gICAgICAgIGxldCBzdGFydEluZGV4ID0gMDtcbiAgICAgICAgaWYgKG9wdGlvbnM/LmN1cnNvcikge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBkZWNvZGVkID0gSlNPTi5wYXJzZShcbiAgICAgICAgICAgICAgQnVmZmVyLmZyb20ob3B0aW9ucy5jdXJzb3IsICdiYXNlNjQnKS50b1N0cmluZygndXRmLTgnKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHN0YXJ0SW5kZXggPSBkZWNvZGVkLmk7XG4gICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICBzdGFydEluZGV4ID0gMDtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBXYWxrIGZyb20gc3RhcnRJbmRleCwgcmVhZGluZyBvbmx5IHRoZSBmaWxlcyB3ZSBuZWVkLlxuICAgICAgICAvLyBGaWxlcyBiZWZvcmUgdGhlIGN1cnNvciBhcmUgc2tpcHBlZCBlbnRpcmVseS5cbiAgICAgICAgbGV0IHN0cmVhbURvbmUgPSBmYWxzZTtcbiAgICAgICAgY29uc3QgcmVzdWx0Q2h1bmtzOiB7IGluZGV4OiBudW1iZXI7IGRhdGE6IFVpbnQ4QXJyYXkgfVtdID0gW107XG4gICAgICAgIGxldCBkYXRhSW5kZXggPSAwOyAvLyBydW5uaW5nIGNvdW50IG9mIGRhdGEgKG5vbi1FT0YpIGZpbGVzIHNlZW5cblxuICAgICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgY2h1bmtGaWxlcykge1xuICAgICAgICAgIGNvbnN0IGV4dCA9IGZpbGVFeHRNYXAuZ2V0KGZpbGUpID8/ICcuYmluJztcbiAgICAgICAgICBjb25zdCBmaWxlUGF0aCA9IHBhdGguam9pbihjaHVua3NEaXIsIGAke2ZpbGV9JHtleHR9YCk7XG5cbiAgICAgICAgICAvLyBCZWZvcmUgdGhlIGN1cnNvcjogb25seSBuZWVkIHRvIGNoZWNrIEVPRiAoMSBieXRlKSwgc2tpcCBjb250ZW50XG4gICAgICAgICAgaWYgKGRhdGFJbmRleCA8IHN0YXJ0SW5kZXgpIHtcbiAgICAgICAgICAgIGlmIChpc0VvZkJ5dGUoYXdhaXQgcmVhZEZpcnN0Qnl0ZShmaWxlUGF0aCkpKSB7XG4gICAgICAgICAgICAgIHN0cmVhbURvbmUgPSB0cnVlO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGRhdGFJbmRleCsrO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gQ29sbGVjdGVkIGVub3VnaCBkYXRhIGNodW5rcyDigJQgcGVlayBhdCB0aGUgbmV4dCBmaWxlIGZvciBFT0YvaGFzTW9yZVxuICAgICAgICAgIGlmIChyZXN1bHRDaHVua3MubGVuZ3RoID49IGxpbWl0KSB7XG4gICAgICAgICAgICBpZiAoaXNFb2ZCeXRlKGF3YWl0IHJlYWRGaXJzdEJ5dGUoZmlsZVBhdGgpKSkge1xuICAgICAgICAgICAgICBzdHJlYW1Eb25lID0gdHJ1ZTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIC8vIE1vcmUgZGF0YSBmaWxlcyBleGlzdCBiZXlvbmQgdGhpcyBwYWdlXG4gICAgICAgICAgICAgIGRhdGFJbmRleCsrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gSW4gdGhlIHBhZ2Ugd2luZG93OiBkZXNlcmlhbGl6ZSBmdWxseVxuICAgICAgICAgIGNvbnN0IGNodW5rID0gZGVzZXJpYWxpemVDaHVuayhhd2FpdCByZWFkQnVmZmVyKGZpbGVQYXRoKSk7XG4gICAgICAgICAgaWYgKGNodW5rLmVvZikge1xuICAgICAgICAgICAgc3RyZWFtRG9uZSA9IHRydWU7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICB9XG4gICAgICAgICAgcmVzdWx0Q2h1bmtzLnB1c2goe1xuICAgICAgICAgICAgaW5kZXg6IGRhdGFJbmRleCxcbiAgICAgICAgICAgIGRhdGE6IFVpbnQ4QXJyYXkuZnJvbShjaHVuay5jaHVuayksXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgZGF0YUluZGV4Kys7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBoYXNNb3JlID0gd2Uga25vdyB0aGVyZSBhcmUgZGF0YSBmaWxlcyBiZXlvbmQgdGhpcyBwYWdlXG4gICAgICAgIGNvbnN0IGhhc01vcmUgPVxuICAgICAgICAgICFzdHJlYW1Eb25lICYmIGRhdGFJbmRleCA+IHN0YXJ0SW5kZXggKyByZXN1bHRDaHVua3MubGVuZ3RoO1xuICAgICAgICBjb25zdCBuZXh0SW5kZXggPSBzdGFydEluZGV4ICsgcmVzdWx0Q2h1bmtzLmxlbmd0aDtcbiAgICAgICAgY29uc3QgbmV4dEN1cnNvciA9IGhhc01vcmVcbiAgICAgICAgICA/IEJ1ZmZlci5mcm9tKEpTT04uc3RyaW5naWZ5KHsgaTogbmV4dEluZGV4IH0pKS50b1N0cmluZygnYmFzZTY0JylcbiAgICAgICAgICA6IG51bGw7XG5cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBkYXRhOiByZXN1bHRDaHVua3MsXG4gICAgICAgICAgY3Vyc29yOiBuZXh0Q3Vyc29yLFxuICAgICAgICAgIGhhc01vcmUsXG4gICAgICAgICAgZG9uZTogc3RyZWFtRG9uZSxcbiAgICAgICAgfTtcbiAgICAgIH0sXG5cbiAgICAgIGFzeW5jIGdldEluZm8oX3J1bklkOiBzdHJpbmcsIG5hbWU6IHN0cmluZyk6IFByb21pc2U8U3RyZWFtSW5mb1Jlc3BvbnNlPiB7XG4gICAgICAgIGNvbnN0IGNodW5rc0RpciA9IHBhdGguam9pbihiYXNlZGlyLCAnc3RyZWFtcycsICdjaHVua3MnKTtcbiAgICAgICAgY29uc3QgeyBmaWxlczogY2h1bmtGaWxlcywgZXh0TWFwOiBmaWxlRXh0TWFwIH0gPVxuICAgICAgICAgIGF3YWl0IGxpc3RDaHVua0ZpbGVzRm9yU3RyZWFtKGNodW5rc0RpciwgbmFtZSwgdGFnKTtcblxuICAgICAgICAvLyBSZWFkIG9ubHkgdGhlIEVPRiBtYXJrZXIgYnl0ZSBiZWNhdXNlIG1ldGFkYXRhIG5ldmVyIG5lZWRzIHBheWxvYWRzLlxuICAgICAgICBsZXQgc3RyZWFtRG9uZSA9IGZhbHNlO1xuICAgICAgICBsZXQgZGF0YUNvdW50ID0gMDtcbiAgICAgICAgZm9yIChjb25zdCBmaWxlIG9mIGNodW5rRmlsZXMpIHtcbiAgICAgICAgICBjb25zdCBleHQgPSBmaWxlRXh0TWFwLmdldChmaWxlKSA/PyAnLmJpbic7XG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgaXNFb2ZCeXRlKFxuICAgICAgICAgICAgICBhd2FpdCByZWFkRmlyc3RCeXRlKHBhdGguam9pbihjaHVua3NEaXIsIGAke2ZpbGV9JHtleHR9YCkpXG4gICAgICAgICAgICApXG4gICAgICAgICAgKSB7XG4gICAgICAgICAgICBzdHJlYW1Eb25lID0gdHJ1ZTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgICBkYXRhQ291bnQrKztcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB7IHRhaWxJbmRleDogZGF0YUNvdW50IC0gMSwgZG9uZTogc3RyZWFtRG9uZSB9O1xuICAgICAgfSxcblxuICAgICAgYXN5bmMgZ2V0KF9ydW5JZDogc3RyaW5nLCBuYW1lOiBzdHJpbmcsIHN0YXJ0SW5kZXggPSAwKSB7XG4gICAgICAgIGNvbnN0IGNodW5rc0RpciA9IHBhdGguam9pbihiYXNlZGlyLCAnc3RyZWFtcycsICdjaHVua3MnKTtcbiAgICAgICAgbGV0IHJlbW92ZUxpc3RlbmVycyA9ICgpID0+IHt9O1xuICAgICAgICBsZXQgcG9sbEludGVydmFsOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRJbnRlcnZhbD4gfCBudWxsID0gbnVsbDtcblxuICAgICAgICByZXR1cm4gbmV3IFJlYWRhYmxlU3RyZWFtPFVpbnQ4QXJyYXk+KHtcbiAgICAgICAgICBhc3luYyBzdGFydChjb250cm9sbGVyKSB7XG4gICAgICAgICAgICAvLyBUcmFjayBjaHVua3MgZGVsaXZlcmVkIHZpYSBldmVudHMgdG8gcHJldmVudCBkdXBsaWNhdGVzIGFuZCBtYWludGFpbiBvcmRlci5cbiAgICAgICAgICAgIGNvbnN0IGRlbGl2ZXJlZENodW5rSWRzID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gICAgICAgICAgICAvLyBCdWZmZXIgZm9yIGNodW5rcyB0aGF0IGFycml2ZSB2aWEgZXZlbnRzIGR1cmluZyBkaXNrIHJlYWRpbmdcbiAgICAgICAgICAgIGNvbnN0IGJ1ZmZlcmVkRXZlbnRDaHVua3M6IEFycmF5PHtcbiAgICAgICAgICAgICAgY2h1bmtJZDogc3RyaW5nO1xuICAgICAgICAgICAgICBjaHVua0RhdGE6IFVpbnQ4QXJyYXk7XG4gICAgICAgICAgICB9PiA9IFtdO1xuICAgICAgICAgICAgbGV0IGlzUmVhZGluZ0Zyb21EaXNrID0gdHJ1ZTtcbiAgICAgICAgICAgIC8vIEJ1ZmZlciBjbG9zZSBldmVudCBpZiBpdCBhcnJpdmVzIGR1cmluZyBkaXNrIHJlYWRpbmdcbiAgICAgICAgICAgIGxldCBwZW5kaW5nQ2xvc2UgPSBmYWxzZTtcbiAgICAgICAgICAgIC8vIFNldCB3aGVuIHRoZSBjb250cm9sbGVyIGlzIGNsb3NlZDsgZ3VhcmRzIGFnYWluc3QgZW5xdWV1ZS1hZnRlci1jbG9zZVxuICAgICAgICAgICAgLy8gaW4gdGhlIHBvbGxpbmcgY2FsbGJhY2sgd2hlbiBjbG9zZUxpc3RlbmVyIGZpcmVzIG1pZC1pdGVyYXRpb24uXG4gICAgICAgICAgICBsZXQgc3RyZWFtQ2xvc2VkID0gZmFsc2U7XG5cbiAgICAgICAgICAgIGNvbnN0IGNodW5rTGlzdGVuZXIgPSAoZXZlbnQ6IHtcbiAgICAgICAgICAgICAgc3RyZWFtTmFtZTogc3RyaW5nO1xuICAgICAgICAgICAgICBjaHVua0RhdGE6IFVpbnQ4QXJyYXk7XG4gICAgICAgICAgICAgIGNodW5rSWQ6IHN0cmluZztcbiAgICAgICAgICAgIH0pID0+IHtcbiAgICAgICAgICAgICAgLy8gU2tpcCBlbXB0eSBjaHVua3MgdG8gbWFpbnRhaW4gY29uc2lzdGVuY3kgd2l0aCBkaXNrIHJlYWRpbmcgYmVoYXZpb3JcbiAgICAgICAgICAgICAgaWYgKGV2ZW50LmNodW5rRGF0YS5ieXRlTGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICAgICAgZGVsaXZlcmVkQ2h1bmtJZHMuYWRkKGV2ZW50LmNodW5rSWQpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGlmIChpc1JlYWRpbmdGcm9tRGlzaykge1xuICAgICAgICAgICAgICAgIGRlbGl2ZXJlZENodW5rSWRzLmFkZChldmVudC5jaHVua0lkKTtcbiAgICAgICAgICAgICAgICAvLyBCdWZmZXIgY2h1bmtzIHRoYXQgYXJyaXZlIGR1cmluZyBkaXNrIHJlYWRpbmcgdG8gbWFpbnRhaW4gb3JkZXJcbiAgICAgICAgICAgICAgICAvLyBDcmVhdGUgYSBjb3B5IHRvIHByZXZlbnQgQXJyYXlCdWZmZXIgZGV0YWNobWVudCB3aGVuIGVucXVldWVkIGxhdGVyXG4gICAgICAgICAgICAgICAgYnVmZmVyZWRFdmVudENodW5rcy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgIGNodW5rSWQ6IGV2ZW50LmNodW5rSWQsXG4gICAgICAgICAgICAgICAgICBjaHVua0RhdGE6IFVpbnQ4QXJyYXkuZnJvbShldmVudC5jaHVua0RhdGEpLFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICB9IGVsc2UgaWYgKCFkZWxpdmVyZWRDaHVua0lkcy5oYXMoZXZlbnQuY2h1bmtJZCkpIHtcbiAgICAgICAgICAgICAgICAvLyBHdWFyZCBhZ2FpbnN0IGR1cGxpY2F0ZXM6IHBvbGxpbmcgbWF5IGhhdmUgYWxyZWFkeSBjbGFpbWVkIHRoaXNcbiAgICAgICAgICAgICAgICAvLyBjaHVuayBiZXR3ZWVuIGl0cyBoYXMoKSBjaGVjayBhbmQgcmVhZEJ1ZmZlcigpIHlpZWxkLlxuICAgICAgICAgICAgICAgIGRlbGl2ZXJlZENodW5rSWRzLmFkZChldmVudC5jaHVua0lkKTtcbiAgICAgICAgICAgICAgICAvLyBBZnRlciBkaXNrIHJlYWRpbmcgaXMgY29tcGxldGUsIGRlbGl2ZXIgY2h1bmtzIGltbWVkaWF0ZWx5XG4gICAgICAgICAgICAgICAgLy8gQ3JlYXRlIGEgY29weSB0byBwcmV2ZW50IEFycmF5QnVmZmVyIGRldGFjaG1lbnRcbiAgICAgICAgICAgICAgICBjb250cm9sbGVyLmVucXVldWUoVWludDhBcnJheS5mcm9tKGV2ZW50LmNodW5rRGF0YSkpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICBjb25zdCBjbG9zZUxpc3RlbmVyID0gKCkgPT4ge1xuICAgICAgICAgICAgICAvLyBCdWZmZXIgY2xvc2UgZXZlbnQgaWYgZGlzayByZWFkaW5nIGlzIHN0aWxsIGluIHByb2dyZXNzXG4gICAgICAgICAgICAgIGlmIChpc1JlYWRpbmdGcm9tRGlzaykge1xuICAgICAgICAgICAgICAgIHBlbmRpbmdDbG9zZSA9IHRydWU7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIFJlbW92ZSBsaXN0ZW5lcnMgYmVmb3JlIGNsb3NpbmdcbiAgICAgICAgICAgICAgc3RyZWFtQ2xvc2VkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgc3RyZWFtRW1pdHRlci5vZmYoYGNodW5rOiR7bmFtZX1gIGFzIGNvbnN0LCBjaHVua0xpc3RlbmVyKTtcbiAgICAgICAgICAgICAgc3RyZWFtRW1pdHRlci5vZmYoYGNsb3NlOiR7bmFtZX1gIGFzIGNvbnN0LCBjbG9zZUxpc3RlbmVyKTtcbiAgICAgICAgICAgICAgaWYgKHBvbGxJbnRlcnZhbCkge1xuICAgICAgICAgICAgICAgIGNsZWFySW50ZXJ2YWwocG9sbEludGVydmFsKTtcbiAgICAgICAgICAgICAgICBwb2xsSW50ZXJ2YWwgPSBudWxsO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgY29udHJvbGxlci5jbG9zZSgpO1xuICAgICAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgICAgICAvLyBJZ25vcmUgaWYgY29udHJvbGxlciBpcyBhbHJlYWR5IGNsb3NlZCAoZS5nLiwgZnJvbSBjYW5jZWwoKSBvciBFT0YpXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByZW1vdmVMaXN0ZW5lcnMgPSBjbG9zZUxpc3RlbmVyO1xuXG4gICAgICAgICAgICAvLyBTZXQgdXAgbGlzdGVuZXJzIEZJUlNUIHRvIGF2b2lkIG1pc3NpbmcgZXZlbnRzXG4gICAgICAgICAgICBzdHJlYW1FbWl0dGVyLm9uKGBjaHVuazoke25hbWV9YCBhcyBjb25zdCwgY2h1bmtMaXN0ZW5lcik7XG4gICAgICAgICAgICBzdHJlYW1FbWl0dGVyLm9uKGBjbG9zZToke25hbWV9YCBhcyBjb25zdCwgY2xvc2VMaXN0ZW5lcik7XG5cbiAgICAgICAgICAgIC8vIE5vdyBsb2FkIGV4aXN0aW5nIGNodW5rcyBmcm9tIGRpc2suXG4gICAgICAgICAgICBjb25zdCB7IGZpbGVzOiBjaHVua0ZpbGVzLCBleHRNYXA6IGZpbGVFeHRNYXAgfSA9XG4gICAgICAgICAgICAgIGF3YWl0IGxpc3RDaHVua0ZpbGVzRm9yU3RyZWFtKGNodW5rc0RpciwgbmFtZSwgdGFnKTtcblxuICAgICAgICAgICAgLy8gUmVzb2x2ZSBuZWdhdGl2ZSBzdGFydEluZGV4IHJlbGF0aXZlIHRvIHRoZSBudW1iZXIgb2YgZGF0YSBjaHVua3NcbiAgICAgICAgICAgIC8vIChleGNsdWRpbmcgdGhlIHRyYWlsaW5nIEVPRiBtYXJrZXIgY2h1bmssIGlmIHByZXNlbnQpLlxuICAgICAgICAgICAgbGV0IGRhdGFDaHVua0NvdW50ID0gY2h1bmtGaWxlcy5sZW5ndGg7XG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgIHR5cGVvZiBzdGFydEluZGV4ID09PSAnbnVtYmVyJyAmJlxuICAgICAgICAgICAgICBzdGFydEluZGV4IDwgMCAmJlxuICAgICAgICAgICAgICBjaHVua0ZpbGVzLmxlbmd0aCA+IDBcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICBjb25zdCBsYXN0RmlsZSA9IGNodW5rRmlsZXNbY2h1bmtGaWxlcy5sZW5ndGggLSAxXTtcbiAgICAgICAgICAgICAgY29uc3QgbGFzdEV4dCA9IGZpbGVFeHRNYXAuZ2V0KGxhc3RGaWxlKSA/PyAnLmJpbic7XG4gICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICBpc0VvZkJ5dGUoXG4gICAgICAgICAgICAgICAgICBhd2FpdCByZWFkRmlyc3RCeXRlKFxuICAgICAgICAgICAgICAgICAgICBwYXRoLmpvaW4oY2h1bmtzRGlyLCBgJHtsYXN0RmlsZX0ke2xhc3RFeHR9YClcbiAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIGRhdGFDaHVua0NvdW50LS07XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHJlc29sdmVkU3RhcnRJbmRleCA9XG4gICAgICAgICAgICAgIHR5cGVvZiBzdGFydEluZGV4ID09PSAnbnVtYmVyJyAmJiBzdGFydEluZGV4IDwgMFxuICAgICAgICAgICAgICAgID8gTWF0aC5tYXgoMCwgZGF0YUNodW5rQ291bnQgKyBzdGFydEluZGV4KVxuICAgICAgICAgICAgICAgIDogc3RhcnRJbmRleDtcblxuICAgICAgICAgICAgLy8gUHJvY2VzcyBleGlzdGluZyBjaHVua3MsIHNraXBwaW5nIGFueSBhbHJlYWR5IGRlbGl2ZXJlZCB2aWEgZXZlbnRzXG4gICAgICAgICAgICBsZXQgaXNDb21wbGV0ZSA9IGZhbHNlO1xuICAgICAgICAgICAgZm9yIChsZXQgaSA9IHJlc29sdmVkU3RhcnRJbmRleDsgaSA8IGNodW5rRmlsZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgY29uc3QgZmlsZSA9IGNodW5rRmlsZXNbaV07XG4gICAgICAgICAgICAgIC8vIEV4dHJhY3QgY2h1bmsgSUQgZnJvbSBmaWxlbmFtZTogXCJzdHJlYW1OYW1lLWNodW5rSWRcIiBvciBcInN0cmVhbU5hbWUtY2h1bmtJZC50YWdcIlxuICAgICAgICAgICAgICBjb25zdCByYXdDaHVua0lkID0gZmlsZS5zdWJzdHJpbmcobmFtZS5sZW5ndGggKyAxKTtcbiAgICAgICAgICAgICAgLy8gU3RyaXAgdGFnIHN1ZmZpeCAoZS5nLiwgXCJjaG5rX1VMSUQudml0ZXN0LTBcIiDihpIgXCJjaG5rX1VMSURcIilcbiAgICAgICAgICAgICAgY29uc3QgY2h1bmtJZCA9IHRhZ1xuICAgICAgICAgICAgICAgID8gcmF3Q2h1bmtJZC5yZXBsYWNlKGAuJHt0YWd9YCwgJycpXG4gICAgICAgICAgICAgICAgOiByYXdDaHVua0lkO1xuXG4gICAgICAgICAgICAgIC8vIFNraXAgaWYgYWxyZWFkeSBkZWxpdmVyZWQgdmlhIGV2ZW50XG4gICAgICAgICAgICAgIGlmIChkZWxpdmVyZWRDaHVua0lkcy5oYXMoY2h1bmtJZCkpIHtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGNvbnN0IGV4dCA9IGZpbGVFeHRNYXAuZ2V0KGZpbGUpID8/ICcuYmluJztcbiAgICAgICAgICAgICAgY29uc3QgY2h1bmsgPSBkZXNlcmlhbGl6ZUNodW5rKFxuICAgICAgICAgICAgICAgIGF3YWl0IHJlYWRCdWZmZXIocGF0aC5qb2luKGNodW5rc0RpciwgYCR7ZmlsZX0ke2V4dH1gKSlcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgaWYgKGNodW5rPy5lb2YgPT09IHRydWUpIHtcbiAgICAgICAgICAgICAgICBpc0NvbXBsZXRlID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAvLyBUcmFjayBhcyBoYW5kbGVkIHNvIHBvbGxpbmcgZG9lc24ndCByZS1kZWxpdmVyXG4gICAgICAgICAgICAgIGRlbGl2ZXJlZENodW5rSWRzLmFkZChjaHVua0lkKTtcbiAgICAgICAgICAgICAgaWYgKGNodW5rLmNodW5rLmJ5dGVMZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAvLyBDcmVhdGUgYSBjb3B5IHRvIHByZXZlbnQgQXJyYXlCdWZmZXIgZGV0YWNobWVudFxuICAgICAgICAgICAgICAgIGNvbnRyb2xsZXIuZW5xdWV1ZShVaW50OEFycmF5LmZyb20oY2h1bmsuY2h1bmspKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBGaW5pc2hlZCByZWFkaW5nIGZyb20gZGlzayAtIG5vdyBkZWxpdmVyIGJ1ZmZlcmVkIGV2ZW50IGNodW5rcyBpbiBjaHJvbm9sb2dpY2FsIG9yZGVyXG4gICAgICAgICAgICBpc1JlYWRpbmdGcm9tRGlzayA9IGZhbHNlO1xuXG4gICAgICAgICAgICAvLyBTb3J0IGJ1ZmZlcmVkIGNodW5rcyBieSBVTElEIChjaHJvbm9sb2dpY2FsIG9yZGVyKVxuICAgICAgICAgICAgYnVmZmVyZWRFdmVudENodW5rcy5zb3J0KChhLCBiKSA9PlxuICAgICAgICAgICAgICBhLmNodW5rSWQubG9jYWxlQ29tcGFyZShiLmNodW5rSWQpXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgZm9yIChjb25zdCBidWZmZXJlZCBvZiBidWZmZXJlZEV2ZW50Q2h1bmtzKSB7XG4gICAgICAgICAgICAgIC8vIENyZWF0ZSBhIGNvcHkgZm9yIGRlZmVuc2UgaW4gZGVwdGggKGFscmVhZHkgY29waWVkIGF0IHN0b3JhZ2UsIGJ1dCBiZSBleHRyYSBzYWZlKVxuICAgICAgICAgICAgICBjb250cm9sbGVyLmVucXVldWUoVWludDhBcnJheS5mcm9tKGJ1ZmZlcmVkLmNodW5rRGF0YSkpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoaXNDb21wbGV0ZSkge1xuICAgICAgICAgICAgICByZW1vdmVMaXN0ZW5lcnMoKTtcbiAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBjb250cm9sbGVyLmNsb3NlKCk7XG4gICAgICAgICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgICAgICAgIC8vIElnbm9yZSBpZiBjb250cm9sbGVyIGlzIGFscmVhZHkgY2xvc2VkIChlLmcuLCBmcm9tIGNsb3NlTGlzdGVuZXIgZXZlbnQpXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBQcm9jZXNzIGFueSBwZW5kaW5nIGNsb3NlIGV2ZW50IHRoYXQgYXJyaXZlZCBkdXJpbmcgZGlzayByZWFkaW5nXG4gICAgICAgICAgICBpZiAocGVuZGluZ0Nsb3NlKSB7XG4gICAgICAgICAgICAgIHN0cmVhbUVtaXR0ZXIub2ZmKGBjaHVuazoke25hbWV9YCBhcyBjb25zdCwgY2h1bmtMaXN0ZW5lcik7XG4gICAgICAgICAgICAgIHN0cmVhbUVtaXR0ZXIub2ZmKGBjbG9zZToke25hbWV9YCBhcyBjb25zdCwgY2xvc2VMaXN0ZW5lcik7XG4gICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgY29udHJvbGxlci5jbG9zZSgpO1xuICAgICAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgICAgICAvLyBJZ25vcmUgaWYgY29udHJvbGxlciBpcyBhbHJlYWR5IGNsb3NlZFxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gVHJhY2sgcHJlLXN0YXJ0SW5kZXggY2h1bmtzIHNvIHBvbGxpbmcgZG9lc24ndCByZS1kZWxpdmVyIHRoZW1cbiAgICAgICAgICAgIGZvciAoXG4gICAgICAgICAgICAgIGxldCBpID0gMDtcbiAgICAgICAgICAgICAgaSA8IHJlc29sdmVkU3RhcnRJbmRleCAmJiBpIDwgY2h1bmtGaWxlcy5sZW5ndGg7XG4gICAgICAgICAgICAgIGkrK1xuICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGZpbGUgPSBjaHVua0ZpbGVzW2ldO1xuICAgICAgICAgICAgICBjb25zdCByYXdDaHVua0lkID0gZmlsZS5zdWJzdHJpbmcobmFtZS5sZW5ndGggKyAxKTtcbiAgICAgICAgICAgICAgY29uc3QgY2h1bmtJZCA9IHRhZ1xuICAgICAgICAgICAgICAgID8gcmF3Q2h1bmtJZC5yZXBsYWNlKGAuJHt0YWd9YCwgJycpXG4gICAgICAgICAgICAgICAgOiByYXdDaHVua0lkO1xuICAgICAgICAgICAgICBkZWxpdmVyZWRDaHVua0lkcy5hZGQoY2h1bmtJZCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIFN0YXJ0IGZpbGVzeXN0ZW0gcG9sbGluZyBmb3IgY3Jvc3MtcHJvY2VzcyBzdHJlYW1pbmcgc3VwcG9ydC5cbiAgICAgICAgICAgIC8vIFRoZSBFdmVudEVtaXR0ZXIgb25seSB3b3JrcyBpbi1wcm9jZXNzOyB3aGVuIHRoZSB3cml0ZXIgaXMgaW4gYVxuICAgICAgICAgICAgLy8gc2VwYXJhdGUgcHJvY2VzcyAoZS5nLiBlMmUgdGVzdCBydW5uZXIg4oaUIHdvcmtiZW5jaCBhcHApLCBwb2xsaW5nXG4gICAgICAgICAgICAvLyB0aGUgc2hhcmVkIGZpbGVzeXN0ZW0gaXMgdGhlIGZhbGxiYWNrIGRlbGl2ZXJ5IG1lY2hhbmlzbS5cbiAgICAgICAgICAgIGxldCBpc1BvbGxpbmcgPSBmYWxzZTtcbiAgICAgICAgICAgIHBvbGxJbnRlcnZhbCA9IHNldEludGVydmFsKGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgICAgaWYgKGlzUG9sbGluZykgcmV0dXJuO1xuICAgICAgICAgICAgICBpc1BvbGxpbmcgPSB0cnVlO1xuICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHsgZmlsZXM6IGN1cnJlbnRGaWxlcywgZXh0TWFwOiBjdXJyZW50RXh0TWFwIH0gPVxuICAgICAgICAgICAgICAgICAgYXdhaXQgbGlzdENodW5rRmlsZXNGb3JTdHJlYW0oY2h1bmtzRGlyLCBuYW1lLCB0YWcpO1xuXG4gICAgICAgICAgICAgICAgZm9yIChjb25zdCBmaWxlIG9mIGN1cnJlbnRGaWxlcykge1xuICAgICAgICAgICAgICAgICAgY29uc3QgcmF3Q2h1bmtJZCA9IGZpbGUuc3Vic3RyaW5nKG5hbWUubGVuZ3RoICsgMSk7XG4gICAgICAgICAgICAgICAgICBjb25zdCBjaHVua0lkID0gdGFnXG4gICAgICAgICAgICAgICAgICAgID8gcmF3Q2h1bmtJZC5yZXBsYWNlKGAuJHt0YWd9YCwgJycpXG4gICAgICAgICAgICAgICAgICAgIDogcmF3Q2h1bmtJZDtcblxuICAgICAgICAgICAgICAgICAgaWYgKGRlbGl2ZXJlZENodW5rSWRzLmhhcyhjaHVua0lkKSkgY29udGludWU7XG4gICAgICAgICAgICAgICAgICBkZWxpdmVyZWRDaHVua0lkcy5hZGQoY2h1bmtJZCk7XG5cbiAgICAgICAgICAgICAgICAgIGNvbnN0IGV4dCA9IGN1cnJlbnRFeHRNYXAuZ2V0KGZpbGUpID8/ICcuYmluJztcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGNodW5rID0gZGVzZXJpYWxpemVDaHVuayhcbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgcmVhZEJ1ZmZlcihwYXRoLmpvaW4oY2h1bmtzRGlyLCBgJHtmaWxlfSR7ZXh0fWApKVxuICAgICAgICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgICAgICAgaWYgKGNodW5rPy5lb2YgPT09IHRydWUpIHtcbiAgICAgICAgICAgICAgICAgICAgc3RyZWFtQ2xvc2VkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHBvbGxJbnRlcnZhbCkge1xuICAgICAgICAgICAgICAgICAgICAgIGNsZWFySW50ZXJ2YWwocG9sbEludGVydmFsKTtcbiAgICAgICAgICAgICAgICAgICAgICBwb2xsSW50ZXJ2YWwgPSBudWxsO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHN0cmVhbUVtaXR0ZXIub2ZmKGBjaHVuazoke25hbWV9YCBhcyBjb25zdCwgY2h1bmtMaXN0ZW5lcik7XG4gICAgICAgICAgICAgICAgICAgIHN0cmVhbUVtaXR0ZXIub2ZmKGBjbG9zZToke25hbWV9YCBhcyBjb25zdCwgY2xvc2VMaXN0ZW5lcik7XG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgY29udHJvbGxlci5jbG9zZSgpO1xuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgICAgICAgICAgICAvLyBJZ25vcmUgaWYgY29udHJvbGxlciBpcyBhbHJlYWR5IGNsb3NlZFxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgLy8gR3VhcmQgYWdhaW5zdCBlbnF1ZXVlLWFmdGVyLWNsb3NlOiBjbG9zZUxpc3RlbmVyIG1heSBoYXZlXG4gICAgICAgICAgICAgICAgICAvLyBmaXJlZCBiZXR3ZWVuIG91ciByZWFkQnVmZmVyKCkgeWllbGQgYW5kIHRoaXMgcG9pbnQuXG4gICAgICAgICAgICAgICAgICBpZiAoc3RyZWFtQ2xvc2VkKSByZXR1cm47XG5cbiAgICAgICAgICAgICAgICAgIGlmIChjaHVuay5jaHVuay5ieXRlTGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnRyb2xsZXIuZW5xdWV1ZShVaW50OEFycmF5LmZyb20oY2h1bmsuY2h1bmspKTtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH0gY2F0Y2ggKGVycjogdW5rbm93bikge1xuICAgICAgICAgICAgICAgIC8vIFNpbGVudGx5IGlnbm9yZSB0cmFuc2llbnQgZmlsZXN5c3RlbSBlcnJvcnMgKEVOT0VOVCwgRUFDQ0VTLCBldGMuKVxuICAgICAgICAgICAgICAgIC8vIFN1cmZhY2UgdW5leHBlY3RlZCBlcnJvcnMgc28gYnVncyBhcmVuJ3QgaGlkZGVuXG4gICAgICAgICAgICAgICAgaWYgKCEoZXJyIGluc3RhbmNlb2YgRXJyb3IgJiYgJ2NvZGUnIGluIGVycikpIHtcbiAgICAgICAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ1t3b3JsZC1sb2NhbF0gVW5leHBlY3RlZCBwb2xsaW5nIGVycm9yOicsIGVycik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9IGZpbmFsbHkge1xuICAgICAgICAgICAgICAgIGlzUG9sbGluZyA9IGZhbHNlO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LCAxMDApO1xuICAgICAgICAgIH0sXG5cbiAgICAgICAgICBjYW5jZWwoKSB7XG4gICAgICAgICAgICByZW1vdmVMaXN0ZW5lcnMoKTtcbiAgICAgICAgICAgIGlmIChwb2xsSW50ZXJ2YWwpIHtcbiAgICAgICAgICAgICAgY2xlYXJJbnRlcnZhbChwb2xsSW50ZXJ2YWwpO1xuICAgICAgICAgICAgICBwb2xsSW50ZXJ2YWwgPSBudWxsO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0sXG4gICAgICAgIH0pO1xuICAgICAgfSxcbiAgICB9LFxuICB9O1xufVxuIl19
|