@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.
Files changed (63) hide show
  1. package/dist/config.d.ts +23 -5
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +19 -14
  4. package/dist/fs.d.ts +113 -2
  5. package/dist/fs.d.ts.map +1 -1
  6. package/dist/fs.js +321 -28
  7. package/dist/index.d.ts +16 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +97 -6
  10. package/dist/init.d.ts +91 -0
  11. package/dist/init.d.ts.map +1 -0
  12. package/dist/init.js +268 -0
  13. package/dist/instrumentObject.d.ts +8 -0
  14. package/dist/instrumentObject.d.ts.map +1 -0
  15. package/dist/instrumentObject.js +66 -0
  16. package/dist/queue.d.ts +8 -1
  17. package/dist/queue.d.ts.map +1 -1
  18. package/dist/queue.js +195 -54
  19. package/dist/storage/events-storage.d.ts +7 -0
  20. package/dist/storage/events-storage.d.ts.map +1 -0
  21. package/dist/storage/events-storage.js +898 -0
  22. package/dist/storage/filters.d.ts +22 -0
  23. package/dist/storage/filters.d.ts.map +1 -0
  24. package/dist/storage/filters.js +33 -0
  25. package/dist/storage/helpers.d.ts +18 -0
  26. package/dist/storage/helpers.d.ts.map +1 -0
  27. package/dist/storage/helpers.js +44 -0
  28. package/dist/storage/hooks-storage.d.ts +12 -0
  29. package/dist/storage/hooks-storage.d.ts.map +1 -0
  30. package/dist/storage/hooks-storage.js +93 -0
  31. package/dist/storage/index.d.ts +21 -0
  32. package/dist/storage/index.d.ts.map +1 -0
  33. package/dist/storage/index.js +30 -0
  34. package/dist/storage/legacy.d.ts +13 -0
  35. package/dist/storage/legacy.d.ts.map +1 -0
  36. package/dist/storage/legacy.js +78 -0
  37. package/dist/storage/runs-storage.d.ts +33 -0
  38. package/dist/storage/runs-storage.d.ts.map +1 -0
  39. package/dist/storage/runs-storage.js +130 -0
  40. package/dist/storage/steps-storage.d.ts +7 -0
  41. package/dist/storage/steps-storage.d.ts.map +1 -0
  42. package/dist/storage/steps-storage.js +52 -0
  43. package/dist/storage.d.ts +9 -2
  44. package/dist/storage.d.ts.map +1 -1
  45. package/dist/storage.js +9 -456
  46. package/dist/streamer.d.ts +4 -2
  47. package/dist/streamer.d.ts.map +1 -1
  48. package/dist/streamer.js +467 -107
  49. package/dist/telemetry.d.ts +28 -0
  50. package/dist/telemetry.d.ts.map +1 -0
  51. package/dist/telemetry.js +71 -0
  52. package/dist/test-helpers.d.ts +54 -0
  53. package/dist/test-helpers.d.ts.map +1 -0
  54. package/dist/test-helpers.js +118 -0
  55. package/dist/util.js +1 -1
  56. package/package.json +12 -11
  57. package/dist/config.js.map +0 -1
  58. package/dist/fs.js.map +0 -1
  59. package/dist/index.js.map +0 -1
  60. package/dist/queue.js.map +0 -1
  61. package/dist/storage.js.map +0 -1
  62. package/dist/streamer.js.map +0 -1
  63. package/dist/util.js.map +0 -1
package/dist/fs.js CHANGED
@@ -1,16 +1,208 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { WorkflowAPIError } from '@workflow/errors';
4
- import { decodeTime, monotonicFactory } from 'ulid';
3
+ import { EntityConflictError, WorkflowWorldError } from '@workflow/errors';
4
+ import { monotonicFactory } from 'ulid';
5
5
  import { z } from 'zod';
6
6
  const ulid = monotonicFactory(() => Math.random());
7
- const Ulid = z.string().ulid();
8
- export function ulidToDate(maybeUlid) {
9
- const ulid = Ulid.safeParse(maybeUlid);
10
- if (!ulid.success) {
11
- return null;
7
+ /**
8
+ * Truncate a possibly-untrusted value for inclusion in an error message.
9
+ * Keeps error output actionable for developers in dev mode while limiting
10
+ * the amount of attacker-controlled data reflected back if a `world-local`
11
+ * backend is ever run in a context that surfaces these messages to clients.
12
+ */
13
+ function truncateForError(value) {
14
+ const s = typeof value === 'string' ? value : String(value);
15
+ const MAX = 48;
16
+ return s.length > MAX ? `${s.slice(0, MAX)}…` : s;
17
+ }
18
+ /**
19
+ * Thrown when a caller-supplied entity ID contains characters that would
20
+ * escape the storage root (path traversal) or otherwise produce an unsafe
21
+ * filename. Extends {@link WorkflowWorldError} so the platform's standard
22
+ * error-to-HTTP mapping handles it alongside other world-layer errors.
23
+ */
24
+ export class UnsafeEntityIdError extends WorkflowWorldError {
25
+ constructor(kind, value) {
26
+ super(`Unsafe ${kind} "${truncateForError(value)}": must not be empty, contain ".", "/", "\\", or null bytes`);
27
+ this.name = 'UnsafeEntityIdError';
28
+ }
29
+ static is(value) {
30
+ return value instanceof Error && value.name === 'UnsafeEntityIdError';
31
+ }
32
+ }
33
+ /**
34
+ * Validate that a string is safe to embed in a filesystem path as a single
35
+ * path component. Rejects values that are:
36
+ * - empty
37
+ * - starting with `.` (blocks `.`, `..`, `.locks`, `.tmp`, and other
38
+ * hidden or reserved filenames)
39
+ * - containing `.` (would collide with the `.tag` / `.json` suffix the
40
+ * tagging logic strips from filenames; see {@link stripTag} /
41
+ * {@link getObjectCreatedAt})
42
+ * - containing `/`, `\`, or a NUL byte
43
+ *
44
+ * This is the primary defense against path-traversal attacks where a
45
+ * request body supplies a `runId` / `stepId` / `correlationId` containing
46
+ * `../` sequences to read or write files outside the storage root.
47
+ * {@link resolveWithinBase} provides a belt-and-suspenders containment
48
+ * check at the point of `path.join` for defense in depth.
49
+ *
50
+ * @param kind - Human-readable label used in the error message (e.g. "runId")
51
+ * @param value - The value to validate; throws {@link UnsafeEntityIdError}
52
+ * if the value is not safe.
53
+ */
54
+ export function assertSafeEntityId(kind, value) {
55
+ if (value.length === 0 ||
56
+ value.startsWith('.') ||
57
+ value.includes('/') ||
58
+ value.includes('\\') ||
59
+ value.includes('\0') ||
60
+ value.includes('.')) {
61
+ throw new UnsafeEntityIdError(kind, value);
62
+ }
63
+ }
64
+ /**
65
+ * Join `basedir` with an entity-relative path and assert the result stays
66
+ * inside `basedir`. Complements {@link assertSafeEntityId}: per-entry-point
67
+ * validation is the primary defense, and this final check catches any path
68
+ * escape that slipped past (e.g. a new call site that forgot to validate,
69
+ * or an unusual character combination on a future filesystem). Throws
70
+ * {@link UnsafeEntityIdError} if the joined path escapes `basedir`.
71
+ */
72
+ export function resolveWithinBase(basedir, ...segments) {
73
+ const resolvedBase = path.resolve(basedir);
74
+ const joined = path.resolve(basedir, ...segments);
75
+ if (joined !== resolvedBase && !joined.startsWith(resolvedBase + path.sep)) {
76
+ throw new UnsafeEntityIdError('path', segments.join('/'));
77
+ }
78
+ return joined;
79
+ }
80
+ const isWindows = process.platform === 'win32';
81
+ /**
82
+ * Execute a filesystem operation with retry logic on Windows.
83
+ * On Windows, file operations can fail with EPERM/EBUSY/EACCES when files
84
+ * are briefly locked by another process or antivirus. This wrapper adds
85
+ * exponential backoff retry logic. On non-Windows platforms, executes directly.
86
+ */
87
+ async function withWindowsRetry(fn, maxRetries = 5) {
88
+ if (!isWindows)
89
+ return fn();
90
+ const retryableErrors = ['EPERM', 'EBUSY', 'EACCES'];
91
+ const baseDelayMs = 10;
92
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
93
+ try {
94
+ return await fn();
95
+ }
96
+ catch (error) {
97
+ const isRetryable = attempt < maxRetries && retryableErrors.includes(error.code);
98
+ if (!isRetryable)
99
+ throw error;
100
+ // Exponential backoff with jitter
101
+ const delay = baseDelayMs * 2 ** attempt + Math.random() * baseDelayMs;
102
+ await new Promise((resolve) => setTimeout(resolve, delay));
103
+ }
104
+ }
105
+ // TypeScript: unreachable, but satisfies return type
106
+ throw new Error('Retry loop exited unexpectedly');
107
+ }
108
+ // In-memory cache of created files to avoid expensive fs.access() calls
109
+ // This is safe because we only write once per file path (no overwrites without explicit flag)
110
+ const createdFilesCache = new Set();
111
+ /**
112
+ * Clear the created files cache. Useful for testing or when files are deleted externally.
113
+ */
114
+ export function clearCreatedFilesCache() {
115
+ createdFilesCache.clear();
116
+ }
117
+ export { ulidToDate } from '@workflow/world';
118
+ /**
119
+ * Regex matching a tag suffix on a fileId (after `.json` has been stripped).
120
+ * E.g., `wrun_ABC.vitest-0` → the `.vitest-0` part.
121
+ * Tags start with a letter and contain alphanumeric chars and hyphens.
122
+ * Entity IDs (ULIDs, step_N, etc.) never contain dots, so the first dot
123
+ * always marks the tag boundary.
124
+ */
125
+ const TAG_PATTERN = /\.[a-zA-Z][a-zA-Z0-9-]*$/;
126
+ /**
127
+ * Strip a tag suffix from a fileId.
128
+ * `wrun_ABC.vitest-0` → `wrun_ABC`
129
+ * `wrun_ABC` → `wrun_ABC` (no-op if no tag)
130
+ */
131
+ export function stripTag(fileId) {
132
+ return fileId.replace(TAG_PATTERN, '');
133
+ }
134
+ /**
135
+ * Check whether a fileId belongs to a specific tag.
136
+ * `wrun_ABC.vitest-0` belongs to `vitest-0`.
137
+ * Untagged fileIds never match a tag-scoped lookup.
138
+ */
139
+ export function hasTag(fileId, tag) {
140
+ return fileId.endsWith(`.${tag}`);
141
+ }
142
+ /**
143
+ * Build the file path for an entity, with optional tag embedded in the filename.
144
+ * `taggedPath('/data', 'runs', 'wrun_ABC', 'vitest-0')` → `/data/runs/wrun_ABC.vitest-0.json`
145
+ * `taggedPath('/data', 'runs', 'wrun_ABC')` → `/data/runs/wrun_ABC.json`
146
+ *
147
+ * The `fileId` and `tag` are validated with {@link assertSafeEntityId} and
148
+ * the result is containment-checked with {@link resolveWithinBase} to
149
+ * prevent path-traversal attacks when values are derived from untrusted
150
+ * request input.
151
+ */
152
+ export function taggedPath(basedir, entityDir, fileId, tag) {
153
+ assertSafeEntityId('fileId', fileId);
154
+ if (tag !== undefined)
155
+ assertSafeEntityId('tag', tag);
156
+ const filename = tag ? `${fileId}.${tag}.json` : `${fileId}.json`;
157
+ return resolveWithinBase(basedir, entityDir, filename);
158
+ }
159
+ /**
160
+ * Read a JSON entity with tagged fallback.
161
+ * When a tag is set, tries the tagged path first, then falls back to the
162
+ * untagged path (so a tagged world can read entities written without a tag).
163
+ */
164
+ export async function readJSONWithFallback(basedir, entityDir, fileId, schema, tag) {
165
+ assertSafeEntityId('fileId', fileId);
166
+ if (tag !== undefined)
167
+ assertSafeEntityId('tag', tag);
168
+ if (tag) {
169
+ const result = await readJSON(resolveWithinBase(basedir, entityDir, `${fileId}.${tag}.json`), schema);
170
+ if (result !== null)
171
+ return result;
172
+ }
173
+ return readJSON(resolveWithinBase(basedir, entityDir, `${fileId}.json`), schema);
174
+ }
175
+ /**
176
+ * List all filenames in a directory that have a specific tag.
177
+ * Returns full filenames (e.g., `wrun_ABC.vitest-0.json`).
178
+ */
179
+ export async function listTaggedFiles(dirPath, tag) {
180
+ const suffix = `.${tag}.json`;
181
+ try {
182
+ const files = await fs.readdir(dirPath);
183
+ return files.filter((f) => f.endsWith(suffix));
184
+ }
185
+ catch (error) {
186
+ if (error.code === 'ENOENT')
187
+ return [];
188
+ throw error;
189
+ }
190
+ }
191
+ /**
192
+ * List all filenames in a directory that have a specific tag and extension.
193
+ * Returns full filenames (e.g., `stream-chnk_ABC.vitest-0.bin`).
194
+ */
195
+ export async function listTaggedFilesByExtension(dirPath, tag, extension) {
196
+ const suffix = `.${tag}${extension}`;
197
+ try {
198
+ const files = await fs.readdir(dirPath);
199
+ return files.filter((f) => f.endsWith(suffix));
200
+ }
201
+ catch (error) {
202
+ if (error.code === 'ENOENT')
203
+ return [];
204
+ throw error;
12
205
  }
13
- return new Date(decodeTime(ulid.data));
14
206
  }
15
207
  export async function ensureDir(dirPath) {
16
208
  try {
@@ -20,14 +212,56 @@ export async function ensureDir(dirPath) {
20
212
  // Ignore if already exists
21
213
  }
22
214
  }
215
+ /**
216
+ * Custom JSON replacer that encodes Uint8Array as base64 strings.
217
+ * Format: { __type: 'Uint8Array', data: '<base64>' }
218
+ */
219
+ export function jsonReplacer(_key, value) {
220
+ if (value instanceof Uint8Array) {
221
+ return {
222
+ __type: 'Uint8Array',
223
+ data: Buffer.from(value).toString('base64'),
224
+ };
225
+ }
226
+ return value;
227
+ }
228
+ /**
229
+ * Custom JSON reviver that decodes base64 strings back to Uint8Array.
230
+ */
231
+ export function jsonReviver(_key, value) {
232
+ if (value !== null &&
233
+ typeof value === 'object' &&
234
+ value.__type === 'Uint8Array' &&
235
+ typeof value.data === 'string') {
236
+ return new Uint8Array(Buffer.from(value.data, 'base64'));
237
+ }
238
+ return value;
239
+ }
23
240
  export async function writeJSON(filePath, data, opts) {
24
- return write(filePath, JSON.stringify(data, null, 2), opts);
241
+ return write(filePath, JSON.stringify(data, jsonReplacer, 2), opts);
25
242
  }
243
+ /**
244
+ * Writes data to a file using atomic write-rename pattern.
245
+ *
246
+ * Note: While this function uses temp files to avoid partial writes,
247
+ * it does not provide protection against concurrent writes from multiple
248
+ * processes. In a multi-writer scenario, the last writer wins.
249
+ * For production use with multiple writers, consider using a proper
250
+ * database or locking mechanism.
251
+ */
26
252
  export async function write(filePath, data, opts) {
27
253
  if (!opts?.overwrite) {
254
+ // Fast path: check in-memory cache first to avoid expensive fs.access() calls
255
+ // This provides significant performance improvement when creating many files
256
+ if (createdFilesCache.has(filePath)) {
257
+ throw new EntityConflictError(`File ${filePath} already exists and 'overwrite' is false`);
258
+ }
259
+ // Slow path: check filesystem for files created before this process started
28
260
  try {
29
261
  await fs.access(filePath);
30
- throw new WorkflowAPIError(`File ${filePath} already exists and 'overwrite' is false`, { status: 409 });
262
+ // File exists on disk, add to cache for future checks
263
+ createdFilesCache.add(filePath);
264
+ throw new EntityConflictError(`File ${filePath} already exists and 'overwrite' is false`);
31
265
  }
32
266
  catch (error) {
33
267
  // If file doesn't exist (ENOENT), continue with write
@@ -37,20 +271,27 @@ export async function write(filePath, data, opts) {
37
271
  }
38
272
  }
39
273
  const tempPath = `${filePath}.tmp.${ulid()}`;
274
+ let tempFileCreated = false;
40
275
  try {
41
276
  await ensureDir(path.dirname(filePath));
42
277
  await fs.writeFile(tempPath, data);
43
- await fs.rename(tempPath, filePath);
278
+ tempFileCreated = true;
279
+ await withWindowsRetry(() => fs.rename(tempPath, filePath));
280
+ // Track this file in cache so future writes know it exists
281
+ createdFilesCache.add(filePath);
44
282
  }
45
283
  catch (error) {
46
- await fs.unlink(tempPath).catch(() => { });
284
+ // Only try to clean up temp file if it was actually created
285
+ if (tempFileCreated) {
286
+ await withWindowsRetry(() => fs.unlink(tempPath), 3).catch(() => { });
287
+ }
47
288
  throw error;
48
289
  }
49
290
  }
50
291
  export async function readJSON(filePath, decoder) {
51
292
  try {
52
293
  const content = await fs.readFile(filePath, 'utf-8');
53
- return decoder.parse(JSON.parse(content));
294
+ return decoder.parse(JSON.parse(content, jsonReviver));
54
295
  }
55
296
  catch (error) {
56
297
  if (error.code === 'ENOENT')
@@ -62,6 +303,17 @@ export async function readBuffer(filePath) {
62
303
  const content = await fs.readFile(filePath);
63
304
  return content;
64
305
  }
306
+ export async function readFirstByte(filePath) {
307
+ const file = await fs.open(filePath, 'r');
308
+ try {
309
+ const byte = Buffer.allocUnsafe(1);
310
+ const { bytesRead } = await file.read(byte, 0, 1, 0);
311
+ return bytesRead === 0 ? undefined : byte[0];
312
+ }
313
+ finally {
314
+ await file.close();
315
+ }
316
+ }
65
317
  export async function deleteJSON(filePath) {
66
318
  try {
67
319
  await fs.unlink(filePath);
@@ -71,12 +323,33 @@ export async function deleteJSON(filePath) {
71
323
  throw error;
72
324
  }
73
325
  }
326
+ /**
327
+ * Atomically create a file using O_CREAT | O_EXCL flags.
328
+ * Returns true if the file was created, false if it already exists.
329
+ * This is atomic at the OS level, safe for concurrent access.
330
+ */
331
+ export async function writeExclusive(filePath, data) {
332
+ await ensureDir(path.dirname(filePath));
333
+ try {
334
+ await fs.writeFile(filePath, data, { flag: 'wx' });
335
+ return true;
336
+ }
337
+ catch (error) {
338
+ if (error.code === 'EEXIST') {
339
+ return false;
340
+ }
341
+ throw error;
342
+ }
343
+ }
74
344
  export async function listJSONFiles(dirPath) {
345
+ return listFilesByExtension(dirPath, '.json');
346
+ }
347
+ export async function listFilesByExtension(dirPath, extension) {
75
348
  try {
76
349
  const files = await fs.readdir(dirPath);
77
350
  return files
78
- .filter((f) => f.endsWith('.json'))
79
- .map((f) => f.replace('.json', ''));
351
+ .filter((f) => f.endsWith(extension))
352
+ .map((f) => f.slice(0, -extension.length));
80
353
  }
81
354
  catch (error) {
82
355
  if (error.code === 'ENOENT')
@@ -97,18 +370,29 @@ function createCursor(timestamp, id) {
97
370
  return id ? `${timestamp.toISOString()}|${id}` : timestamp.toISOString();
98
371
  }
99
372
  export async function paginatedFileSystemQuery(config) {
100
- const { directory, schema, filePrefix, filter, sortOrder = 'desc', limit = 20, cursor, getCreatedAt, getId, } = config;
373
+ const { directory, schema, filePrefix, fileIdFilter, filter, sortOrder = 'desc', limit = 20, cursor, getCreatedAt, getId, } = config;
374
+ // Validate filePrefix (typically `${runId}-`) so request-derived prefixes
375
+ // consistently reject unsafe characters. filePrefix is only used below to
376
+ // filter readdir() results by prefix — it doesn't participate in path
377
+ // construction — but keeping the validation rule uniform across the
378
+ // storage layer avoids special cases and catches bad values earlier.
379
+ if (filePrefix !== undefined) {
380
+ assertSafeEntityId('filePrefix', filePrefix);
381
+ }
101
382
  // 1. Get all JSON files in directory
102
383
  const fileIds = await listJSONFiles(directory);
103
384
  // 2. Filter by prefix if provided
104
385
  const relevantFileIds = filePrefix
105
386
  ? fileIds.filter((fileId) => fileId.startsWith(filePrefix))
106
387
  : fileIds;
388
+ const filteredFileIds = fileIdFilter
389
+ ? relevantFileIds.filter(fileIdFilter)
390
+ : relevantFileIds;
107
391
  // 3. ULID Optimization: Filter by cursor using filename timestamps before loading JSON
108
392
  const parsedCursor = parseCursor(cursor);
109
- let candidateFileIds = relevantFileIds;
393
+ let candidateFileIds = filteredFileIds;
110
394
  if (parsedCursor) {
111
- candidateFileIds = relevantFileIds.filter((fileId) => {
395
+ candidateFileIds = filteredFileIds.filter((fileId) => {
112
396
  const filenameDate = getCreatedAt(`${fileId}.json`);
113
397
  if (filenameDate) {
114
398
  // Use filename timestamp for cursor filtering
@@ -130,21 +414,30 @@ export async function paginatedFileSystemQuery(config) {
130
414
  : fileTime > cursorTime;
131
415
  }
132
416
  }
133
- // Skip files where we can't extract timestamp - no optimization benefit
134
- return false;
135
- });
136
- }
137
- else {
138
- // Even without cursor, skip files where getCreatedAt returns null for consistency
139
- candidateFileIds = relevantFileIds.filter((fileId) => {
140
- return getCreatedAt(`${fileId}.json`) !== null;
417
+ // Can't extract timestamp from filename (e.g., steps use sequential IDs).
418
+ // Include the file and defer to JSON-based filtering below.
419
+ return true;
141
420
  });
142
421
  }
143
422
  // 4. Load files individually and collect valid items
144
423
  const validItems = [];
145
424
  for (const fileId of candidateFileIds) {
146
425
  const filePath = path.join(directory, `${fileId}.json`);
147
- const item = await readJSON(filePath, schema);
426
+ let item = null;
427
+ try {
428
+ item = await readJSON(filePath, schema);
429
+ }
430
+ catch (error) {
431
+ // We don't expect zod errors to happen, but if the JSON does get malformed,
432
+ // we skip the item. Preferably, we'd have a way to mark items as malformed,
433
+ // so that the UI can display them as such, with richer messaging. In the meantime,
434
+ // we just log a warning and skip the item.
435
+ if (error instanceof z.ZodError) {
436
+ console.warn(`Skipping item ${fileId} due to malformed JSON: ${error.message}`);
437
+ continue;
438
+ }
439
+ throw error;
440
+ }
148
441
  if (item) {
149
442
  // Apply custom filter early if provided
150
443
  if (filter && !filter(item))
@@ -207,4 +500,4 @@ export async function paginatedFileSystemQuery(config) {
207
500
  hasMore,
208
501
  };
209
502
  }
210
- //# sourceMappingURL=fs.js.map
503
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsSUFBSSxFQUFFLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDekMsT0FBTyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQzdCLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRTNFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN4QyxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBRXhCLE1BQU0sSUFBSSxHQUFHLGdCQUFnQixDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO0FBRW5EOzs7OztHQUtHO0FBQ0gsU0FBUyxnQkFBZ0IsQ0FBQyxLQUFjO0lBQ3RDLE1BQU0sQ0FBQyxHQUFHLE9BQU8sS0FBSyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDNUQsTUFBTSxHQUFHLEdBQUcsRUFBRSxDQUFDO0lBQ2YsT0FBTyxDQUFDLENBQUMsTUFBTSxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsTUFBTSxPQUFPLG1CQUFvQixTQUFRLGtCQUFrQjtJQUN6RCxZQUFZLElBQVksRUFBRSxLQUFhO1FBQ3JDLEtBQUssQ0FDSCxVQUFVLElBQUksS0FBSyxnQkFBZ0IsQ0FBQyxLQUFLLENBQUMsNkRBQTZELENBQ3hHLENBQUM7UUFDRixJQUFJLENBQUMsSUFBSSxHQUFHLHFCQUFxQixDQUFDO0lBQ3BDLENBQUM7SUFF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QUMsQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1FBRTNFLHVFQUF1RTtRQUN2RSxJQUFJLGNBQWMsS0FBSyxDQUFDLElBQUksS0FBSyxFQUFFLENBQUM7WUFDbEMsTUFBTSxHQUFHLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3JCLE1BQU0sR0FBRyxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNyQixPQUFPLFNBQVMsS0FBSyxLQUFLO2dCQUN4QixDQUFDLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUM7Z0JBQ3hCLENBQUMsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzdCLENBQUM7UUFFRCxPQUFPLGNBQWMsQ0FBQztJQUN4QixDQUFDLENBQUMsQ0FBQztJQUVILHNCQUFzQjtJQUN0QixNQUFNLE9BQU8sR0FBRyxVQUFVLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztJQUMxQyxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUM7SUFDaEUsTUFBTSxVQUFVLEdBQ2QsS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDO1FBQ2QsQ0FBQyxDQUFDLFlBQVksQ0FDVixLQUFLLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxTQUFTLEVBQ2pDLEtBQUssRUFBRSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQ2pDO1FBQ0gsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUVYLE9BQU87UUFDTCxJQUFJLEVBQUUsS0FBSztRQUNYLE1BQU0sRUFBRSxVQUFVO1FBQ2xCLE9BQU87S0FDUixDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHByb21pc2VzIGFzIGZzIH0gZnJvbSAnbm9kZTpmcyc7XG5pbXBvcnQgcGF0aCBmcm9tICdub2RlOnBhdGgnO1xuaW1wb3J0IHsgRW50aXR5Q29uZmxpY3RFcnJvciwgV29ya2Zsb3dXb3JsZEVycm9yIH0gZnJvbSAnQHdvcmtmbG93L2Vycm9ycyc7XG5pbXBvcnQgdHlwZSB7IFBhZ2luYXRlZFJlc3BvbnNlIH0gZnJvbSAnQHdvcmtmbG93L3dvcmxkJztcbmltcG9ydCB7IG1vbm90b25pY0ZhY3RvcnkgfSBmcm9tICd1bGlkJztcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xuXG5jb25zdCB1bGlkID0gbW9ub3RvbmljRmFjdG9yeSgoKSA9PiBNYXRoLnJhbmRvbSgpKTtcblxuLyoqXG4gKiBUcnVuY2F0ZSBhIHBvc3NpYmx5LXVudHJ1c3RlZCB2YWx1ZSBmb3IgaW5jbHVzaW9uIGluIGFuIGVycm9yIG1lc3NhZ2UuXG4gKiBLZWVwcyBlcnJvciBvdXRwdXQgYWN0aW9uYWJsZSBmb3IgZGV2ZWxvcGVycyBpbiBkZXYgbW9kZSB3aGlsZSBsaW1pdGluZ1xuICogdGhlIGFtb3VudCBvZiBhdHRhY2tlci1jb250cm9sbGVkIGRhdGEgcmVmbGVjdGVkIGJhY2sgaWYgYSBgd29ybGQtbG9jYWxgXG4gKiBiYWNrZW5kIGlzIGV2ZXIgcnVuIGluIGEgY29udGV4dCB0aGF0IHN1cmZhY2VzIHRoZXNlIG1lc3NhZ2VzIHRvIGNsaWVudHMuXG4gKi9cbmZ1bmN0aW9uIHRydW5jYXRlRm9yRXJyb3IodmFsdWU6IHVua25vd24pOiBzdHJpbmcge1xuICBjb25zdCBzID0gdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/IHZhbHVlIDogU3RyaW5nKHZhbHVlKTtcbiAgY29uc3QgTUFYID0gNDg7XG4gIHJldHVybiBzLmxlbmd0aCA+IE1BWCA/IGAke3Muc2xpY2UoMCwgTUFYKX3igKZgIDogcztcbn1cblxuLyoqXG4gKiBUaHJvd24gd2hlbiBhIGNhbGxlci1zdXBwbGllZCBlbnRpdHkgSUQgY29udGFpbnMgY2hhcmFjdGVycyB0aGF0IHdvdWxkXG4gKiBlc2NhcGUgdGhlIHN0b3JhZ2Ugcm9vdCAocGF0aCB0cmF2ZXJzYWwpIG9yIG90aGVyd2lzZSBwcm9kdWNlIGFuIHVuc2FmZVxuICogZmlsZW5hbWUuIEV4dGVuZHMge0BsaW5rIFdvcmtmbG93V29ybGRFcnJvcn0gc28gdGhlIHBsYXRmb3JtJ3Mgc3RhbmRhcmRcbiAqIGVycm9yLXRvLUhUVFAgbWFwcGluZyBoYW5kbGVzIGl0IGFsb25nc2lkZSBvdGhlciB3b3JsZC1sYXllciBlcnJvcnMuXG4gKi9cbmV4cG9ydCBjbGFzcyBVbnNhZmVFbnRpdHlJZEVycm9yIGV4dGVuZHMgV29ya2Zsb3dXb3JsZEVycm9yIHtcbiAgY29uc3RydWN0b3Ioa2luZDogc3RyaW5nLCB2YWx1ZTogc3RyaW5nKSB7XG4gICAgc3VwZXIoXG4gICAgICBgVW5zYWZlICR7a2luZH0gXCIke3RydW5jYXRlRm9yRXJyb3IodmFsdWUpfVwiOiBtdXN0IG5vdCBiZSBlbXB0eSwgY29udGFpbiBcIi5cIiwgXCIvXCIsIFwiXFxcXFwiLCBvciBudWxsIGJ5dGVzYFxuICAgICk7XG4gICAgdGhpcy5uYW1lID0gJ1Vuc2FmZUVudGl0eUlkRXJyb3InO1xuICB9XG5cbiAgc3RhdGljIGlzKHZhbHVlOiB1bmtub3duKTogdmFsdWUgaXMgVW5zYWZlRW50aXR5SWRFcnJvciB7XG4gICAgcmV0dXJuIHZhbHVlIGluc3RhbmNlb2YgRXJyb3IgJiYgdmFsdWUubmFtZSA9PT0gJ1Vuc2FmZUVudGl0eUlkRXJyb3InO1xuICB9XG59XG5cbi8qKlxuICogVmFsaWRhdGUgdGhhdCBhIHN0cmluZyBpcyBzYWZlIHRvIGVtYmVkIGluIGEgZmlsZXN5c3RlbSBwYXRoIGFzIGEgc2luZ2xlXG4gKiBwYXRoIGNvbXBvbmVudC4gUmVqZWN0cyB2YWx1ZXMgdGhhdCBhcmU6XG4gKiAgIC0gZW1wdHlcbiAqICAgLSBzdGFydGluZyB3aXRoIGAuYCAoYmxvY2tzIGAuYCwgYC4uYCwgYC5sb2Nrc2AsIGAudG1wYCwgYW5kIG90aGVyXG4gKiAgICAgaGlkZGVuIG9yIHJlc2VydmVkIGZpbGVuYW1lcylcbiAqICAgLSBjb250YWluaW5nIGAuYCAod291bGQgY29sbGlkZSB3aXRoIHRoZSBgLnRhZ2AgLyBgLmpzb25gIHN1ZmZpeCB0aGVcbiAqICAgICB0YWdnaW5nIGxvZ2ljIHN0cmlwcyBmcm9tIGZpbGVuYW1lczsgc2VlIHtAbGluayBzdHJpcFRhZ30gL1xuICogICAgIHtAbGluayBnZXRPYmplY3RDcmVhdGVkQXR9KVxuICogICAtIGNvbnRhaW5pbmcgYC9gLCBgXFxgLCBvciBhIE5VTCBieXRlXG4gKlxuICogVGhpcyBpcyB0aGUgcHJpbWFyeSBkZWZlbnNlIGFnYWluc3QgcGF0aC10cmF2ZXJzYWwgYXR0YWNrcyB3aGVyZSBhXG4gKiByZXF1ZXN0IGJvZHkgc3VwcGxpZXMgYSBgcnVuSWRgIC8gYHN0ZXBJZGAgLyBgY29ycmVsYXRpb25JZGAgY29udGFpbmluZ1xuICogYC4uL2Agc2VxdWVuY2VzIHRvIHJlYWQgb3Igd3JpdGUgZmlsZXMgb3V0c2lkZSB0aGUgc3RvcmFnZSByb290LlxuICoge0BsaW5rIHJlc29sdmVXaXRoaW5CYXNlfSBwcm92aWRlcyBhIGJlbHQtYW5kLXN1c3BlbmRlcnMgY29udGFpbm1lbnRcbiAqIGNoZWNrIGF0IHRoZSBwb2ludCBvZiBgcGF0aC5qb2luYCBmb3IgZGVmZW5zZSBpbiBkZXB0aC5cbiAqXG4gKiBAcGFyYW0ga2luZCAtIEh1bWFuLXJlYWRhYmxlIGxhYmVsIHVzZWQgaW4gdGhlIGVycm9yIG1lc3NhZ2UgKGUuZy4gXCJydW5JZFwiKVxuICogQHBhcmFtIHZhbHVlIC0gVGhlIHZhbHVlIHRvIHZhbGlkYXRlOyB0aHJvd3Mge0BsaW5rIFVuc2FmZUVudGl0eUlkRXJyb3J9XG4gKiAgIGlmIHRoZSB2YWx1ZSBpcyBub3Qgc2FmZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFzc2VydFNhZmVFbnRpdHlJZChraW5kOiBzdHJpbmcsIHZhbHVlOiBzdHJpbmcpOiB2b2lkIHtcbiAgaWYgKFxuICAgIHZhbHVlLmxlbmd0aCA9PT0gMCB8fFxuICAgIHZhbHVlLnN0YXJ0c1dpdGgoJy4nKSB8fFxuICAgIHZhbHVlLmluY2x1ZGVzKCcvJykgfHxcbiAgICB2YWx1ZS5pbmNsdWRlcygnXFxcXCcpIHx8XG4gICAgdmFsdWUuaW5jbHVkZXMoJ1xcMCcpIHx8XG4gICAgdmFsdWUuaW5jbHVkZXMoJy4nKVxuICApIHtcbiAgICB0aHJvdyBuZXcgVW5zYWZlRW50aXR5SWRFcnJvcihraW5kLCB2YWx1ZSk7XG4gIH1cbn1cblxuLyoqXG4gKiBKb2luIGBiYXNlZGlyYCB3aXRoIGFuIGVudGl0eS1yZWxhdGl2ZSBwYXRoIGFuZCBhc3NlcnQgdGhlIHJlc3VsdCBzdGF5c1xuICogaW5zaWRlIGBiYXNlZGlyYC4gQ29tcGxlbWVudHMge0BsaW5rIGFzc2VydFNhZmVFbnRpdHlJZH06IHBlci1lbnRyeS1wb2ludFxuICogdmFsaWRhdGlvbiBpcyB0aGUgcHJpbWFyeSBkZWZlbnNlLCBhbmQgdGhpcyBmaW5hbCBjaGVjayBjYXRjaGVzIGFueSBwYXRoXG4gKiBlc2NhcGUgdGhhdCBzbGlwcGVkIHBhc3QgKGUuZy4gYSBuZXcgY2FsbCBzaXRlIHRoYXQgZm9yZ290IHRvIHZhbGlkYXRlLFxuICogb3IgYW4gdW51c3VhbCBjaGFyYWN0ZXIgY29tYmluYXRpb24gb24gYSBmdXR1cmUgZmlsZXN5c3RlbSkuIFRocm93c1xuICoge0BsaW5rIFVuc2FmZUVudGl0eUlkRXJyb3J9IGlmIHRoZSBqb2luZWQgcGF0aCBlc2NhcGVzIGBiYXNlZGlyYC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVXaXRoaW5CYXNlKFxuICBiYXNlZGlyOiBzdHJpbmcsXG4gIC4uLnNlZ21lbnRzOiBzdHJpbmdbXVxuKTogc3RyaW5nIHtcbiAgY29uc3QgcmVzb2x2ZWRCYXNlID0gcGF0aC5yZXNvbHZlKGJhc2VkaXIpO1xuICBjb25zdCBqb2luZWQgPSBwYXRoLnJlc29sdmUoYmFzZWRpciwgLi4uc2VnbWVudHMpO1xuICBpZiAoam9pbmVkICE9PSByZXNvbHZlZEJhc2UgJiYgIWpvaW5lZC5zdGFydHNXaXRoKHJlc29sdmVkQmFzZSArIHBhdGguc2VwKSkge1xuICAgIHRocm93IG5ldyBVbnNhZmVFbnRpdHlJZEVycm9yKCdwYXRoJywgc2VnbWVudHMuam9pbignLycpKTtcbiAgfVxuICByZXR1cm4gam9pbmVkO1xufVxuXG5jb25zdCBpc1dpbmRvd3MgPSBwcm9jZXNzLnBsYXRmb3JtID09PSAnd2luMzInO1xuXG4vKipcbiAqIEV4ZWN1dGUgYSBmaWxlc3lzdGVtIG9wZXJhdGlvbiB3aXRoIHJldHJ5IGxvZ2ljIG9uIFdpbmRvd3MuXG4gKiBPbiBXaW5kb3dzLCBmaWxlIG9wZXJhdGlvbnMgY2FuIGZhaWwgd2l0aCBFUEVSTS9FQlVTWS9FQUNDRVMgd2hlbiBmaWxlc1xuICogYXJlIGJyaWVmbHkgbG9ja2VkIGJ5IGFub3RoZXIgcHJvY2VzcyBvciBhbnRpdmlydXMuIFRoaXMgd3JhcHBlciBhZGRzXG4gKiBleHBvbmVudGlhbCBiYWNrb2ZmIHJldHJ5IGxvZ2ljLiBPbiBub24tV2luZG93cyBwbGF0Zm9ybXMsIGV4ZWN1dGVzIGRpcmVjdGx5LlxuICovXG5hc3luYyBmdW5jdGlvbiB3aXRoV2luZG93c1JldHJ5PFQ+KFxuICBmbjogKCkgPT4gUHJvbWlzZTxUPixcbiAgbWF4UmV0cmllcyA9IDVcbik6IFByb21pc2U8VD4ge1xuICBpZiAoIWlzV2luZG93cykgcmV0dXJuIGZuKCk7XG5cbiAgY29uc3QgcmV0cnlhYmxlRXJyb3JzID0gWydFUEVSTScsICdFQlVTWScsICdFQUNDRVMnXTtcbiAgY29uc3QgYmFzZURlbGF5TXMgPSAxMDtcblxuICBmb3IgKGxldCBhdHRlbXB0ID0gMDsgYXR0ZW1wdCA8PSBtYXhSZXRyaWVzOyBhdHRlbXB0KyspIHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IGZuKCk7XG4gICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgY29uc3QgaXNSZXRyeWFibGUgPVxuICAgICAgICBhdHRlbXB0IDwgbWF4UmV0cmllcyAmJiByZXRyeWFibGVFcnJvcnMuaW5jbHVkZXMoZXJyb3IuY29kZSk7XG4gICAgICBpZiAoIWlzUmV0cnlhYmxlKSB0aHJvdyBlcnJvcjtcbiAgICAgIC8vIEV4cG9uZW50aWFsIGJhY2tvZmYgd2l0aCBqaXR0ZXJcbiAgICAgIGNvbnN0IGRlbGF5ID0gYmFzZURlbGF5TXMgKiAyICoqIGF0dGVtcHQgKyBNYXRoLnJhbmRvbSgpICogYmFzZURlbGF5TXM7XG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCBkZWxheSkpO1xuICAgIH1cbiAgfVxuICAvLyBUeXBlU2NyaXB0OiB1bnJlYWNoYWJsZSwgYnV0IHNhdGlzZmllcyByZXR1cm4gdHlwZVxuICB0aHJvdyBuZXcgRXJyb3IoJ1JldHJ5IGxvb3AgZXhpdGVkIHVuZXhwZWN0ZWRseScpO1xufVxuXG4vLyBJbi1tZW1vcnkgY2FjaGUgb2YgY3JlYXRlZCBmaWxlcyB0byBhdm9pZCBleHBlbnNpdmUgZnMuYWNjZXNzKCkgY2FsbHNcbi8vIFRoaXMgaXMgc2FmZSBiZWNhdXNlIHdlIG9ubHkgd3JpdGUgb25jZSBwZXIgZmlsZSBwYXRoIChubyBvdmVyd3JpdGVzIHdpdGhvdXQgZXhwbGljaXQgZmxhZylcbmNvbnN0IGNyZWF0ZWRGaWxlc0NhY2hlID0gbmV3IFNldDxzdHJpbmc+KCk7XG5cbi8qKlxuICogQ2xlYXIgdGhlIGNyZWF0ZWQgZmlsZXMgY2FjaGUuIFVzZWZ1bCBmb3IgdGVzdGluZyBvciB3aGVuIGZpbGVzIGFyZSBkZWxldGVkIGV4dGVybmFsbHkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjbGVhckNyZWF0ZWRGaWxlc0NhY2hlKCk6IHZvaWQge1xuICBjcmVhdGVkRmlsZXNDYWNoZS5jbGVhcigpO1xufVxuXG5leHBvcnQgeyB1bGlkVG9EYXRlIH0gZnJvbSAnQHdvcmtmbG93L3dvcmxkJztcblxuLyoqXG4gKiBSZWdleCBtYXRjaGluZyBhIHRhZyBzdWZmaXggb24gYSBmaWxlSWQgKGFmdGVyIGAuanNvbmAgaGFzIGJlZW4gc3RyaXBwZWQpLlxuICogRS5nLiwgYHdydW5fQUJDLnZpdGVzdC0wYCDihpIgdGhlIGAudml0ZXN0LTBgIHBhcnQuXG4gKiBUYWdzIHN0YXJ0IHdpdGggYSBsZXR0ZXIgYW5kIGNvbnRhaW4gYWxwaGFudW1lcmljIGNoYXJzIGFuZCBoeXBoZW5zLlxuICogRW50aXR5IElEcyAoVUxJRHMsIHN0ZXBfTiwgZXRjLikgbmV2ZXIgY29udGFpbiBkb3RzLCBzbyB0aGUgZmlyc3QgZG90XG4gKiBhbHdheXMgbWFya3MgdGhlIHRhZyBib3VuZGFyeS5cbiAqL1xuY29uc3QgVEFHX1BBVFRFUk4gPSAvXFwuW2EtekEtWl1bYS16QS1aMC05LV0qJC87XG5cbi8qKlxuICogU3RyaXAgYSB0YWcgc3VmZml4IGZyb20gYSBmaWxlSWQuXG4gKiBgd3J1bl9BQkMudml0ZXN0LTBgIOKGkiBgd3J1bl9BQkNgXG4gKiBgd3J1bl9BQkNgIOKGkiBgd3J1bl9BQkNgIChuby1vcCBpZiBubyB0YWcpXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzdHJpcFRhZyhmaWxlSWQ6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBmaWxlSWQucmVwbGFjZShUQUdfUEFUVEVSTiwgJycpO1xufVxuXG4vKipcbiAqIENoZWNrIHdoZXRoZXIgYSBmaWxlSWQgYmVsb25ncyB0byBhIHNwZWNpZmljIHRhZy5cbiAqIGB3cnVuX0FCQy52aXRlc3QtMGAgYmVsb25ncyB0byBgdml0ZXN0LTBgLlxuICogVW50YWdnZWQgZmlsZUlkcyBuZXZlciBtYXRjaCBhIHRhZy1zY29wZWQgbG9va3VwLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaGFzVGFnKGZpbGVJZDogc3RyaW5nLCB0YWc6IHN0cmluZyk6IGJvb2xlYW4ge1xuICByZXR1cm4gZmlsZUlkLmVuZHNXaXRoKGAuJHt0YWd9YCk7XG59XG5cbi8qKlxuICogQnVpbGQgdGhlIGZpbGUgcGF0aCBmb3IgYW4gZW50aXR5LCB3aXRoIG9wdGlvbmFsIHRhZyBlbWJlZGRlZCBpbiB0aGUgZmlsZW5hbWUuXG4gKiBgdGFnZ2VkUGF0aCgnL2RhdGEnLCAncnVucycsICd3cnVuX0FCQycsICd2aXRlc3QtMCcpYCDihpIgYC9kYXRhL3J1bnMvd3J1bl9BQkMudml0ZXN0LTAuanNvbmBcbiAqIGB0YWdnZWRQYXRoKCcvZGF0YScsICdydW5zJywgJ3dydW5fQUJDJylgIOKGkiBgL2RhdGEvcnVucy93cnVuX0FCQy5qc29uYFxuICpcbiAqIFRoZSBgZmlsZUlkYCBhbmQgYHRhZ2AgYXJlIHZhbGlkYXRlZCB3aXRoIHtAbGluayBhc3NlcnRTYWZlRW50aXR5SWR9IGFuZFxuICogdGhlIHJlc3VsdCBpcyBjb250YWlubWVudC1jaGVja2VkIHdpdGgge0BsaW5rIHJlc29sdmVXaXRoaW5CYXNlfSB0b1xuICogcHJldmVudCBwYXRoLXRyYXZlcnNhbCBhdHRhY2tzIHdoZW4gdmFsdWVzIGFyZSBkZXJpdmVkIGZyb20gdW50cnVzdGVkXG4gKiByZXF1ZXN0IGlucHV0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gdGFnZ2VkUGF0aChcbiAgYmFzZWRpcjogc3RyaW5nLFxuICBlbnRpdHlEaXI6IHN0cmluZyxcbiAgZmlsZUlkOiBzdHJpbmcsXG4gIHRhZz86IHN0cmluZ1xuKTogc3RyaW5nIHtcbiAgYXNzZXJ0U2FmZUVudGl0eUlkKCdmaWxlSWQnLCBmaWxlSWQpO1xuICBpZiAodGFnICE9PSB1bmRlZmluZWQpIGFzc2VydFNhZmVFbnRpdHlJZCgndGFnJywgdGFnKTtcbiAgY29uc3QgZmlsZW5hbWUgPSB0YWcgPyBgJHtmaWxlSWR9LiR7dGFnfS5qc29uYCA6IGAke2ZpbGVJZH0uanNvbmA7XG4gIHJldHVybiByZXNvbHZlV2l0aGluQmFzZShiYXNlZGlyLCBlbnRpdHlEaXIsIGZpbGVuYW1lKTtcbn1cblxuLyoqXG4gKiBSZWFkIGEgSlNPTiBlbnRpdHkgd2l0aCB0YWdnZWQgZmFsbGJhY2suXG4gKiBXaGVuIGEgdGFnIGlzIHNldCwgdHJpZXMgdGhlIHRhZ2dlZCBwYXRoIGZpcnN0LCB0aGVuIGZhbGxzIGJhY2sgdG8gdGhlXG4gKiB1bnRhZ2dlZCBwYXRoIChzbyBhIHRhZ2dlZCB3b3JsZCBjYW4gcmVhZCBlbnRpdGllcyB3cml0dGVuIHdpdGhvdXQgYSB0YWcpLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVhZEpTT05XaXRoRmFsbGJhY2s8VD4oXG4gIGJhc2VkaXI6IHN0cmluZyxcbiAgZW50aXR5RGlyOiBzdHJpbmcsXG4gIGZpbGVJZDogc3RyaW5nLFxuICBzY2hlbWE6IHouWm9kVHlwZTxUPixcbiAgdGFnPzogc3RyaW5nXG4pOiBQcm9taXNlPFQgfCBudWxsPiB7XG4gIGFzc2VydFNhZmVFbnRpdHlJZCgnZmlsZUlkJywgZmlsZUlkKTtcbiAgaWYgKHRhZyAhPT0gdW5kZWZpbmVkKSBhc3NlcnRTYWZlRW50aXR5SWQoJ3RhZycsIHRhZyk7XG4gIGlmICh0YWcpIHtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCByZWFkSlNPTihcbiAgICAgIHJlc29sdmVXaXRoaW5CYXNlKGJhc2VkaXIsIGVudGl0eURpciwgYCR7ZmlsZUlkfS4ke3RhZ30uanNvbmApLFxuICAgICAgc2NoZW1hXG4gICAgKTtcbiAgICBpZiAocmVzdWx0ICE9PSBudWxsKSByZXR1cm4gcmVzdWx0O1xuICB9XG4gIHJldHVybiByZWFkSlNPTihcbiAgICByZXNvbHZlV2l0aGluQmFzZShiYXNlZGlyLCBlbnRpdHlEaXIsIGAke2ZpbGVJZH0uanNvbmApLFxuICAgIHNjaGVtYVxuICApO1xufVxuXG4vKipcbiAqIExpc3QgYWxsIGZpbGVuYW1lcyBpbiBhIGRpcmVjdG9yeSB0aGF0IGhhdmUgYSBzcGVjaWZpYyB0YWcuXG4gKiBSZXR1cm5zIGZ1bGwgZmlsZW5hbWVzIChlLmcuLCBgd3J1bl9BQkMudml0ZXN0LTAuanNvbmApLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbGlzdFRhZ2dlZEZpbGVzKFxuICBkaXJQYXRoOiBzdHJpbmcsXG4gIHRhZzogc3RyaW5nXG4pOiBQcm9taXNlPHN0cmluZ1tdPiB7XG4gIGNvbnN0IHN1ZmZpeCA9IGAuJHt0YWd9Lmpzb25gO1xuICB0cnkge1xuICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZnMucmVhZGRpcihkaXJQYXRoKTtcbiAgICByZXR1cm4gZmlsZXMuZmlsdGVyKChmKSA9PiBmLmVuZHNXaXRoKHN1ZmZpeCkpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmICgoZXJyb3IgYXMgYW55KS5jb2RlID09PSAnRU5PRU5UJykgcmV0dXJuIFtdO1xuICAgIHRocm93IGVycm9yO1xuICB9XG59XG5cbi8qKlxuICogTGlzdCBhbGwgZmlsZW5hbWVzIGluIGEgZGlyZWN0b3J5IHRoYXQgaGF2ZSBhIHNwZWNpZmljIHRhZyBhbmQgZXh0ZW5zaW9uLlxuICogUmV0dXJucyBmdWxsIGZpbGVuYW1lcyAoZS5nLiwgYHN0cmVhbS1jaG5rX0FCQy52aXRlc3QtMC5iaW5gKS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxpc3RUYWdnZWRGaWxlc0J5RXh0ZW5zaW9uKFxuICBkaXJQYXRoOiBzdHJpbmcsXG4gIHRhZzogc3RyaW5nLFxuICBleHRlbnNpb246IHN0cmluZ1xuKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICBjb25zdCBzdWZmaXggPSBgLiR7dGFnfSR7ZXh0ZW5zaW9ufWA7XG4gIHRyeSB7XG4gICAgY29uc3QgZmlsZXMgPSBhd2FpdCBmcy5yZWFkZGlyKGRpclBhdGgpO1xuICAgIHJldHVybiBmaWxlcy5maWx0ZXIoKGYpID0+IGYuZW5kc1dpdGgoc3VmZml4KSk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgaWYgKChlcnJvciBhcyBhbnkpLmNvZGUgPT09ICdFTk9FTlQnKSByZXR1cm4gW107XG4gICAgdGhyb3cgZXJyb3I7XG4gIH1cbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGVuc3VyZURpcihkaXJQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgdHJ5IHtcbiAgICBhd2FpdCBmcy5ta2RpcihkaXJQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgfSBjYXRjaCAoX2Vycm9yKSB7XG4gICAgLy8gSWdub3JlIGlmIGFscmVhZHkgZXhpc3RzXG4gIH1cbn1cblxuaW50ZXJmYWNlIFdyaXRlT3B0aW9ucyB7XG4gIG92ZXJ3cml0ZT86IGJvb2xlYW47XG59XG5cbi8qKlxuICogQ3VzdG9tIEpTT04gcmVwbGFjZXIgdGhhdCBlbmNvZGVzIFVpbnQ4QXJyYXkgYXMgYmFzZTY0IHN0cmluZ3MuXG4gKiBGb3JtYXQ6IHsgX190eXBlOiAnVWludDhBcnJheScsIGRhdGE6ICc8YmFzZTY0PicgfVxuICovXG5leHBvcnQgZnVuY3Rpb24ganNvblJlcGxhY2VyKF9rZXk6IHN0cmluZywgdmFsdWU6IHVua25vd24pOiB1bmtub3duIHtcbiAgaWYgKHZhbHVlIGluc3RhbmNlb2YgVWludDhBcnJheSkge1xuICAgIHJldHVybiB7XG4gICAgICBfX3R5cGU6ICdVaW50OEFycmF5JyxcbiAgICAgIGRhdGE6IEJ1ZmZlci5mcm9tKHZhbHVlKS50b1N0cmluZygnYmFzZTY0JyksXG4gICAgfTtcbiAgfVxuICByZXR1cm4gdmFsdWU7XG59XG5cbi8qKlxuICogQ3VzdG9tIEpTT04gcmV2aXZlciB0aGF0IGRlY29kZXMgYmFzZTY0IHN0cmluZ3MgYmFjayB0byBVaW50OEFycmF5LlxuICovXG5leHBvcnQgZnVuY3Rpb24ganNvblJldml2ZXIoX2tleTogc3RyaW5nLCB2YWx1ZTogdW5rbm93bik6IHVua25vd24ge1xuICBpZiAoXG4gICAgdmFsdWUgIT09IG51bGwgJiZcbiAgICB0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnICYmXG4gICAgKHZhbHVlIGFzIGFueSkuX190eXBlID09PSAnVWludDhBcnJheScgJiZcbiAgICB0eXBlb2YgKHZhbHVlIGFzIGFueSkuZGF0YSA9PT0gJ3N0cmluZydcbiAgKSB7XG4gICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KEJ1ZmZlci5mcm9tKCh2YWx1ZSBhcyBhbnkpLmRhdGEsICdiYXNlNjQnKSk7XG4gIH1cbiAgcmV0dXJuIHZhbHVlO1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gd3JpdGVKU09OKFxuICBmaWxlUGF0aDogc3RyaW5nLFxuICBkYXRhOiBhbnksXG4gIG9wdHM/OiBXcml0ZU9wdGlvbnNcbik6IFByb21pc2U8dm9pZD4ge1xuICByZXR1cm4gd3JpdGUoZmlsZVBhdGgsIEpTT04uc3RyaW5naWZ5KGRhdGEsIGpzb25SZXBsYWNlciwgMiksIG9wdHMpO1xufVxuXG4vKipcbiAqIFdyaXRlcyBkYXRhIHRvIGEgZmlsZSB1c2luZyBhdG9taWMgd3JpdGUtcmVuYW1lIHBhdHRlcm4uXG4gKlxuICogTm90ZTogV2hpbGUgdGhpcyBmdW5jdGlvbiB1c2VzIHRlbXAgZmlsZXMgdG8gYXZvaWQgcGFydGlhbCB3cml0ZXMsXG4gKiBpdCBkb2VzIG5vdCBwcm92aWRlIHByb3RlY3Rpb24gYWdhaW5zdCBjb25jdXJyZW50IHdyaXRlcyBmcm9tIG11bHRpcGxlXG4gKiBwcm9jZXNzZXMuIEluIGEgbXVsdGktd3JpdGVyIHNjZW5hcmlvLCB0aGUgbGFzdCB3cml0ZXIgd2lucy5cbiAqIEZvciBwcm9kdWN0aW9uIHVzZSB3aXRoIG11bHRpcGxlIHdyaXRlcnMsIGNvbnNpZGVyIHVzaW5nIGEgcHJvcGVyXG4gKiBkYXRhYmFzZSBvciBsb2NraW5nIG1lY2hhbmlzbS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHdyaXRlKFxuICBmaWxlUGF0aDogc3RyaW5nLFxuICBkYXRhOiBzdHJpbmcgfCBCdWZmZXIsXG4gIG9wdHM/OiBXcml0ZU9wdGlvbnNcbik6IFByb21pc2U8dm9pZD4ge1xuICBpZiAoIW9wdHM/Lm92ZXJ3cml0ZSkge1xuICAgIC8vIEZhc3QgcGF0aDogY2hlY2sgaW4tbWVtb3J5IGNhY2hlIGZpcnN0IHRvIGF2b2lkIGV4cGVuc2l2ZSBmcy5hY2Nlc3MoKSBjYWxsc1xuICAgIC8vIFRoaXMgcHJvdmlkZXMgc2lnbmlmaWNhbnQgcGVyZm9ybWFuY2UgaW1wcm92ZW1lbnQgd2hlbiBjcmVhdGluZyBtYW55IGZpbGVzXG4gICAgaWYgKGNyZWF0ZWRGaWxlc0NhY2hlLmhhcyhmaWxlUGF0aCkpIHtcbiAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICBgRmlsZSAke2ZpbGVQYXRofSBhbHJlYWR5IGV4aXN0cyBhbmQgJ292ZXJ3cml0ZScgaXMgZmFsc2VgXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIFNsb3cgcGF0aDogY2hlY2sgZmlsZXN5c3RlbSBmb3IgZmlsZXMgY3JlYXRlZCBiZWZvcmUgdGhpcyBwcm9jZXNzIHN0YXJ0ZWRcbiAgICB0cnkge1xuICAgICAgYXdhaXQgZnMuYWNjZXNzKGZpbGVQYXRoKTtcbiAgICAgIC8vIEZpbGUgZXhpc3RzIG9uIGRpc2ssIGFkZCB0byBjYWNoZSBmb3IgZnV0dXJlIGNoZWNrc1xuICAgICAgY3JlYXRlZEZpbGVzQ2FjaGUuYWRkKGZpbGVQYXRoKTtcbiAgICAgIHRocm93IG5ldyBFbnRpdHlDb25mbGljdEVycm9yKFxuICAgICAgICBgRmlsZSAke2ZpbGVQYXRofSBhbHJlYWR5IGV4aXN0cyBhbmQgJ292ZXJ3cml0ZScgaXMgZmFsc2VgXG4gICAgICApO1xuICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgIC8vIElmIGZpbGUgZG9lc24ndCBleGlzdCAoRU5PRU5UKSwgY29udGludWUgd2l0aCB3cml0ZVxuICAgICAgaWYgKGVycm9yLmNvZGUgIT09ICdFTk9FTlQnKSB7XG4gICAgICAgIHRocm93IGVycm9yO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGNvbnN0IHRlbXBQYXRoID0gYCR7ZmlsZVBhdGh9LnRtcC4ke3VsaWQoKX1gO1xuICBsZXQgdGVtcEZpbGVDcmVhdGVkID0gZmFsc2U7XG4gIHRyeSB7XG4gICAgYXdhaXQgZW5zdXJlRGlyKHBhdGguZGlybmFtZShmaWxlUGF0aCkpO1xuICAgIGF3YWl0IGZzLndyaXRlRmlsZSh0ZW1wUGF0aCwgZGF0YSk7XG4gICAgdGVtcEZpbGVDcmVhdGVkID0gdHJ1ZTtcbiAgICBhd2FpdCB3aXRoV2luZG93c1JldHJ5KCgpID0+IGZzLnJlbmFtZSh0ZW1wUGF0aCwgZmlsZVBhdGgpKTtcbiAgICAvLyBUcmFjayB0aGlzIGZpbGUgaW4gY2FjaGUgc28gZnV0dXJlIHdyaXRlcyBrbm93IGl0IGV4aXN0c1xuICAgIGNyZWF0ZWRGaWxlc0NhY2hlLmFkZChmaWxlUGF0aCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgLy8gT25seSB0cnkgdG8gY2xlYW4gdXAgdGVtcCBmaWxlIGlmIGl0IHdhcyBhY3R1YWxseSBjcmVhdGVkXG4gICAgaWYgKHRlbXBGaWxlQ3JlYXRlZCkge1xuICAgICAgYXdhaXQgd2l0aFdpbmRvd3NSZXRyeSgoKSA9PiBmcy51bmxpbmsodGVtcFBhdGgpLCAzKS5jYXRjaCgoKSA9PiB7fSk7XG4gICAgfVxuICAgIHRocm93IGVycm9yO1xuICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWFkSlNPTjxUPihcbiAgZmlsZVBhdGg6IHN0cmluZyxcbiAgZGVjb2Rlcjogei5ab2RUeXBlPFQ+XG4pOiBQcm9taXNlPFQgfCBudWxsPiB7XG4gIHRyeSB7XG4gICAgY29uc3QgY29udGVudCA9IGF3YWl0IGZzLnJlYWRGaWxlKGZpbGVQYXRoLCAndXRmLTgnKTtcbiAgICByZXR1cm4gZGVjb2Rlci5wYXJzZShKU09OLnBhcnNlKGNvbnRlbnQsIGpzb25SZXZpdmVyKSk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgaWYgKChlcnJvciBhcyBhbnkpLmNvZGUgPT09ICdFTk9FTlQnKSByZXR1cm4gbnVsbDtcbiAgICB0aHJvdyBlcnJvcjtcbiAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVhZEJ1ZmZlcihmaWxlUGF0aDogc3RyaW5nKTogUHJvbWlzZTxCdWZmZXI+IHtcbiAgY29uc3QgY29udGVudCA9IGF3YWl0IGZzLnJlYWRGaWxlKGZpbGVQYXRoKTtcbiAgcmV0dXJuIGNvbnRlbnQ7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWFkRmlyc3RCeXRlKFxuICBmaWxlUGF0aDogc3RyaW5nXG4pOiBQcm9taXNlPG51bWJlciB8IHVuZGVmaW5lZD4ge1xuICBjb25zdCBmaWxlID0gYXdhaXQgZnMub3BlbihmaWxlUGF0aCwgJ3InKTtcbiAgdHJ5IHtcbiAgICBjb25zdCBieXRlID0gQnVmZmVyLmFsbG9jVW5zYWZlKDEpO1xuICAgIGNvbnN0IHsgYnl0ZXNSZWFkIH0gPSBhd2FpdCBmaWxlLnJlYWQoYnl0ZSwgMCwgMSwgMCk7XG4gICAgcmV0dXJuIGJ5dGVzUmVhZCA9PT0gMCA/IHVuZGVmaW5lZCA6IGJ5dGVbMF07XG4gIH0gZmluYWxseSB7XG4gICAgYXdhaXQgZmlsZS5jbG9zZSgpO1xuICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBkZWxldGVKU09OKGZpbGVQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgdHJ5IHtcbiAgICBhd2FpdCBmcy51bmxpbmsoZmlsZVBhdGgpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmICgoZXJyb3IgYXMgYW55KS5jb2RlICE9PSAnRU5PRU5UJykgdGhyb3cgZXJyb3I7XG4gIH1cbn1cblxuLyoqXG4gKiBBdG9taWNhbGx5IGNyZWF0ZSBhIGZpbGUgdXNpbmcgT19DUkVBVCB8IE9fRVhDTCBmbGFncy5cbiAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgZmlsZSB3YXMgY3JlYXRlZCwgZmFsc2UgaWYgaXQgYWxyZWFkeSBleGlzdHMuXG4gKiBUaGlzIGlzIGF0b21pYyBhdCB0aGUgT1MgbGV2ZWwsIHNhZmUgZm9yIGNvbmN1cnJlbnQgYWNjZXNzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gd3JpdGVFeGNsdXNpdmUoXG4gIGZpbGVQYXRoOiBzdHJpbmcsXG4gIGRhdGE6IHN0cmluZ1xuKTogUHJvbWlzZTxib29sZWFuPiB7XG4gIGF3YWl0IGVuc3VyZURpcihwYXRoLmRpcm5hbWUoZmlsZVBhdGgpKTtcbiAgdHJ5IHtcbiAgICBhd2FpdCBmcy53cml0ZUZpbGUoZmlsZVBhdGgsIGRhdGEsIHsgZmxhZzogJ3d4JyB9KTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgIGlmIChlcnJvci5jb2RlID09PSAnRUVYSVNUJykge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICB0aHJvdyBlcnJvcjtcbiAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbGlzdEpTT05GaWxlcyhkaXJQYXRoOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZ1tdPiB7XG4gIHJldHVybiBsaXN0RmlsZXNCeUV4dGVuc2lvbihkaXJQYXRoLCAnLmpzb24nKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxpc3RGaWxlc0J5RXh0ZW5zaW9uKFxuICBkaXJQYXRoOiBzdHJpbmcsXG4gIGV4dGVuc2lvbjogc3RyaW5nXG4pOiBQcm9taXNlPHN0cmluZ1tdPiB7XG4gIHRyeSB7XG4gICAgY29uc3QgZmlsZXMgPSBhd2FpdCBmcy5yZWFkZGlyKGRpclBhdGgpO1xuICAgIHJldHVybiBmaWxlc1xuICAgICAgLmZpbHRlcigoZikgPT4gZi5lbmRzV2l0aChleHRlbnNpb24pKVxuICAgICAgLm1hcCgoZikgPT4gZi5zbGljZSgwLCAtZXh0ZW5zaW9uLmxlbmd0aCkpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmICgoZXJyb3IgYXMgYW55KS5jb2RlID09PSAnRU5PRU5UJykgcmV0dXJuIFtdO1xuICAgIHRocm93IGVycm9yO1xuICB9XG59XG5cbmludGVyZmFjZSBQYWdpbmF0ZWRGaWxlU3lzdGVtUXVlcnlDb25maWc8VD4ge1xuICBkaXJlY3Rvcnk6IHN0cmluZztcbiAgc2NoZW1hOiB6LlpvZFR5cGU8VD47XG4gIGZpbGVQcmVmaXg/OiBzdHJpbmc7XG4gIGZpbGVJZEZpbHRlcj86IChmaWxlSWQ6IHN0cmluZykgPT4gYm9vbGVhbjtcbiAgZmlsdGVyPzogKGl0ZW06IFQpID0+IGJvb2xlYW47XG4gIHNvcnRPcmRlcj86ICdhc2MnIHwgJ2Rlc2MnO1xuICBsaW1pdD86IG51bWJlcjtcbiAgY3Vyc29yPzogc3RyaW5nO1xuICBnZXRDcmVhdGVkQXQoZmlsZW5hbWU6IHN0cmluZyk6IERhdGUgfCBudWxsO1xuICBnZXRJZD8oaXRlbTogVCk6IHN0cmluZztcbn1cbi8vIEN1cnNvciBmb3JtYXQ6IFwidGltZXN0YW1wfGlkXCIgZm9yIHRpZS1icmVha2luZ1xuaW50ZXJmYWNlIFBhcnNlZEN1cnNvciB7XG4gIHRpbWVzdGFtcDogRGF0ZTtcbiAgaWQ6IHN0cmluZyB8IG51bGw7XG59XG5cbmZ1bmN0aW9uIHBhcnNlQ3Vyc29yKGN1cnNvcjogc3RyaW5nIHwgdW5kZWZpbmVkKTogUGFyc2VkQ3Vyc29yIHwgbnVsbCB7XG4gIGlmICghY3Vyc29yKSByZXR1cm4gbnVsbDtcblxuICBjb25zdCBwYXJ0cyA9IGN1cnNvci5zcGxpdCgnfCcpO1xuICByZXR1cm4ge1xuICAgIHRpbWVzdGFtcDogbmV3IERhdGUocGFydHNbMF0pLFxuICAgIGlkOiBwYXJ0c1sxXSB8fCBudWxsLFxuICB9O1xufVxuXG5mdW5jdGlvbiBjcmVhdGVDdXJzb3IodGltZXN0YW1wOiBEYXRlLCBpZDogc3RyaW5nIHwgdW5kZWZpbmVkKTogc3RyaW5nIHtcbiAgcmV0dXJuIGlkID8gYCR7dGltZXN0YW1wLnRvSVNPU3RyaW5nKCl9fCR7aWR9YCA6IHRpbWVzdGFtcC50b0lTT1N0cmluZygpO1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcGFnaW5hdGVkRmlsZVN5c3RlbVF1ZXJ5PFQgZXh0ZW5kcyB7IGNyZWF0ZWRBdDogRGF0ZSB9PihcbiAgY29uZmlnOiBQYWdpbmF0ZWRGaWxlU3lzdGVtUXVlcnlDb25maWc8VD5cbik6IFByb21pc2U8UGFnaW5hdGVkUmVzcG9uc2U8VD4+IHtcbiAgY29uc3Qge1xuICAgIGRpcmVjdG9yeSxcbiAgICBzY2hlbWEsXG4gICAgZmlsZVByZWZpeCxcbiAgICBmaWxlSWRGaWx0ZXIsXG4gICAgZmlsdGVyLFxuICAgIHNvcnRPcmRlciA9ICdkZXNjJyxcbiAgICBsaW1pdCA9IDIwLFxuICAgIGN1cnNvcixcbiAgICBnZXRDcmVhdGVkQXQsXG4gICAgZ2V0SWQsXG4gIH0gPSBjb25maWc7XG5cbiAgLy8gVmFsaWRhdGUgZmlsZVByZWZpeCAodHlwaWNhbGx5IGAke3J1bklkfS1gKSBzbyByZXF1ZXN0LWRlcml2ZWQgcHJlZml4ZXNcbiAgLy8gY29uc2lzdGVudGx5IHJlamVjdCB1bnNhZmUgY2hhcmFjdGVycy4gZmlsZVByZWZpeCBpcyBvbmx5IHVzZWQgYmVsb3cgdG9cbiAgLy8gZmlsdGVyIHJlYWRkaXIoKSByZXN1bHRzIGJ5IHByZWZpeCDigJQgaXQgZG9lc24ndCBwYXJ0aWNpcGF0ZSBpbiBwYXRoXG4gIC8vIGNvbnN0cnVjdGlvbiDigJQgYnV0IGtlZXBpbmcgdGhlIHZhbGlkYXRpb24gcnVsZSB1bmlmb3JtIGFjcm9zcyB0aGVcbiAgLy8gc3RvcmFnZSBsYXllciBhdm9pZHMgc3BlY2lhbCBjYXNlcyBhbmQgY2F0Y2hlcyBiYWQgdmFsdWVzIGVhcmxpZXIuXG4gIGlmIChmaWxlUHJlZml4ICE9PSB1bmRlZmluZWQpIHtcbiAgICBhc3NlcnRTYWZlRW50aXR5SWQoJ2ZpbGVQcmVmaXgnLCBmaWxlUHJlZml4KTtcbiAgfVxuXG4gIC8vIDEuIEdldCBhbGwgSlNPTiBmaWxlcyBpbiBkaXJlY3RvcnlcbiAgY29uc3QgZmlsZUlkcyA9IGF3YWl0IGxpc3RKU09ORmlsZXMoZGlyZWN0b3J5KTtcblxuICAvLyAyLiBGaWx0ZXIgYnkgcHJlZml4IGlmIHByb3ZpZGVkXG4gIGNvbnN0IHJlbGV2YW50RmlsZUlkcyA9IGZpbGVQcmVmaXhcbiAgICA/IGZpbGVJZHMuZmlsdGVyKChmaWxlSWQpID0+IGZpbGVJZC5zdGFydHNXaXRoKGZpbGVQcmVmaXgpKVxuICAgIDogZmlsZUlkcztcbiAgY29uc3QgZmlsdGVyZWRGaWxlSWRzID0gZmlsZUlkRmlsdGVyXG4gICAgPyByZWxldmFudEZpbGVJZHMuZmlsdGVyKGZpbGVJZEZpbHRlcilcbiAgICA6IHJlbGV2YW50RmlsZUlkcztcblxuICAvLyAzLiBVTElEIE9wdGltaXphdGlvbjogRmlsdGVyIGJ5IGN1cnNvciB1c2luZyBmaWxlbmFtZSB0aW1lc3RhbXBzIGJlZm9yZSBsb2FkaW5nIEpTT05cbiAgY29uc3QgcGFyc2VkQ3Vyc29yID0gcGFyc2VDdXJzb3IoY3Vyc29yKTtcbiAgbGV0IGNhbmRpZGF0ZUZpbGVJZHMgPSBmaWx0ZXJlZEZpbGVJZHM7XG5cbiAgaWYgKHBhcnNlZEN1cnNvcikge1xuICAgIGNhbmRpZGF0ZUZpbGVJZHMgPSBmaWx0ZXJlZEZpbGVJZHMuZmlsdGVyKChmaWxlSWQpID0+IHtcbiAgICAgIGNvbnN0IGZpbGVuYW1lRGF0ZSA9IGdldENyZWF0ZWRBdChgJHtmaWxlSWR9Lmpzb25gKTtcbiAgICAgIGlmIChmaWxlbmFtZURhdGUpIHtcbiAgICAgICAgLy8gVXNlIGZpbGVuYW1lIHRpbWVzdGFtcCBmb3IgY3Vyc29yIGZpbHRlcmluZ1xuICAgICAgICAvLyBXZSBuZWVkIHRvIGJlIGNhcmVmdWwgaGVyZTogaWYgcGFyc2VkQ3Vyc29yIGhhcyBhbiBJRCAoZm9yIHRpZS1icmVha2luZyksXG4gICAgICAgIC8vIHdlIG5lZWQgdG8gaW5jbHVkZSBpdGVtcyB3aXRoIHRoZSBzYW1lIHRpbWVzdGFtcCBmb3IgbGF0ZXIgSUQtYmFzZWQgZmlsdGVyaW5nLlxuICAgICAgICAvLyBJZiBubyBJRCwgd2UgY2FuIHVzZSBzdHJpY3QgaW5lcXVhbGl0eSBmb3Igb3B0aW1pemF0aW9uLlxuICAgICAgICBjb25zdCBjdXJzb3JUaW1lID0gcGFyc2VkQ3Vyc29yLnRpbWVzdGFtcC5nZXRUaW1lKCk7XG4gICAgICAgIGNvbnN0IGZpbGVUaW1lID0gZmlsZW5hbWVEYXRlLmdldFRpbWUoKTtcblxuICAgICAgICBpZiAocGFyc2VkQ3Vyc29yLmlkKSB7XG4gICAgICAgICAgLy8gVGllLWJyZWFraW5nIG1vZGU6IGluY2x1ZGUgaXRlbXMgYXQgb3IgbmVhciBjdXJzb3IgdGltZXN0YW1wXG4gICAgICAgICAgcmV0dXJuIHNvcnRPcmRlciA9PT0gJ2Rlc2MnXG4gICAgICAgICAgICA/IGZpbGVUaW1lIDw9IGN1cnNvclRpbWVcbiAgICAgICAgICAgIDogZmlsZVRpbWUgPj0gY3Vyc29yVGltZTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBObyB0aWUtYnJlYWtpbmc6IHN0cmljdCBpbmVxdWFsaXR5XG4gICAgICAgICAgcmV0dXJuIHNvcnRPcmRlciA9PT0gJ2Rlc2MnXG4gICAgICAgICAgICA/IGZpbGVUaW1lIDwgY3Vyc29yVGltZVxuICAgICAgICAgICAgOiBmaWxlVGltZSA+IGN1cnNvclRpbWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIC8vIENhbid0IGV4dHJhY3QgdGltZXN0YW1wIGZyb20gZmlsZW5hbWUgKGUuZy4sIHN0ZXBzIHVzZSBzZXF1ZW50aWFsIElEcykuXG4gICAgICAvLyBJbmNsdWRlIHRoZSBmaWxlIGFuZCBkZWZlciB0byBKU09OLWJhc2VkIGZpbHRlcmluZyBiZWxvdy5cbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0pO1xuICB9XG5cbiAgLy8gNC4gTG9hZCBmaWxlcyBpbmRpdmlkdWFsbHkgYW5kIGNvbGxlY3QgdmFsaWQgaXRlbXNcbiAgY29uc3QgdmFsaWRJdGVtczogVFtdID0gW107XG5cbiAgZm9yIChjb25zdCBmaWxlSWQgb2YgY2FuZGlkYXRlRmlsZUlkcykge1xuICAgIGNvbnN0IGZpbGVQYXRoID0gcGF0aC5qb2luKGRpcmVjdG9yeSwgYCR7ZmlsZUlkfS5qc29uYCk7XG4gICAgbGV0IGl0ZW06IFQgfCBudWxsID0gbnVsbDtcbiAgICB0cnkge1xuICAgICAgaXRlbSA9IGF3YWl0IHJlYWRKU09OKGZpbGVQYXRoLCBzY2hlbWEpO1xuICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAvLyBXZSBkb24ndCBleHBlY3Qgem9kIGVycm9ycyB0byBoYXBwZW4sIGJ1dCBpZiB0aGUgSlNPTiBkb2VzIGdldCBtYWxmb3JtZWQsXG4gICAgICAvLyB3ZSBza2lwIHRoZSBpdGVtLiBQcmVmZXJhYmx5LCB3ZSdkIGhhdmUgYSB3YXkgdG8gbWFyayBpdGVtcyBhcyBtYWxmb3JtZWQsXG4gICAgICAvLyBzbyB0aGF0IHRoZSBVSSBjYW4gZGlzcGxheSB0aGVtIGFzIHN1Y2gsIHdpdGggcmljaGVyIG1lc3NhZ2luZy4gSW4gdGhlIG1lYW50aW1lLFxuICAgICAgLy8gd2UganVzdCBsb2cgYSB3YXJuaW5nIGFuZCBza2lwIHRoZSBpdGVtLlxuICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2Ygei5ab2RFcnJvcikge1xuICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgYFNraXBwaW5nIGl0ZW0gJHtmaWxlSWR9IGR1ZSB0byBtYWxmb3JtZWQgSlNPTjogJHtlcnJvci5tZXNzYWdlfWBcbiAgICAgICAgKTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG5cbiAgICBpZiAoaXRlbSkge1xuICAgICAgLy8gQXBwbHkgY3VzdG9tIGZpbHRlciBlYXJseSBpZiBwcm92aWRlZFxuICAgICAgaWYgKGZpbHRlciAmJiAhZmlsdGVyKGl0ZW0pKSBjb250aW51ZTtcblxuICAgICAgLy8gRG91YmxlLWNoZWNrIGN1cnNvciBmaWx0ZXJpbmcgd2l0aCBhY3R1YWwgY3JlYXRlZEF0IGZyb20gSlNPTlxuICAgICAgLy8gKGluIGNhc2UgVUxJRCB0aW1lc3RhbXAgZGlmZmVycyBmcm9tIHN0b3JlZCBjcmVhdGVkQXQpXG4gICAgICBpZiAocGFyc2VkQ3Vyc29yKSB7XG4gICAgICAgIGNvbnN0IGl0ZW1UaW1lID0gaXRlbS5jcmVhdGVkQXQuZ2V0VGltZSgpO1xuICAgICAgICBjb25zdCBjdXJzb3JUaW1lID0gcGFyc2VkQ3Vyc29yLnRpbWVzdGFtcC5nZXRUaW1lKCk7XG5cbiAgICAgICAgaWYgKHNvcnRPcmRlciA9PT0gJ2Rlc2MnKSB7XG4gICAgICAgICAgLy8gRm9yIGRlc2NlbmRpbmcgb3JkZXIsIHNraXAgaXRlbXMgPj0gY3Vyc29yXG4gICAgICAgICAgaWYgKGl0ZW1UaW1lID4gY3Vyc29yVGltZSkgY29udGludWU7XG4gICAgICAgICAgLy8gSWYgdGltZXN0YW1wcyBhcmUgZXF1YWwsIHVzZSBJRCBmb3IgdGllLWJyZWFraW5nIChza2lwIGlmIElEID49IGN1cnNvcklkKVxuICAgICAgICAgIGlmIChpdGVtVGltZSA9PT0gY3Vyc29yVGltZSAmJiBwYXJzZWRDdXJzb3IuaWQgJiYgZ2V0SWQpIHtcbiAgICAgICAgICAgIGNvbnN0IGl0ZW1JZCA9IGdldElkKGl0ZW0pO1xuICAgICAgICAgICAgaWYgKGl0ZW1JZCA+PSBwYXJzZWRDdXJzb3IuaWQpIGNvbnRpbnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBGb3IgYXNjZW5kaW5nIG9yZGVyLCBza2lwIGl0ZW1zIDw9IGN1cnNvclxuICAgICAgICAgIGlmIChpdGVtVGltZSA8IGN1cnNvclRpbWUpIGNvbnRpbnVlO1xuICAgICAgICAgIC8vIElmIHRpbWVzdGFtcHMgYXJlIGVxdWFsLCB1c2UgSUQgZm9yIHRpZS1icmVha2luZyAoc2tpcCBpZiBJRCA8PSBjdXJzb3JJZClcbiAgICAgICAgICBpZiAoaXRlbVRpbWUgPT09IGN1cnNvclRpbWUgJiYgcGFyc2VkQ3Vyc29yLmlkICYmIGdldElkKSB7XG4gICAgICAgICAgICBjb25zdCBpdGVtSWQgPSBnZXRJZChpdGVtKTtcbiAgICAgICAgICAgIGlmIChpdGVtSWQgPD0gcGFyc2VkQ3Vyc29yLmlkKSBjb250aW51ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgdmFsaWRJdGVtcy5wdXNoKGl0ZW0pO1xuICAgIH1cbiAgfVxuXG4gIC8vIDUuIFNvcnQgYnkgY3JlYXRlZEF0IChhbmQgYnkgSUQgZm9yIHRpZS1icmVha2luZyBpZiBnZXRJZCBpcyBwcm92aWRlZClcbiAgdmFsaWRJdGVtcy5zb3J0KChhLCBiKSA9PiB7XG4gICAgY29uc3QgYVRpbWUgPSBhLmNyZWF0ZWRBdC5nZXRUaW1lKCk7XG4gICAgY29uc3QgYlRpbWUgPSBiLmNyZWF0ZWRBdC5nZXRUaW1lKCk7XG4gICAgY29uc3QgdGltZUNvbXBhcmlzb24gPSBzb3J0T3JkZXIgPT09ICdhc2MnID8gYVRpbWUgLSBiVGltZSA6IGJUaW1lIC0gYVRpbWU7XG5cbiAgICAvLyBJZiB0aW1lc3RhbXBzIGFyZSBlcXVhbCBhbmQgd2UgaGF2ZSBnZXRJZCwgdXNlIElEIGZvciBzdGFibGUgc29ydGluZ1xuICAgIGlmICh0aW1lQ29tcGFyaXNvbiA9PT0gMCAmJiBnZXRJZCkge1xuICAgICAgY29uc3QgYUlkID0gZ2V0SWQoYSk7XG4gICAgICBjb25zdCBiSWQgPSBnZXRJZChiKTtcbiAgICAgIHJldHVybiBzb3J0T3JkZXIgPT09ICdhc2MnXG4gICAgICAgID8gYUlkLmxvY2FsZUNvbXBhcmUoYklkKVxuICAgICAgICA6IGJJZC5sb2NhbGVDb21wYXJlKGFJZCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRpbWVDb21wYXJpc29uO1xuICB9KTtcblxuICAvLyA2LiBBcHBseSBwYWdpbmF0aW9uXG4gIGNvbnN0IGhhc01vcmUgPSB2YWxpZEl0ZW1zLmxlbmd0aCA+IGxpbWl0O1xuICBjb25zdCBpdGVtcyA9IGhhc01vcmUgPyB2YWxpZEl0ZW1zLnNsaWNlKDAsIGxpbWl0KSA6IHZhbGlkSXRlbXM7XG4gIGNvbnN0IG5leHRDdXJzb3IgPVxuICAgIGl0ZW1zLmxlbmd0aCA+IDBcbiAgICAgID8gY3JlYXRlQ3Vyc29yKFxuICAgICAgICAgIGl0ZW1zW2l0ZW1zLmxlbmd0aCAtIDFdLmNyZWF0ZWRBdCxcbiAgICAgICAgICBnZXRJZD8uKGl0ZW1zW2l0ZW1zLmxlbmd0aCAtIDFdKVxuICAgICAgICApXG4gICAgICA6IG51bGw7XG5cbiAgcmV0dXJuIHtcbiAgICBkYXRhOiBpdGVtcyxcbiAgICBjdXJzb3I6IG5leHRDdXJzb3IsXG4gICAgaGFzTW9yZSxcbiAgfTtcbn1cbiJdfQ==
package/dist/index.d.ts CHANGED
@@ -1,12 +1,26 @@
1
1
  import type { World } from '@workflow/world';
2
2
  import type { Config } from './config.js';
3
+ import { type DirectHandler } from './queue.js';
4
+ export { DataDirAccessError, DataDirVersionError, ensureDataDir, initDataDir, type ParsedVersion, parseVersion, } from './init.js';
5
+ export type { DirectHandler } from './queue.js';
6
+ export type LocalWorld = World & {
7
+ /** Register a direct in-process handler for a queue prefix, bypassing HTTP. */
8
+ registerHandler(prefix: '__wkf_step_' | '__wkf_workflow_', handler: DirectHandler): void;
9
+ /** Clear all workflow data (runs, steps, events, hooks, streams). */
10
+ clear(): Promise<void>;
11
+ };
3
12
  /**
4
- * Creates an embedded world instance that combines queue, storage, and streamer functionalities.
13
+ * Creates a local world instance that combines queue, storage, and streamer functionalities.
5
14
  *
6
15
  * @param args - Optional configuration object
7
16
  * @param args.dataDir - Directory for storing workflow data (default: `.workflow-data/`)
8
17
  * @param args.port - Port override for queue transport (default: auto-detected)
9
18
  * @param args.baseUrl - Full base URL override for queue transport (default: `http://localhost:{port}`)
19
+ * @param args.recoverActiveRuns - Whether `start()` should re-enqueue pending/running runs from storage (default: `true`)
20
+ * @param args.tag - Optional tag to scope files (e.g., `vitest-0`). When set, files are written
21
+ * as `{id}.{tag}.json` and `clear()` only deletes files matching this tag.
22
+ * @throws {DataDirAccessError} If the data directory cannot be created or accessed
23
+ * @throws {DataDirVersionError} If the data directory version is incompatible
10
24
  */
11
- export declare function createEmbeddedWorld(args?: Partial<Config>): World;
25
+ export declare function createLocalWorld(args?: Partial<Config>): LocalWorld;
12
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAM1C;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,CAYjE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAY1C,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAM7D,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,WAAW,EACX,KAAK,aAAa,EAClB,YAAY,GACb,MAAM,WAAW,CAAC;AAEnB,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG;IAC/B,+EAA+E;IAC/E,eAAe,CACb,MAAM,EAAE,aAAa,GAAG,iBAAiB,EACzC,OAAO,EAAE,aAAa,GACrB,IAAI,CAAC;IACR,qEAAqE;IACrE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,CA6GnE"}