@mikeyt23/node-cli-utils 1.4.1 → 2.0.0-beta.2

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 (76) hide show
  1. package/README.md +45 -87
  2. package/dist/cjs/GitUtility.d.ts +7 -0
  3. package/dist/cjs/GitUtility.d.ts.map +1 -0
  4. package/dist/cjs/GitUtility.js +49 -0
  5. package/dist/cjs/NodeCliUtilsConfig.d.ts +21 -0
  6. package/dist/cjs/NodeCliUtilsConfig.d.ts.map +1 -0
  7. package/dist/cjs/NodeCliUtilsConfig.js +41 -0
  8. package/dist/cjs/TarballUtility.d.ts +63 -0
  9. package/dist/cjs/TarballUtility.d.ts.map +1 -0
  10. package/dist/cjs/TarballUtility.js +143 -0
  11. package/dist/cjs/certUtils.d.ts +66 -0
  12. package/dist/cjs/certUtils.d.ts.map +1 -0
  13. package/dist/cjs/certUtils.js +283 -0
  14. package/dist/cjs/dbMigrationUtils.d.ts +39 -0
  15. package/dist/cjs/dbMigrationUtils.d.ts.map +1 -0
  16. package/dist/cjs/dbMigrationUtils.js +195 -0
  17. package/dist/cjs/dotnetUtils.d.ts +25 -0
  18. package/dist/cjs/dotnetUtils.d.ts.map +1 -0
  19. package/dist/cjs/dotnetUtils.js +61 -0
  20. package/dist/cjs/esmSpecific.d.mts +7 -0
  21. package/dist/cjs/esmSpecific.d.mts.map +1 -0
  22. package/dist/cjs/esmSpecific.mjs +15 -0
  23. package/dist/cjs/generalUtils.d.ts +565 -0
  24. package/dist/cjs/generalUtils.d.ts.map +1 -0
  25. package/dist/cjs/generalUtils.js +1068 -0
  26. package/dist/cjs/generalUtilsInternal.d.ts +15 -0
  27. package/dist/cjs/generalUtilsInternal.d.ts.map +1 -0
  28. package/dist/cjs/generalUtilsInternal.js +317 -0
  29. package/dist/cjs/hostsUtils.d.ts +16 -0
  30. package/dist/cjs/hostsUtils.d.ts.map +1 -0
  31. package/dist/cjs/hostsUtils.js +82 -0
  32. package/dist/cjs/index.d.ts +4 -0
  33. package/dist/cjs/index.d.ts.map +1 -0
  34. package/dist/cjs/index.js +25 -0
  35. package/dist/cjs/package.json +5 -0
  36. package/dist/cjs/runWhileParentAlive.d.ts +2 -0
  37. package/dist/cjs/runWhileParentAlive.d.ts.map +1 -0
  38. package/dist/cjs/runWhileParentAlive.js +159 -0
  39. package/dist/esm/GitUtility.d.ts +7 -0
  40. package/dist/esm/GitUtility.d.ts.map +1 -0
  41. package/dist/esm/GitUtility.js +43 -0
  42. package/dist/esm/NodeCliUtilsConfig.d.ts +21 -0
  43. package/dist/esm/NodeCliUtilsConfig.d.ts.map +1 -0
  44. package/dist/esm/NodeCliUtilsConfig.js +35 -0
  45. package/dist/esm/TarballUtility.d.ts +63 -0
  46. package/dist/esm/TarballUtility.d.ts.map +1 -0
  47. package/dist/esm/TarballUtility.js +139 -0
  48. package/dist/esm/certUtils.d.ts +66 -0
  49. package/dist/esm/certUtils.d.ts.map +1 -0
  50. package/dist/esm/certUtils.js +271 -0
  51. package/dist/esm/dbMigrationUtils.d.ts +39 -0
  52. package/dist/esm/dbMigrationUtils.d.ts.map +1 -0
  53. package/dist/esm/dbMigrationUtils.js +184 -0
  54. package/dist/esm/dotnetUtils.d.ts +25 -0
  55. package/dist/esm/dotnetUtils.d.ts.map +1 -0
  56. package/dist/esm/dotnetUtils.js +54 -0
  57. package/dist/esm/esmSpecific.d.mts +7 -0
  58. package/dist/esm/esmSpecific.d.mts.map +1 -0
  59. package/dist/esm/esmSpecific.mjs +11 -0
  60. package/dist/esm/generalUtils.d.ts +565 -0
  61. package/dist/esm/generalUtils.d.ts.map +1 -0
  62. package/dist/esm/generalUtils.js +976 -0
  63. package/dist/esm/generalUtilsInternal.d.ts +15 -0
  64. package/dist/esm/generalUtilsInternal.d.ts.map +1 -0
  65. package/dist/esm/generalUtilsInternal.js +278 -0
  66. package/dist/esm/hostsUtils.d.ts +16 -0
  67. package/dist/esm/hostsUtils.d.ts.map +1 -0
  68. package/dist/esm/hostsUtils.js +69 -0
  69. package/dist/esm/index.d.ts +4 -0
  70. package/dist/esm/index.d.ts.map +1 -0
  71. package/dist/esm/index.js +4 -0
  72. package/dist/esm/runWhileParentAlive.d.ts +2 -0
  73. package/dist/esm/runWhileParentAlive.d.ts.map +1 -0
  74. package/dist/esm/runWhileParentAlive.js +151 -0
  75. package/package.json +69 -10
  76. package/index.js +0 -627
@@ -0,0 +1,976 @@
1
+ import fs from 'node:fs';
2
+ import fsp from 'node:fs/promises';
3
+ import { platform } from 'node:os';
4
+ import path, { resolve } from 'node:path';
5
+ import * as readline from 'readline';
6
+ import * as net from 'net';
7
+ import { config } from './NodeCliUtilsConfig.js';
8
+ import { copyEnv, dictionaryToEnvFileString, getEnvAsDictionary, simpleSpawnAsyncInternal, simpleSpawnSyncInternal, spawnAsyncInternal, validateFindFilesRecursivelyParams, whichInternal } from './generalUtilsInternal.js';
9
+ const dockerComposeCommandsThatSupportDetached = ['exec', 'logs', 'ps', 'restart', 'run', 'start', 'stop', 'up'];
10
+ /**
11
+ * Just a wrapper for console.log() to type less.
12
+ * @param data The data to log
13
+ * @param moreData More data to log
14
+ */
15
+ export function log(data, ...moreData) {
16
+ console.log(data, ...moreData);
17
+ }
18
+ /**
19
+ * Log conditionally. Useful for methods that have an option to either suppress output or to show it when it normally isn't.
20
+ * @param data The data to log
21
+ * @param moreData More data to log
22
+ */
23
+ export function logIf(shouldLog, data, ...moreData) {
24
+ if (shouldLog) {
25
+ console.log(data, ...moreData);
26
+ }
27
+ }
28
+ /**
29
+ * Wrapper for console.log() that is suppressed if NodeCliUtilsConfig.logEnabled is false.
30
+ * @param data The data to log
31
+ * @param moreData More data to log
32
+ */
33
+ export function trace(data, ...moreData) {
34
+ if (config.traceEnabled) {
35
+ const prefix = `[TRACE]`;
36
+ console.log(prefix, data, ...moreData);
37
+ }
38
+ }
39
+ /**
40
+ * Error throw by {@link spawnAsync} when the spawned process exits with a non-zero exit code and options.throwOnNonZero is true.
41
+ *
42
+ * Contains a {@link SpawnResult} with the exit code, stdout, stderr, and error (if any).
43
+ */
44
+ export class SpawnError extends Error {
45
+ result;
46
+ constructor(message, result) {
47
+ super(message);
48
+ this.result = result;
49
+ }
50
+ }
51
+ /**
52
+ * Error throw by {@link simpleSpawnSync} and {@link simpleCmdSync} when the spawned process exits with a non-zero exit code and throwOnNonZero param is true (the default).
53
+ *
54
+ * Contains a {@link SimpleSpawnResult} with the exit code, stdout, stderr, and error (if any) in addition to stdoutLines, which is stdout split into lines from stdout that weren't empty.
55
+ */
56
+ export class SimpleSpawnError extends Error {
57
+ result;
58
+ constructor(message, result) {
59
+ super(message);
60
+ this.result = result;
61
+ }
62
+ }
63
+ /**
64
+ * Sleeps for the specified number of milliseconds.
65
+ * @param ms The number of milliseconds to sleep
66
+ * @returns A Promise that resolves after the specified number of milliseconds
67
+ */
68
+ export async function sleep(ms) {
69
+ return new Promise((resolve) => {
70
+ setTimeout(resolve, ms);
71
+ });
72
+ }
73
+ /**
74
+ * This is a wrapper function for NodeJS spawn. Defaults stdio to inherit so that output is visible in the console,
75
+ * but note that this means stdout and stderr will not be available in the returned SpawnResult. To hide the output
76
+ * from the console but collect the stdout and stderr in the SpawnResult, use stdio: 'pipe'.
77
+ *
78
+ * When spawning long-running processes, use {@link spawnAsyncLongRunning} instead so that unexpected
79
+ * termination of the parent process will not orphan the child process tree on windows.
80
+ *
81
+ * **Warning:** Do NOT use this for generating commands dynamically from user input as it could be used to execute arbitrary code.
82
+ * This is meant solely for building up known commands that are not made up of unsanitized user input, and only at compile time.
83
+ * See {@link winInstallCert} and {@link winUninstallCert} for examples of taking user input and inserting it safely into known commands.
84
+ * @param command The command to spawn
85
+ * @param args The arguments to pass to the command
86
+ * @param options The options to pass to the command
87
+ * @returns A Promise that resolves to a {@link SpawnResult}
88
+ */
89
+ export async function spawnAsync(command, args, options) {
90
+ return spawnAsyncInternal(command, args ?? [], options);
91
+ }
92
+ /**
93
+ * Use this alternate spawn wrapper instead of {@link spawnAsync} when spawning long-running processes to
94
+ * avoid orphaned child process trees on Windows.
95
+ *
96
+ * **Warning:** Do NOT use this for generating commands dynamically from user input as it could be used to execute arbitrary code.
97
+ * This is meant solely for building up known commands that are not made up of unsanitized user input, and only at compile time.
98
+ * See {@link winInstallCert} and {@link winUninstallCert} for examples of taking user input and inserting it safely into known commands.
99
+ * @param command The command to spawn
100
+ * @param args The arguments to pass to the command
101
+ * @param cwd The current working directory to run the command from - defaults to process.cwd()
102
+ * @returns A Promise that resolves to a {@link SpawnResult}
103
+ */
104
+ export async function spawnAsyncLongRunning(command, args, cwd) {
105
+ return spawnAsyncInternal(command, args ?? [], { cwd: cwd, isLongRunning: true });
106
+ }
107
+ /**
108
+ * Ensure the directory exists. Similar to `mkdir -p` (creates parent directories if they don't exist).
109
+ * @param dir The directory to ensure exists. If it does not exist, it will be created.
110
+ */
111
+ export async function ensureDirectory(dir) {
112
+ return await mkdirp(dir);
113
+ }
114
+ /**
115
+ * Create a directory. Will create parent directory structure if it don't exist. Similar to `mkdir -p`.
116
+ * @param dir The directory to create.
117
+ */
118
+ export async function mkdirp(dir) {
119
+ requireString('dir', dir);
120
+ await fsp.mkdir(dir, { recursive: true });
121
+ }
122
+ /**
123
+ * Create a directory. Will create parent directory structure if it don't exist. Similar to `mkdir -p`.
124
+ * @param dir The directory to create.
125
+ */
126
+ export async function mkdirpSync(dir) {
127
+ requireString('dir', dir);
128
+ fs.mkdirSync(dir, { recursive: true });
129
+ }
130
+ /**
131
+ * Empties a directory of all files and subdirectories.
132
+ *
133
+ * Optionally skips files and directories at the top level.
134
+ * @param directoryToEmpty The directory to empty.
135
+ * @param fileAndDirectoryNamesToSkip An optional array of file and directory names to skip, but only at the top level of the directoryToEmpty.
136
+ */
137
+ export async function emptyDirectory(directoryToEmpty, fileAndDirectoryNamesToSkip) {
138
+ requireString('directoryToEmpty', directoryToEmpty);
139
+ if (!fs.existsSync(directoryToEmpty)) {
140
+ trace(`directoryToEmpty does not exist - creating directory ${directoryToEmpty}`);
141
+ await mkdirp(directoryToEmpty);
142
+ return;
143
+ }
144
+ if (!fs.lstatSync(directoryToEmpty).isDirectory()) {
145
+ throw new Error(`directoryToEmpty is not a directory: ${directoryToEmpty}`);
146
+ }
147
+ // Add some guardrails to prevent accidentally emptying the wrong directory
148
+ const absolutePath = path.resolve(directoryToEmpty);
149
+ log(`emptying directory: ${absolutePath}`);
150
+ if (!absolutePath.startsWith(process.cwd())) {
151
+ throw new Error(`directoryToEmpty must be a child of the current working directory: ${directoryToEmpty}`);
152
+ }
153
+ if (absolutePath === process.cwd()) {
154
+ throw new Error(`directoryToEmpty cannot be the current working directory: ${directoryToEmpty}`);
155
+ }
156
+ const dir = await fsp.opendir(directoryToEmpty, { encoding: 'utf-8' });
157
+ if (fileAndDirectoryNamesToSkip && !Array.isArray(fileAndDirectoryNamesToSkip)) {
158
+ throw new Error('fileAndDirectoryNamesToSkip must be an array');
159
+ }
160
+ let dirEntry = await dir.read();
161
+ while (dirEntry) {
162
+ if (fileAndDirectoryNamesToSkip?.includes(dirEntry.name)) {
163
+ dirEntry = await dir.read();
164
+ continue;
165
+ }
166
+ const direntPath = path.join(directoryToEmpty, dirEntry.name);
167
+ if (dirEntry.isDirectory()) {
168
+ await fsp.rm(direntPath, { recursive: true });
169
+ }
170
+ else {
171
+ await fsp.unlink(direntPath);
172
+ }
173
+ dirEntry = await dir.read();
174
+ }
175
+ await dir.close();
176
+ }
177
+ /**
178
+ * Copies the contents of a directory to another directory (not including the top-level directory itself).
179
+ *
180
+ * If the destination directory does not exist, it will be created.
181
+ * @param sourceDirectory Directory to copy from
182
+ * @param destinationDirectory Directory to copy to
183
+ */
184
+ export async function copyDirectoryContents(sourceDirectory, destinationDirectory) {
185
+ requireString('sourceDirectory', sourceDirectory);
186
+ requireString('destinationDirectory', destinationDirectory);
187
+ if (!fs.existsSync(sourceDirectory)) {
188
+ throw new Error(`sourceDirectory directory does not exist: ${sourceDirectory}`);
189
+ }
190
+ if (!fs.lstatSync(sourceDirectory).isDirectory()) {
191
+ throw new Error(`sourceDirectory is not a directory: ${sourceDirectory}`);
192
+ }
193
+ if (!fs.existsSync(destinationDirectory)) {
194
+ await mkdirp(destinationDirectory);
195
+ }
196
+ if (!fs.lstatSync(destinationDirectory).isDirectory()) {
197
+ throw new Error(`destinationDirectory is not a directory: ${destinationDirectory}`);
198
+ }
199
+ const dir = await fsp.opendir(sourceDirectory, { encoding: 'utf-8' });
200
+ let dirEntry = await dir.read();
201
+ while (dirEntry) {
202
+ const sourcePath = path.join(sourceDirectory, dirEntry.name);
203
+ const destPath = path.join(destinationDirectory, dirEntry.name);
204
+ if (dirEntry.isDirectory()) {
205
+ await copyDirectoryContents(sourcePath, destPath);
206
+ }
207
+ else {
208
+ await fsp.copyFile(sourcePath, destPath);
209
+ }
210
+ dirEntry = await dir.read();
211
+ }
212
+ }
213
+ /**
214
+ * Helper method to validate that a non-falsy and non-empty value is provided for a parameter that should be a string.
215
+ * @param paramName The name of the parameter to be used in the error message
216
+ * @param paramValue The value of the parameter
217
+ */
218
+ export function requireString(paramName, paramValue) {
219
+ if (paramValue === undefined || paramValue === null || paramValue === '' || typeof paramValue !== 'string' || paramValue.trim() === '') {
220
+ throw new Error(`Required param '${paramName}' is missing`);
221
+ }
222
+ }
223
+ /**
224
+ * Helper method to validate that the path actually exists for the provided value.
225
+ * @param paramName The name of the parameter, for logging purposes
226
+ * @param paramValue The value of the parameter
227
+ */
228
+ export function requireValidPath(paramName, paramValue) {
229
+ requireString(paramName, paramValue);
230
+ if (!fs.existsSync(paramValue)) {
231
+ throw new Error(`Invalid or nonexistent path provided for param '${paramName}': ${paramValue}`);
232
+ }
233
+ }
234
+ /**
235
+ * Project names must contain only lowercase letters, decimal digits, dashes, and underscores, and must begin with a lowercase letter or decimal digit.
236
+ *
237
+ * See https://docs.docker.com/compose/environment-variables/envvars/#compose_project_name.
238
+ * @param projectName The string to validate
239
+ * @returns `true` if it's a valid docker compose project name and `false` otherwise
240
+ */
241
+ export function isDockerComposeProjectNameValid(projectName) {
242
+ requireString('projectName', projectName);
243
+ // Ensure first char is a lowercase letter or digit
244
+ if (!/^[a-z0-9]/.test(projectName[0])) {
245
+ return false;
246
+ }
247
+ // Ensure the rest of the chars are only lowercase letters, digits, dashes and underscores
248
+ return /^[a-z0-9-_]+$/.test(projectName);
249
+ }
250
+ /**
251
+ * For docker compose commands, see https://docs.docker.com/compose/reference/. For available options for this wrapper function, see {@link DockerComposeOptions}.
252
+ *
253
+ * The current working directory will be the directory of the {@link dockerComposePath} unless specified in the options. This ensures relative paths in the
254
+ * docker compose file will be relative to itself by default.
255
+ *
256
+ * See {@link DockerComposeOptions.projectName} for info on where to locate your docker compose file and how to specify the docker project name.
257
+ * @param dockerComposePath Path to docker-compose.yml
258
+ * @param dockerComposeCommand The docker-compose command to run
259
+ * @param options {@link DockerComposeOptions} to use, including additional arguments to pass to the docker compose command and the project name
260
+ */
261
+ export async function spawnDockerCompose(dockerComposePath, dockerComposeCommand, options) {
262
+ requireValidPath('dockerComposePath', dockerComposePath);
263
+ requireString('dockerComposeCommand', dockerComposeCommand);
264
+ if (options?.cwd) {
265
+ requireValidPath('cwd', options.cwd);
266
+ }
267
+ if (options?.projectName && !isDockerComposeProjectNameValid(options.projectName)) {
268
+ throw new Error('Invalid docker compose project name specified for the projectName param. Project names must contain only lowercase letters, decimal digits, dashes, and underscores, and must begin with a lowercase letter or decimal digit.');
269
+ }
270
+ if (options?.profile && !/[a-zA-Z0-9][a-zA-Z0-9_.-]+/.test(options.profile)) {
271
+ throw new Error('Invalid profile option - must match regex: [a-zA-Z0-9][a-zA-Z0-9_.-]+');
272
+ }
273
+ if (!await isDockerRunning()) {
274
+ throw new Error('Docker is not running');
275
+ }
276
+ const defaultOptions = { args: [], attached: false, projectName: undefined, cwd: undefined };
277
+ const mergedOptions = { ...defaultOptions, ...options };
278
+ const dockerComposeDir = path.dirname(dockerComposePath);
279
+ const dockerComposeFilename = path.basename(dockerComposePath);
280
+ if (!mergedOptions.cwd) {
281
+ mergedOptions.cwd = dockerComposeDir;
282
+ }
283
+ let spawnArgs = ['compose', '-f', mergedOptions.cwd ? path.resolve(dockerComposePath) : dockerComposeFilename];
284
+ if (mergedOptions.projectName) {
285
+ spawnArgs.push('--project-name', mergedOptions.projectName);
286
+ }
287
+ if (mergedOptions.profile) {
288
+ spawnArgs.push('--profile', mergedOptions.profile);
289
+ }
290
+ spawnArgs.push(dockerComposeCommand);
291
+ if (!mergedOptions.attached && dockerComposeCommandsThatSupportDetached.includes(dockerComposeCommand)) {
292
+ spawnArgs.push('--detach');
293
+ }
294
+ if (mergedOptions.args) {
295
+ spawnArgs = spawnArgs.concat(mergedOptions.args);
296
+ }
297
+ trace(`running command in ${mergedOptions.cwd}: docker ${spawnArgs.join(' ')}`);
298
+ const longRunning = dockerComposeCommandsThatSupportDetached.includes(dockerComposeCommand) && options?.attached === true;
299
+ trace(`docker compose command will be configured to use long running options: ${longRunning}`);
300
+ const spawnOptions = {
301
+ cwd: mergedOptions.cwd,
302
+ shell: isPlatformWindows(),
303
+ isLongRunning: longRunning
304
+ };
305
+ const spawnResult = await spawnAsyncInternal('docker', spawnArgs, spawnOptions);
306
+ if (spawnResult.code !== 0) {
307
+ throw new Error(`docker compose command failed with code ${spawnResult.code}`);
308
+ }
309
+ }
310
+ /**
311
+ * Splits a string into lines, removing `\n` and `\r` characters. Does not return empty lines. Also see {@link stringToLines}.
312
+ * @param str String to split into lines
313
+ * @returns An array of lines from the string, with empty lines removed
314
+ */
315
+ export function stringToNonEmptyLines(str) {
316
+ if (!str) {
317
+ return [];
318
+ }
319
+ return str.split('\n').filter(line => line?.trim()).map(line => line.replace('\r', ''));
320
+ }
321
+ /**
322
+ * Splits a string into lines, removing `\n` and `\r` characters. Returns empty lines. Also see {@link stringToNonEmptyLines}.
323
+ * @param str String to split into lines
324
+ * @returns An array of lines from the string, with empty lines removed
325
+ */
326
+ export function stringToLines(str) {
327
+ if (!str) {
328
+ return [];
329
+ }
330
+ return str.split('\n').map(line => line.replace('\r', ''));
331
+ }
332
+ /**
333
+ * Runs the requested command using NodeJS spawnSync wrapped in an outer Windows CMD.exe command and returns the result with stdout split into lines.
334
+ *
335
+ * Use this for simple quick commands that don't require a lot of control.
336
+ *
337
+ * For commands that aren't Windows and CMD specific, use {@link simpleSpawnSync}.
338
+ *
339
+ * **Warning:** Do NOT use this for generating commands dynamically from user input as it could be used to execute arbitrary code.
340
+ * This is meant solely for building up known commands that are not made up of unsanitized user input, and only at compile time.
341
+ * See {@link winInstallCert} and {@link winUninstallCert} for examples of taking user input and inserting it safely into known commands.
342
+ * @param command Command to run
343
+ * @param args Arguments to pass to the command
344
+ * @returns An object with the status code, stdout, stderr, and error (if any)
345
+ * @throws {@link SimpleSpawnError} if the command fails and throwOnNonZero is true
346
+ */
347
+ export function simpleCmdSync(command, args, throwOnNonZero = true) {
348
+ if (!isPlatformWindows()) {
349
+ throw new Error('getCmdResult is only supported on Windows');
350
+ }
351
+ // Was previously spawning 'cmd' directly with params '/D', '/S', '/C' - but we may as well let NodeJS do the work of escaping args to work correctly with cmd
352
+ return simpleSpawnSyncInternal(command, args, throwOnNonZero, true);
353
+ }
354
+ /**
355
+ * Runs the requested command using {@link spawnAsync} wrapped in an outer Windows CMD.exe command and returns the result with stdout split into lines.
356
+ *
357
+ * Use this for simple quick commands that don't require a lot of control.
358
+ *
359
+ * For commands that aren't Windows and CMD specific, use {@link simpleSpawnAsync}.
360
+ *
361
+ * **Warning:** Do NOT use this for generating commands dynamically from user input as it could be used to execute arbitrary code.
362
+ * This is meant solely for building up known commands that are not made up of unsanitized user input, and only at compile time.
363
+ * See {@link winInstallCert} and {@link winUninstallCert} for examples of taking user input and inserting it safely into known commands.
364
+ * @param command Command to run
365
+ * @param args Arguments to pass to the command
366
+ * @returns An object with the status code, stdout, stderr, and error (if any)
367
+ * @throws {@link SimpleSpawnError} if the command fails and throwOnNonZero is true
368
+ */
369
+ export async function simpleCmdAsync(command, args, throwOnNonZero = true) {
370
+ if (!isPlatformWindows()) {
371
+ throw new Error('getCmdResult is only supported on Windows');
372
+ }
373
+ // Was previously spawning 'cmd' directly with params '/D', '/S', '/C' - but we may as well let NodeJS do the work of escaping args to work correctly with cmd
374
+ return await simpleSpawnAsyncInternal(command, args, throwOnNonZero, true);
375
+ }
376
+ /**
377
+ * Runs the requested command using NodeJS spawnSync and returns the result with stdout split into lines.
378
+ *
379
+ * Use this for simple quick commands that don't require a lot of control.
380
+ *
381
+ * For commands that are Windows and CMD specific, use {@link simpleCmdSync}.
382
+ *
383
+ * **Warning:** Do NOT use this for generating commands dynamically from user input as it could be used to execute arbitrary code.
384
+ * This is meant solely for building up known commands that are not made up of unsanitized user input, and only at compile time.
385
+ * See {@link winInstallCert} and {@link winUninstallCert} for examples of taking user input and inserting it safely into known commands.
386
+ * @param command Command to run
387
+ * @param args Arguments to pass to the command
388
+ * @returns An object with the status code, stdout, stderr, and error (if any)
389
+ * @throws {@link SimpleSpawnError} if the command fails and throwOnNonZero is true
390
+ */
391
+ export function simpleSpawnSync(command, args, throwOnNonZero = true) {
392
+ return simpleSpawnSyncInternal(command, args, throwOnNonZero);
393
+ }
394
+ /**
395
+ * Runs the requested command using {@link spawnAsync} and returns the result with stdout split into lines.
396
+ *
397
+ * Use this for simple quick commands that don't require a lot of control.
398
+ *
399
+ * For commands that are Windows and CMD specific, use {@link simpleCmdSync}.
400
+ *
401
+ * **Warning:** Do NOT use this for generating commands dynamically from user input as it could be used to execute arbitrary code.
402
+ * This is meant solely for building up known commands that are not made up of unsanitized user input, and only at compile time.
403
+ * See {@link winInstallCert} and {@link winUninstallCert} for examples of taking user input and inserting it safely into known commands.
404
+ * @param command Command to run
405
+ * @param args Arguments to pass to the command
406
+ * @returns An object with the status code, stdout, stderr, and error (if any)
407
+ * @throws {@link SimpleSpawnError} if the command fails and throwOnNonZero is true
408
+ */
409
+ export async function simpleSpawnAsync(command, args, throwOnNonZero = true) {
410
+ return await simpleSpawnAsyncInternal(command, args, throwOnNonZero);
411
+ }
412
+ /**
413
+ * @returns `true` if platform() is 'win32', `false` otherwise
414
+ */
415
+ export function isPlatformWindows() {
416
+ return platform() === 'win32';
417
+ }
418
+ /**
419
+ *
420
+ * @returns `true` if platform() is 'darwin', `false` otherwise
421
+ */
422
+ export function isPlatformMac() {
423
+ return platform() === 'darwin';
424
+ }
425
+ /**
426
+ *
427
+ * @returns `true` if {@link isPlatformWindows} and {@link isPlatformMac} are both `false, otherwise returns `true`
428
+ */
429
+ export function isPlatformLinux() {
430
+ return !isPlatformWindows() && !isPlatformMac();
431
+ }
432
+ /**
433
+ * This is a cross-platform method to get the location of a system command. Useful for checking if software
434
+ * is installed, where it's installed and whether there are multiple locations.
435
+ * @param commandName The name of the command to find
436
+ * @returns The location of the command, any additional locations, and an error if one occurred
437
+ */
438
+ export async function which(commandName) {
439
+ return whichInternal(commandName, simpleCmdAsync, simpleSpawnAsync);
440
+ }
441
+ /**
442
+ * This is a cross-platform method to get the location of a system command. Useful for checking if software
443
+ * is installed, where it's installed and whether there are multiple locations.
444
+ * @param commandName The name of the command to find
445
+ * @returns The location of the command, any additional locations, and an error if one occurred
446
+ */
447
+ export function whichSync(commandName) {
448
+ return whichInternal(commandName, simpleCmdSync, simpleSpawnSync);
449
+ }
450
+ /**
451
+ * First checks if docker is installed and if not immediately returns false. Then runs the `docker info`
452
+ * command and looks for "error during connect" in the output to determine if docker is running.
453
+ * @returns `true` if docker is installed and running, `false` otherwise
454
+ */
455
+ export async function isDockerRunning() {
456
+ if (!whichSync('docker').location) {
457
+ trace('whichSync will not check if docker is running because docker does not appear to be installed - returning false');
458
+ return false;
459
+ }
460
+ try {
461
+ const result = await simpleSpawnAsync('docker', ['info']);
462
+ return !result.stdout.includes('error during connect');
463
+ }
464
+ catch (err) {
465
+ return false;
466
+ }
467
+ }
468
+ /**
469
+ * Uses built-in NodeJS readline to ask a question and return the user's answer.
470
+ * @param query The question to ask
471
+ * @returns A Promise that resolves to the user's answer
472
+ */
473
+ export function askQuestion(query) {
474
+ const rl = readline.createInterface({
475
+ input: process.stdin,
476
+ output: process.stdout,
477
+ });
478
+ return new Promise(resolve => rl.question(`\n${query}\n`, ans => {
479
+ rl.close();
480
+ resolve(ans);
481
+ }));
482
+ }
483
+ /**
484
+ * A simple CLI prompt using the built-in NodeJS readline functionality to ask for confirmation.
485
+ * @param question The question to ask
486
+ * @returns A Promise that resolves to true if the user answers 'y' or 'yes', false otherwise
487
+ */
488
+ export function getConfirmation(question) {
489
+ const rl = readline.createInterface({
490
+ input: process.stdin,
491
+ output: process.stdout,
492
+ });
493
+ return new Promise((resolve) => {
494
+ rl.question(`\n ${Emoji.RedQuestion} ${question}\n ${Emoji.RightArrow} Proceed? (yes/no): `, (answer) => {
495
+ rl.close();
496
+ const confirmed = answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
497
+ log(confirmed ? ` ${Emoji.GreenCheck} Proceeding\n` : ` ${Emoji.RedX} Aborting\n`);
498
+ resolve(confirmed);
499
+ });
500
+ });
501
+ }
502
+ /**
503
+ * Example of using {@link getConfirmation}.
504
+ */
505
+ export async function getConfirmationExample() {
506
+ if (await getConfirmation('Do you even?')) {
507
+ log('you do even');
508
+ }
509
+ else {
510
+ log('you do not even');
511
+ }
512
+ }
513
+ /**
514
+ * Copy entries from a source .env file to a destination .env file for which the destination .env file does not already have entries.
515
+ * If the destination .env file does not exist, it will be created and populated with the source .env file's values.
516
+ *
517
+ * This is useful for copying values from a .env.template file to a root .env file.
518
+ *
519
+ * For copying root .env files to other locations, use {@link overwriteEnvFile}.
520
+ * @param sourcePath The path to the source .env file such as a `.env.template` file (use {@link overwriteEnvFile} for copying root .env files to other locations)
521
+ * @param destinationPath The path to the destination .env file, such as the root .env file
522
+ */
523
+ export async function copyNewEnvValues(sourcePath, destinationPath) {
524
+ await copyEnv(sourcePath, destinationPath, false);
525
+ }
526
+ /**
527
+ * Copy entries from a source .env file to a destination .env file, overwriting any existing entries in the destination .env file.
528
+ * If the destination .env file does not exist, it will be created and populated with the source .env file's values.
529
+ *
530
+ * This is useful for copying values from a root .env file to additional locations (server, client, docker-compose directory, etc.)
531
+ * throughout your solution so you only have to manage one .env file.
532
+ *
533
+ * Note that this does not delete any existing entries in the destination .env file, which is useful if you have additional entries in
534
+ * the destination .env file that you don't want to overwrite.
535
+ *
536
+ * For copying .env.template files to root .env files, use {@link copyNewEnvValues}.
537
+ * @param sourcePath The path to the source .env file such as a root .env file (use {@link copyNewEnvValues} for .env.template files)
538
+ * @param destinationPath The path to the destination .env file
539
+ * @param suppressAddKeysMessages If true, messages about adding missing keys will not be logged (useful if you're always calling {@link copyModifiedEnv} after this call)
540
+ */
541
+ export async function overwriteEnvFile(sourcePath, destinationPath, suppressAddKeysMessages = false) {
542
+ await copyEnv(sourcePath, destinationPath, true, suppressAddKeysMessages);
543
+ }
544
+ /**
545
+ * Copy entries from a source .env file to a destination .env file, but only for the keys specified in keepKeys.
546
+ * Will also modify entries in the destination .env file as specified in modifyEntries.
547
+ * @param sourcePath The path to the source .env file
548
+ * @param destinationPath The path to the destination .env file
549
+ * @param keepKeys The keys to keep from the source .env file
550
+ * @param modifyEntries The entries to modify in the destination .env file
551
+ */
552
+ export async function copyModifiedEnv(sourcePath, destinationPath, keepKeys, modifyEntries) {
553
+ requireValidPath('sourcePath', sourcePath);
554
+ const destPathDir = path.dirname(destinationPath);
555
+ if (!fs.existsSync(destPathDir)) {
556
+ await ensureDirectory(destPathDir);
557
+ }
558
+ const sourceDict = getEnvAsDictionary(sourcePath);
559
+ const newDict = filterDictionary(sourceDict, key => keepKeys.includes(key));
560
+ if (modifyEntries && Object.keys(modifyEntries).length > 0) {
561
+ for (const [key, value] of Object.entries(modifyEntries)) {
562
+ newDict[key] = value;
563
+ }
564
+ }
565
+ const newSortedDict = sortDictionaryByKeyAsc(newDict);
566
+ const newEnvFileContent = dictionaryToEnvFileString(newSortedDict);
567
+ await fsp.writeFile(destinationPath, newEnvFileContent);
568
+ }
569
+ /**
570
+ * Filters a dictionary by key.
571
+ * @param dict The dictionary to filter
572
+ * @param predicate A function that returns true if the key should be included in the filtered dictionary
573
+ * @returns A new dictionary with only the keys that passed the predicate
574
+ */
575
+ export function filterDictionary(dict, predicate) {
576
+ // Notes to self:
577
+ // - The second param of reduce is the initial value of the accumulator
578
+ // - Reduce processes each element of the array and returns the accumulator for the next iteration
579
+ // - In our case, the accumulator is a new dictionary that we're building up
580
+ return Object.keys(dict)
581
+ .filter(predicate)
582
+ .reduce((accumulator, key) => {
583
+ accumulator[key] = dict[key];
584
+ return accumulator;
585
+ }, {});
586
+ }
587
+ /**
588
+ * Sorts a dictionary by key in ascending order.
589
+ * @param dict The dictionary to sort
590
+ * @returns A new dictionary sorted by key in ascending order
591
+ */
592
+ export function sortDictionaryByKeyAsc(dict) {
593
+ const newSortedDict = Object.entries(dict).sort((a, b) => {
594
+ if (a < b) {
595
+ return -1;
596
+ }
597
+ if (a > b) {
598
+ return 1;
599
+ }
600
+ return 0;
601
+ });
602
+ return Object.fromEntries(newSortedDict);
603
+ }
604
+ /**
605
+ * Helper method to delete a .env file if it exists.
606
+ * @param envPath The path to the .env file to delete
607
+ */
608
+ export async function deleteEnvIfExists(envPath) {
609
+ // Just protecting ourselves from accidentally deleting something we didn't mean to
610
+ if (envPath.endsWith('.env') === false) {
611
+ throw new Error(`envPath must end with '.env': ${envPath}`);
612
+ }
613
+ // Using fsp.unlink will throw an error if it's a directory
614
+ if (fs.existsSync(envPath)) {
615
+ await fsp.unlink(envPath);
616
+ }
617
+ }
618
+ /**
619
+ * Searches a directory recursively for files that match the specified pattern.
620
+ * The filenamePattern is a simple text string with asterisks (*) for wildcards.
621
+ * @param dir The directory to find files in
622
+ * @param filenamePattern The pattern to match files against
623
+ * @param options Specify a max depth to search, defaults to 5
624
+ * @returns A Promise that resolves to an array of file paths that match the pattern
625
+ */
626
+ export async function findFilesRecursively(dir, filenamePattern, options) {
627
+ validateFindFilesRecursivelyParams(dir, filenamePattern);
628
+ const defaultOptions = { maxDepth: 5, excludeDirectoryNames: [], returnForwardSlashRelativePaths: false };
629
+ const mergedOptions = { ...defaultOptions, ...options };
630
+ // Convert the pattern to a regex
631
+ const regex = new RegExp('^' + filenamePattern.split(/\*+/).map(escapeStringForRegex).join('.*') + '$');
632
+ const matches = [];
633
+ // Recursive function to search within directories
634
+ async function searchDirectory(directory, depth) {
635
+ if (depth > mergedOptions.maxDepth)
636
+ return;
637
+ const entries = await fsp.readdir(directory, { withFileTypes: true });
638
+ for (const entry of entries) {
639
+ const fullPath = resolve(directory, entry.name);
640
+ if (entry.isDirectory()) {
641
+ // Check if directory is in the exclude list
642
+ if (!mergedOptions.excludeDirectoryNames?.includes(entry.name)) {
643
+ await searchDirectory(fullPath, depth + 1);
644
+ }
645
+ }
646
+ else if (entry.isFile() && regex.test(entry.name)) {
647
+ if (mergedOptions.returnForwardSlashRelativePaths) {
648
+ matches.push(path.relative(dir, fullPath).replace(/\\/g, '/'));
649
+ }
650
+ else {
651
+ matches.push(fullPath);
652
+ }
653
+ }
654
+ }
655
+ }
656
+ await searchDirectory(dir, 1); // Start search from the first depth
657
+ return matches;
658
+ }
659
+ /** Utility function to escape a string for use within regex */
660
+ export function escapeStringForRegex(str) {
661
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
662
+ }
663
+ /**
664
+ * Logs the provided 2-dimensional string array as a formatted table.
665
+ *
666
+ * @param data 2-dimensional string array where the first row is the column headers
667
+ * @example
668
+ *
669
+ * logTable([
670
+ * ['Name', 'Age', 'Country'],
671
+ * ['Alice', '28', 'USA'],
672
+ * ['Bob', '22', 'Canada']
673
+ * ])
674
+ */
675
+ export function logTable(data) {
676
+ if (data.length === 0 || data[0].length === 0)
677
+ return;
678
+ const numColumns = data[0].length;
679
+ const columnWidths = [];
680
+ for (let i = 0; i < numColumns; i++) {
681
+ columnWidths[i] = Math.max(...data.map(row => row[i]?.length || 0));
682
+ }
683
+ const lineSeparator = columnWidths.map(width => '-'.repeat(width)).join(' + ');
684
+ for (let i = 0; i < data.length; i++) {
685
+ const paddedRowArray = data[i].map((cell, colIdx) => cell.padEnd(columnWidths[colIdx], ' '));
686
+ log(paddedRowArray.join(' | '));
687
+ if (i === 0)
688
+ log(lineSeparator);
689
+ }
690
+ }
691
+ /**
692
+ * See {@link getPowershellHackArgs}.
693
+ */
694
+ export const powershellHackPrefix = `$env:PSModulePath = [Environment]::GetEnvironmentVariable('PSModulePath', 'Machine'); `;
695
+ /**
696
+ * Powershell doesn't load the system PSModulePath when running in a non-interactive shell.
697
+ * This is a workaround to set the PSModulePath environment variable to the system value before running a powershell command.
698
+ *
699
+ * **Warning:** Do NOT use this for generating commands dynamically from user input as it could be used to execute arbitrary code.
700
+ * This is meant solely for building up known commands that are not made up of unsanitized user input, and only at compile time.
701
+ * See {@link winInstallCert} and {@link winUninstallCert} for examples of taking user input and inserting it safely into known commands.
702
+ * @param command The powershell command to run
703
+ * @returns An array of arguments to pass to {@link spawnAsync} with the "powershell" command as the first argument
704
+ */
705
+ export function getPowershellHackArgs(command) {
706
+ return ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', `${powershellHackPrefix}${command}`];
707
+ }
708
+ /**
709
+ * Returns a humanized string representation of the number of milliseconds using ms, seconds, minutes, or hours.
710
+ * @param milliseconds The number of milliseconds to humanize
711
+ * @returns A humanized string representation of the number
712
+ */
713
+ export function humanizeTime(milliseconds) {
714
+ let value;
715
+ let unit;
716
+ if (milliseconds < 1000) {
717
+ return `${milliseconds} ms`;
718
+ }
719
+ if (milliseconds < 60000) {
720
+ value = milliseconds / 1000;
721
+ unit = 'second';
722
+ }
723
+ else if (milliseconds < 3600000) {
724
+ value = milliseconds / 60000;
725
+ unit = 'minute';
726
+ }
727
+ else {
728
+ value = milliseconds / 3600000;
729
+ unit = 'hour';
730
+ }
731
+ let stringValue = value.toFixed(2);
732
+ if (stringValue.endsWith('.00')) {
733
+ stringValue = stringValue.slice(0, -3);
734
+ }
735
+ else if (stringValue.endsWith('0')) {
736
+ stringValue = stringValue.slice(0, -1);
737
+ }
738
+ if (stringValue !== '1') {
739
+ unit += 's';
740
+ }
741
+ return `${stringValue} ${unit}`;
742
+ }
743
+ export class ExtendedError extends Error {
744
+ innerError;
745
+ constructor(message, innerError) {
746
+ super(message);
747
+ this.innerError = innerError ?? null;
748
+ Object.setPrototypeOf(this, ExtendedError.prototype);
749
+ }
750
+ }
751
+ export function getHostname(url) {
752
+ requireString('url', url);
753
+ trace(`attempting to convert url to hostname: ${url}`);
754
+ try {
755
+ const encodedUrl = encodeURI(url);
756
+ const parsedUrl = new URL(encodedUrl.startsWith('http') ? encodedUrl : 'https://' + encodedUrl);
757
+ trace(`parsed url: ${parsedUrl}`);
758
+ return parsedUrl.hostname;
759
+ }
760
+ catch (e) {
761
+ throw new ExtendedError("Invalid URL", e);
762
+ }
763
+ }
764
+ export async function isDirectory(path) {
765
+ try {
766
+ const stats = await fsp.stat(path);
767
+ return stats.isDirectory();
768
+ }
769
+ catch (err) {
770
+ trace('error checking idDirectory (returning false)', err);
771
+ return false;
772
+ }
773
+ }
774
+ export function isDirectorySync(path) {
775
+ try {
776
+ const stats = fs.statSync(path);
777
+ return stats.isDirectory();
778
+ }
779
+ catch (err) {
780
+ trace('error checking idDirectory (returning false)', err);
781
+ return false;
782
+ }
783
+ }
784
+ /**
785
+ * This is a somewhat naive method but is useful if you rarely or never deal with unusual operating systems.
786
+ * @returns `win`, `mac` or `linux`
787
+ */
788
+ export function getPlatformCode() {
789
+ if (isPlatformWindows()) {
790
+ return 'win';
791
+ }
792
+ if (isPlatformMac()) {
793
+ return 'mac';
794
+ }
795
+ if (isPlatformLinux()) {
796
+ return 'linux';
797
+ }
798
+ throw new Error('unrecognized platform: ' + platform());
799
+ }
800
+ /**
801
+ * Tries connecting to a port to see if it's being listened on or not. It's likely that this won't work in a lot of scenarios, so use it at your own risk.
802
+ * @param port The port to check
803
+ * @returns `true` if the port is available, `false` otherwise
804
+ */
805
+ export async function isPortAvailable(port) {
806
+ return new Promise((resolve) => {
807
+ const tester = net.connect(port, '127.0.0.1');
808
+ tester.on('connect', () => {
809
+ tester.destroy();
810
+ resolve(false); // port is in use
811
+ });
812
+ tester.on('error', (err) => {
813
+ tester.destroy();
814
+ if (err.code === 'ECONNREFUSED') {
815
+ resolve(true); // port is available
816
+ }
817
+ else {
818
+ resolve(false); // some other error occurred, assume port is in use
819
+ }
820
+ });
821
+ });
822
+ }
823
+ /**
824
+ * Returns the value for an environment variable or throws if it's undefined or null. Pass optional {@param throwOnEmpty} to throw when the key exists but has an empty value.
825
+ * @param varName The name of the environment variable to get.
826
+ * @param throwOnEmpty Throw an error if key exists (not undefined or null) but is empty.
827
+ * @returns
828
+ */
829
+ export function getRequiredEnvVar(varName, throwOnEmpty = true) {
830
+ requireString('varName', varName);
831
+ const val = process.env[varName];
832
+ if (val === undefined || val === null) {
833
+ throw new Error(`Missing required environment variable: ${varName}`);
834
+ }
835
+ if (throwOnEmpty && val.trim() === '') {
836
+ throw new Error(`Required environment variable is empty: ${varName}`);
837
+ }
838
+ return val;
839
+ }
840
+ export function getNormalizedError(err) {
841
+ let lastErrorAsError;
842
+ if (err === undefined || err === null) {
843
+ lastErrorAsError = new Error('lastError was undefined or null');
844
+ }
845
+ else if (err instanceof Error) {
846
+ lastErrorAsError = err;
847
+ }
848
+ else if (typeof err === 'string') {
849
+ lastErrorAsError = new Error(err);
850
+ }
851
+ else if (err instanceof Object) {
852
+ try {
853
+ lastErrorAsError = new Error(JSON.stringify(err));
854
+ }
855
+ catch (jsonError) {
856
+ lastErrorAsError = new Error('Object could not be serialized - could not normalize');
857
+ }
858
+ }
859
+ else {
860
+ lastErrorAsError = new Error(`Unknown error of type ${typeof err} - could not normalize`);
861
+ }
862
+ return lastErrorAsError;
863
+ }
864
+ /**
865
+ * Call a function until it succeeds. Will stop after the number of calls specified by {@param maxCalls}, or forever if -1 is passed.
866
+ * @param func The function to call
867
+ * @param maxCalls The maximum number of times to call the function before giving up. Pass -1 to retry forever.
868
+ * @param delayMilliseconds The number of milliseconds to wait between calls
869
+ * @param options Options for controlling the behavior of the retry. See {@link WithRetryOptions}.
870
+ */
871
+ export async function withRetryAsync(func, maxCalls, delayMilliseconds, options) {
872
+ let attemptNumber = 0;
873
+ let lastError;
874
+ const forever = maxCalls === -1;
875
+ const defaultOptions = { initialDelayMilliseconds: 0, traceEnabled: false };
876
+ const mergedOptions = { ...defaultOptions, ...options };
877
+ const shouldLog = config.traceEnabled || mergedOptions.traceEnabled;
878
+ const retryLog = shouldLog ? log : () => { };
879
+ const funcName = mergedOptions.functionLabel ?? func.name ?? 'anonymous';
880
+ if (mergedOptions.initialDelayMilliseconds > 0) {
881
+ retryLog(`initialDelayMilliseconds set to ${mergedOptions.initialDelayMilliseconds} - waiting before first try`);
882
+ await sleep(mergedOptions.initialDelayMilliseconds);
883
+ }
884
+ // eslint-disable-next-line no-constant-condition
885
+ while (true) {
886
+ attemptNumber++;
887
+ retryLog(`calling ${funcName} - attempt number ${attemptNumber}`);
888
+ try {
889
+ await func();
890
+ retryLog(`attempt ${attemptNumber} was successful`);
891
+ break;
892
+ }
893
+ catch (err) {
894
+ if (shouldLog) {
895
+ console.error(err);
896
+ }
897
+ lastError = err;
898
+ }
899
+ if (!forever && attemptNumber === maxCalls) {
900
+ throw new ExtendedError(`Failed to run method with retry after ${maxCalls} attempts`, getNormalizedError(lastError));
901
+ }
902
+ retryLog(`attempt number ${attemptNumber} failed - waiting ${delayMilliseconds} milliseconds before trying again`);
903
+ await sleep(delayMilliseconds);
904
+ }
905
+ }
906
+ /**
907
+ * Collapses each instance of consecutive whitespace characters into a single space.
908
+ */
909
+ export function collapseWhitespace(str) {
910
+ return str.replace(/\s+/g, ' ');
911
+ }
912
+ /**
913
+ * Check if a string is a valid directory name. This is a very simple check that just makes sure the string doesn't contain any invalid characters.
914
+ * @param dirName The directory name to check
915
+ * @returns `true` if the directory name is valid, `false` otherwise
916
+ */
917
+ export function isValidDirName(dirName) {
918
+ // List of generally invalid characters for directory names in Windows, macOS, and Linux
919
+ const invalidChars = ['<', '>', ':', '"', '/', '\\', '|', '?', '*'];
920
+ for (const char of dirName) {
921
+ if (invalidChars.includes(char) || char.charCodeAt(0) <= 31) {
922
+ return false;
923
+ }
924
+ }
925
+ return true;
926
+ }
927
+ export function hasWhitespace(str) {
928
+ return /\s/.test(str);
929
+ }
930
+ export function stripShellMetaCharacters(input) {
931
+ const metaCharacters = [
932
+ '\\', '`', '$', '"', "'", '<', '>', '|', ';', ' ',
933
+ '&', '(', ')', '[', ']', '{', '}', '?', '*', '#', '~', '^'
934
+ ];
935
+ const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
936
+ const regex = new RegExp(`[${metaCharacters.map(escapeRegex).join('')}]`, 'g');
937
+ return input.replace(regex, '');
938
+ }
939
+ export var AnsiColor;
940
+ (function (AnsiColor) {
941
+ AnsiColor["RESET"] = "\u001B[0m";
942
+ AnsiColor["RED"] = "\u001B[31m";
943
+ AnsiColor["GREEN"] = "\u001B[32m";
944
+ AnsiColor["YELLOW"] = "\u001B[33m";
945
+ AnsiColor["CYAN"] = "\u001B[96m";
946
+ AnsiColor["GRAY"] = "\u001B[90m";
947
+ AnsiColor["PURPLE"] = "\u001B[35m";
948
+ })(AnsiColor || (AnsiColor = {}));
949
+ export const color = (str, colorAnsiCode) => {
950
+ return `${colorAnsiCode}${str}${AnsiColor.RESET}`;
951
+ };
952
+ export const red = (str) => color(str, AnsiColor.RED);
953
+ export const green = (str) => color(str, AnsiColor.GREEN);
954
+ export const cyan = (str) => color(str, AnsiColor.CYAN);
955
+ export const gray = (str) => color(str, AnsiColor.GRAY);
956
+ export const purple = (str) => color(str, AnsiColor.PURPLE);
957
+ export const yellow = (str) => color(str, AnsiColor.YELLOW);
958
+ export var Emoji;
959
+ (function (Emoji) {
960
+ Emoji["RightArrow"] = "\u27A1\uFE0F";
961
+ Emoji["LeftArrow"] = "\u2B05\uFE0F";
962
+ Emoji["GreenCheck"] = "\u2705";
963
+ Emoji["Warning"] = "\u26A0\uFE0F";
964
+ Emoji["Lightning"] = "\u26A1";
965
+ Emoji["Exclamation"] = "\u2757";
966
+ Emoji["RedQuestion"] = "\u2753";
967
+ Emoji["RedX"] = "\u274C";
968
+ Emoji["Info"] = "\u2139\uFE0F";
969
+ Emoji["SadFace"] = "\uD83D\uDE22";
970
+ Emoji["Tools"] = "\uD83D\uDEE0\uFE0F";
971
+ Emoji["NoEntry"] = "\u26D4";
972
+ Emoji["Stop"] = "\uD83D\uDED1";
973
+ Emoji["Certificate"] = "\uD83D\uDCDC";
974
+ Emoji["Key"] = "\uD83D\uDD11";
975
+ })(Emoji || (Emoji = {}));
976
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VuZXJhbFV0aWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2dlbmVyYWxVdGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsTUFBTSxTQUFTLENBQUE7QUFDeEIsT0FBTyxHQUFHLE1BQU0sa0JBQWtCLENBQUE7QUFDbEMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFNBQVMsQ0FBQTtBQUNsQyxPQUFPLElBQUksRUFBRSxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQTtBQUN6QyxPQUFPLEtBQUssUUFBUSxNQUFNLFVBQVUsQ0FBQTtBQUNwQyxPQUFPLEtBQUssR0FBRyxNQUFNLEtBQUssQ0FBQTtBQUMxQixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0seUJBQXlCLENBQUE7QUFDaEQsT0FBTyxFQUF3QixPQUFPLEVBQUUseUJBQXlCLEVBQUUsa0JBQWtCLEVBQUUsd0JBQXdCLEVBQUUsdUJBQXVCLEVBQUUsa0JBQWtCLEVBQUUsa0NBQWtDLEVBQUUsYUFBYSxFQUFFLE1BQU0sMkJBQTJCLENBQUE7QUFNbFAsTUFBTSx3Q0FBd0MsR0FBRyxDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQTtBQUVoSDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLEdBQUcsQ0FBQyxJQUFhLEVBQUUsR0FBRyxRQUFtQjtJQUN2RCxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxHQUFHLFFBQVEsQ0FBQyxDQUFBO0FBQ2hDLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLEtBQUssQ0FBQyxTQUFrQixFQUFFLElBQWEsRUFBRSxHQUFHLFFBQW1CO0lBQzdFLElBQUksU0FBUyxFQUFFO1FBQ2IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsR0FBRyxRQUFRLENBQUMsQ0FBQTtLQUMvQjtBQUNILENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLEtBQUssQ0FBQyxJQUFjLEVBQUUsR0FBRyxRQUFtQjtJQUMxRCxJQUFJLE1BQU0sQ0FBQyxZQUFZLEVBQUU7UUFDdkIsTUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFBO1FBQ3hCLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxHQUFHLFFBQVEsQ0FBQyxDQUFBO0tBQ3ZDO0FBQ0gsQ0FBQztBQWlDRDs7OztHQUlHO0FBQ0gsTUFBTSxPQUFPLFVBQVcsU0FBUSxLQUFLO0lBQ25DLE1BQU0sQ0FBYTtJQUVuQixZQUFZLE9BQWUsRUFBRSxNQUFtQjtRQUM5QyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDZCxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQTtJQUN0QixDQUFDO0NBQ0Y7QUFXRDs7OztHQUlHO0FBQ0gsTUFBTSxPQUFPLGdCQUFpQixTQUFRLEtBQUs7SUFDekMsTUFBTSxDQUFtQjtJQUV6QixZQUFZLE9BQWUsRUFBRSxNQUF5QjtRQUNwRCxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDZCxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQTtJQUN0QixDQUFDO0NBQ0Y7QUFnQkQ7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsS0FBSyxDQUFDLEVBQVU7SUFDcEMsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1FBQzdCLFVBQVUsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUE7SUFDekIsQ0FBQyxDQUFDLENBQUE7QUFDSixDQUFDO0FBVUQ7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxVQUFVLENBQUMsT0FBZSxFQUFFLElBQWUsRUFBRSxPQUF3QztJQUN6RyxPQUFPLGtCQUFrQixDQUFDLE9BQU8sRUFBRSxJQUFJLElBQUksRUFBRSxFQUFFLE9BQU8sQ0FBQyxDQUFBO0FBQ3pELENBQUM7QUFFRDs7Ozs7Ozs7Ozs7R0FXRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUscUJBQXFCLENBQUMsT0FBZSxFQUFFLElBQWUsRUFBRSxHQUFZO0lBQ3hGLE9BQU8sa0JBQWtCLENBQUMsT0FBTyxFQUFFLElBQUksSUFBSSxFQUFFLEVBQUUsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLGFBQWEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFBO0FBQ25GLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGVBQWUsQ0FBQyxHQUFXO0lBQy9DLE9BQU8sTUFBTSxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUE7QUFDMUIsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsTUFBTSxDQUFDLEdBQVc7SUFDdEMsYUFBYSxDQUFDLEtBQUssRUFBRSxHQUFHLENBQUMsQ0FBQTtJQUN6QixNQUFNLEdBQUcsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUE7QUFDM0MsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsVUFBVSxDQUFDLEdBQVc7SUFDMUMsYUFBYSxDQUFDLEtBQUssRUFBRSxHQUFHLENBQUMsQ0FBQTtJQUN6QixFQUFFLENBQUMsU0FBUyxDQUFDLEdBQUcsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFBO0FBQ3hDLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGNBQWMsQ0FBQyxnQkFBd0IsRUFBRSwyQkFBc0M7SUFDbkcsYUFBYSxDQUFDLGtCQUFrQixFQUFFLGdCQUFnQixDQUFDLENBQUE7SUFFbkQsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsZ0JBQWdCLENBQUMsRUFBRTtRQUNwQyxLQUFLLENBQUMsd0RBQXdELGdCQUFnQixFQUFFLENBQUMsQ0FBQTtRQUNqRixNQUFNLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFBO1FBQzlCLE9BQU07S0FDUDtJQUVELElBQUksQ0FBQyxFQUFFLENBQUMsU0FBUyxDQUFDLGdCQUFnQixDQUFDLENBQUMsV0FBVyxFQUFFLEVBQUU7UUFDakQsTUFBTSxJQUFJLEtBQUssQ0FBQyx3Q0FBd0MsZ0JBQWdCLEVBQUUsQ0FBQyxDQUFBO0tBQzVFO0lBRUQsMkVBQTJFO0lBQzNFLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsQ0FBQTtJQUNuRCxHQUFHLENBQUMsdUJBQXVCLFlBQVksRUFBRSxDQUFDLENBQUE7SUFDMUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxDQUFDLEVBQUU7UUFDM0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxzRUFBc0UsZ0JBQWdCLEVBQUUsQ0FBQyxDQUFBO0tBQzFHO0lBRUQsSUFBSSxZQUFZLEtBQUssT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUFFO1FBQ2xDLE1BQU0sSUFBSSxLQUFLLENBQUMsNkRBQTZELGdCQUFnQixFQUFFLENBQUMsQ0FBQTtLQUNqRztJQUVELE1BQU0sR0FBRyxHQUFHLE1BQU0sR0FBRyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRSxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFBO0lBRXRFLElBQUksMkJBQTJCLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLDJCQUEyQixDQUFDLEVBQUU7UUFDOUUsTUFBTSxJQUFJLEtBQUssQ0FBQyw4Q0FBOEMsQ0FBQyxDQUFBO0tBQ2hFO0lBRUQsSUFBSSxRQUFRLEdBQUcsTUFBTSxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUE7SUFFL0IsT0FBTyxRQUFRLEVBQUU7UUFDZixJQUFJLDJCQUEyQixFQUFFLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUU7WUFDeEQsUUFBUSxHQUFHLE1BQU0sR0FBRyxDQUFDLElBQUksRUFBRSxDQUFBO1lBQzNCLFNBQVE7U0FDVDtRQUVELE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFBO1FBRTdELElBQUksUUFBUSxDQUFDLFdBQVcsRUFBRSxFQUFFO1lBQzFCLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQyxVQUFVLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtTQUM5QzthQUFNO1lBQ0wsTUFBTSxHQUFHLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFBO1NBQzdCO1FBRUQsUUFBUSxHQUFHLE1BQU0sR0FBRyxDQUFDLElBQUksRUFBRSxDQUFBO0tBQzVCO0lBRUQsTUFBTSxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUE7QUFDbkIsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUscUJBQXFCLENBQUMsZUFBdUIsRUFBRSxvQkFBNEI7SUFDL0YsYUFBYSxDQUFDLGlCQUFpQixFQUFFLGVBQWUsQ0FBQyxDQUFBO0lBQ2pELGFBQWEsQ0FBQyxzQkFBc0IsRUFBRSxvQkFBb0IsQ0FBQyxDQUFBO0lBRTNELElBQUksQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLGVBQWUsQ0FBQyxFQUFFO1FBQ25DLE1BQU0sSUFBSSxLQUFLLENBQUMsNkNBQTZDLGVBQWUsRUFBRSxDQUFDLENBQUE7S0FDaEY7SUFFRCxJQUFJLENBQUMsRUFBRSxDQUFDLFNBQVMsQ0FBQyxlQUFlLENBQUMsQ0FBQyxXQUFXLEVBQUUsRUFBRTtRQUNoRCxNQUFNLElBQUksS0FBSyxDQUFDLHVDQUF1QyxlQUFlLEVBQUUsQ0FBQyxDQUFBO0tBQzFFO0lBRUQsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsb0JBQW9CLENBQUMsRUFBRTtRQUN4QyxNQUFNLE1BQU0sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFBO0tBQ25DO0lBRUQsSUFBSSxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsb0JBQW9CLENBQUMsQ0FBQyxXQUFXLEVBQUUsRUFBRTtRQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLDRDQUE0QyxvQkFBb0IsRUFBRSxDQUFDLENBQUE7S0FDcEY7SUFFRCxNQUFNLEdBQUcsR0FBRyxNQUFNLEdBQUcsQ0FBQyxPQUFPLENBQUMsZUFBZSxFQUFFLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUE7SUFFckUsSUFBSSxRQUFRLEdBQUcsTUFBTSxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUE7SUFFL0IsT0FBTyxRQUFRLEVBQUU7UUFDZixNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRSxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUE7UUFDNUQsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUE7UUFFL0QsSUFBSSxRQUFRLENBQUMsV0FBVyxFQUFFLEVBQUU7WUFDMUIsTUFBTSxxQkFBcUIsQ0FBQyxVQUFVLEVBQUUsUUFBUSxDQUFDLENBQUE7U0FDbEQ7YUFBTTtZQUNMLE1BQU0sR0FBRyxDQUFDLFFBQVEsQ0FBQyxVQUFVLEVBQUUsUUFBUSxDQUFDLENBQUE7U0FDekM7UUFFRCxRQUFRLEdBQUcsTUFBTSxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUE7S0FDNUI7QUFDSCxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxhQUFhLENBQUMsU0FBaUIsRUFBRSxVQUFrQjtJQUNqRSxJQUFJLFVBQVUsS0FBSyxTQUFTLElBQUksVUFBVSxLQUFLLElBQUksSUFBSSxVQUFVLEtBQUssRUFBRSxJQUFJLE9BQU8sVUFBVSxLQUFLLFFBQVEsSUFBSSxVQUFVLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxFQUFFO1FBQ3RJLE1BQU0sSUFBSSxLQUFLLENBQUMsbUJBQW1CLFNBQVMsY0FBYyxDQUFDLENBQUE7S0FDNUQ7QUFDSCxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxTQUFpQixFQUFFLFVBQWtCO0lBQ3BFLGFBQWEsQ0FBQyxTQUFTLEVBQUUsVUFBVSxDQUFDLENBQUE7SUFFcEMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLEVBQUU7UUFDOUIsTUFBTSxJQUFJLEtBQUssQ0FBQyxtREFBbUQsU0FBUyxNQUFNLFVBQVUsRUFBRSxDQUFDLENBQUE7S0FDaEc7QUFDSCxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLCtCQUErQixDQUFDLFdBQW1CO0lBQ2pFLGFBQWEsQ0FBQyxhQUFhLEVBQUUsV0FBVyxDQUFDLENBQUE7SUFFekMsbURBQW1EO0lBQ25ELElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFO1FBQ3JDLE9BQU8sS0FBSyxDQUFBO0tBQ2I7SUFFRCwwRkFBMEY7SUFDMUYsT0FBTyxlQUFlLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFBO0FBQzFDLENBQUM7QUFrREQ7Ozs7Ozs7Ozs7R0FVRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsa0JBQWtCLENBQUMsaUJBQXlCLEVBQUUsb0JBQTBDLEVBQUUsT0FBdUM7SUFDckosZ0JBQWdCLENBQUMsbUJBQW1CLEVBQUUsaUJBQWlCLENBQUMsQ0FBQTtJQUN4RCxhQUFhLENBQUMsc0JBQXNCLEVBQUUsb0JBQW9CLENBQUMsQ0FBQTtJQUMzRCxJQUFJLE9BQU8sRUFBRSxHQUFHLEVBQUU7UUFDaEIsZ0JBQWdCLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQTtLQUNyQztJQUNELElBQUksT0FBTyxFQUFFLFdBQVcsSUFBSSxDQUFDLCtCQUErQixDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsRUFBRTtRQUNqRixNQUFNLElBQUksS0FBSyxDQUFDLCtOQUErTixDQUFDLENBQUE7S0FDalA7SUFDRCxJQUFJLE9BQU8sRUFBRSxPQUFPLElBQUksQ0FBQyw0QkFBNEIsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFO1FBQzNFLE1BQU0sSUFBSSxLQUFLLENBQUMsdUVBQXVFLENBQUMsQ0FBQTtLQUN6RjtJQUNELElBQUksQ0FBQyxNQUFNLGVBQWUsRUFBRSxFQUFFO1FBQzVCLE1BQU0sSUFBSSxLQUFLLENBQUMsdUJBQXVCLENBQUMsQ0FBQTtLQUN6QztJQUVELE1BQU0sY0FBYyxHQUF5QixFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQUUsQ0FBQTtJQUNsSCxNQUFNLGFBQWEsR0FBRyxFQUFFLEdBQUcsY0FBYyxFQUFFLEdBQUcsT0FBTyxFQUFFLENBQUE7SUFFdkQsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLGlCQUFpQixDQUFDLENBQUE7SUFDeEQsTUFBTSxxQkFBcUIsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLGlCQUFpQixDQUFDLENBQUE7SUFFOUQsSUFBSSxDQUFDLGFBQWEsQ0FBQyxHQUFHLEVBQUU7UUFDdEIsYUFBYSxDQUFDLEdBQUcsR0FBRyxnQkFBZ0IsQ0FBQTtLQUNyQztJQUVELElBQUksU0FBUyxHQUFHLENBQUMsU0FBUyxFQUFFLElBQUksRUFBRSxhQUFhLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxDQUFDLHFCQUFxQixDQUFDLENBQUE7SUFFOUcsSUFBSSxhQUFhLENBQUMsV0FBVyxFQUFFO1FBQzdCLFNBQVMsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsYUFBYSxDQUFDLFdBQVcsQ0FBQyxDQUFBO0tBQzVEO0lBRUQsSUFBSSxhQUFhLENBQUMsT0FBTyxFQUFFO1FBQ3pCLFNBQVMsQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLGFBQWEsQ0FBQyxPQUFPLENBQUMsQ0FBQTtLQUNuRDtJQUVELFNBQVMsQ0FBQyxJQUFJLENBQUMsb0JBQW9CLENBQUMsQ0FBQTtJQUVwQyxJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsSUFBSSx3Q0FBd0MsQ0FBQyxRQUFRLENBQUMsb0JBQW9CLENBQUMsRUFBRTtRQUN0RyxTQUFTLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFBO0tBQzNCO0lBRUQsSUFBSSxhQUFhLENBQUMsSUFBSSxFQUFFO1FBQ3RCLFNBQVMsR0FBRyxTQUFTLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQTtLQUNqRDtJQUVELEtBQUssQ0FBQyxzQkFBc0IsYUFBYSxDQUFDLEdBQUcsWUFBWSxTQUFTLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQTtJQUUvRSxNQUFNLFdBQVcsR0FBRyx3Q0FBd0MsQ0FBQyxRQUFRLENBQUMsb0JBQW9CLENBQUMsSUFBSSxPQUFPLEVBQUUsUUFBUSxLQUFLLElBQUksQ0FBQTtJQUV6SCxLQUFLLENBQUMsMEVBQTBFLFdBQVcsRUFBRSxDQUFDLENBQUE7SUFFOUYsTUFBTSxZQUFZLEdBQWtDO1FBQ2xELEdBQUcsRUFBRSxhQUFhLENBQUMsR0FBRztRQUN0QixLQUFLLEVBQUUsaUJBQWlCLEVBQUU7UUFDMUIsYUFBYSxFQUFFLFdBQVc7S0FDM0IsQ0FBQTtJQUVELE1BQU0sV0FBVyxHQUFHLE1BQU0sa0JBQWtCLENBQUMsUUFBUSxFQUFFLFNBQVMsRUFBRSxZQUFZLENBQUMsQ0FBQTtJQUUvRSxJQUFJLFdBQVcsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUFFO1FBQzFCLE1BQU0sSUFBSSxLQUFLLENBQUMsMkNBQTJDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFBO0tBQy9FO0FBQ0gsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUscUJBQXFCLENBQUMsR0FBVztJQUMvQyxJQUFJLENBQUMsR0FBRyxFQUFFO1FBQUUsT0FBTyxFQUFFLENBQUE7S0FBRTtJQUN2QixPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQTtBQUN6RixDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxhQUFhLENBQUMsR0FBVztJQUN2QyxJQUFJLENBQUMsR0FBRyxFQUFFO1FBQUUsT0FBTyxFQUFFLENBQUE7S0FBRTtJQUN2QixPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQTtBQUM1RCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUFDLE9BQWUsRUFBRSxJQUFlLEVBQUUsaUJBQTBCLElBQUk7SUFDNUYsSUFBSSxDQUFDLGlCQUFpQixFQUFFLEVBQUU7UUFDeEIsTUFBTSxJQUFJLEtBQUssQ0FBQywyQ0FBMkMsQ0FBQyxDQUFBO0tBQzdEO0lBQ0QsOEpBQThKO0lBQzlKLE9BQU8sdUJBQXVCLENBQUMsT0FBTyxFQUFFLElBQUksRUFBRSxjQUFjLEVBQUUsSUFBSSxDQUFDLENBQUE7QUFDckUsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxjQUFjLENBQUMsT0FBZSxFQUFFLElBQWUsRUFBRSxpQkFBMEIsSUFBSTtJQUNuRyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsRUFBRTtRQUN4QixNQUFNLElBQUksS0FBSyxDQUFDLDJDQUEyQyxDQUFDLENBQUE7S0FDN0Q7SUFDRCw4SkFBOEo7SUFDOUosT0FBTyxNQUFNLHdCQUF3QixDQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUUsY0FBYyxFQUFFLElBQUksQ0FBQyxDQUFBO0FBQzVFLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNILE1BQU0sVUFBVSxlQUFlLENBQUMsT0FBZSxFQUFFLElBQWUsRUFBRSxpQkFBMEIsSUFBSTtJQUM5RixPQUFPLHVCQUF1QixDQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUUsY0FBYyxDQUFDLENBQUE7QUFDL0QsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxnQkFBZ0IsQ0FBQyxPQUFlLEVBQUUsSUFBZSxFQUFFLGlCQUEwQixJQUFJO0lBQ3JHLE9BQU8sTUFBTSx3QkFBd0IsQ0FBQyxPQUFPLEVBQUUsSUFBSSxFQUFFLGNBQWMsQ0FBQyxDQUFBO0FBQ3RFLENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sVUFBVSxpQkFBaUI7SUFDL0IsT0FBTyxRQUFRLEVBQUUsS0FBSyxPQUFPLENBQUE7QUFDL0IsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sVUFBVSxhQUFhO0lBQzNCLE9BQU8sUUFBUSxFQUFFLEtBQUssUUFBUSxDQUFBO0FBQ2hDLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsZUFBZTtJQUM3QixPQUFPLENBQUMsaUJBQWlCLEVBQUUsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFBO0FBQ2pELENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsS0FBSyxDQUFDLFdBQW1CO0lBQzdDLE9BQU8sYUFBYSxDQUFDLFdBQVcsRUFBRSxjQUFjLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQTtBQUNyRSxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsU0FBUyxDQUFDLFdBQW1CO0lBQzNDLE9BQU8sYUFBYSxDQUFDLFdBQVcsRUFBRSxhQUFhLEVBQUUsZUFBZSxDQUFnQixDQUFBO0FBQ2xGLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxlQUFlO0lBQ25DLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLENBQUMsUUFBUSxFQUFFO1FBQ2pDLEtBQUssQ0FBQyxnSEFBZ0gsQ0FBQyxDQUFBO1FBQ3ZILE9BQU8sS0FBSyxDQUFBO0tBQ2I7SUFFRCxJQUFJO1FBQ0YsTUFBTSxNQUFNLEdBQUcsTUFBTSxnQkFBZ0IsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFBO1FBQ3pELE9BQU8sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxzQkFBc0IsQ0FBQyxDQUFBO0tBQ3ZEO0lBQUMsT0FBTyxHQUFHLEVBQUU7UUFDWixPQUFPLEtBQUssQ0FBQTtLQUNiO0FBQ0gsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsV0FBVyxDQUFDLEtBQWE7SUFDdkMsTUFBTSxFQUFFLEdBQUcsUUFBUSxDQUFDLGVBQWUsQ0FBQztRQUNsQyxLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7UUFDcEIsTUFBTSxFQUFFLE9BQU8sQ0FBQyxNQUFNO0tBQ3ZCLENBQUMsQ0FBQTtJQUVGLE9BQU8sSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FDM0IsRUFBRSxDQUFDLFFBQVEsQ0FBQyxLQUFLLEtBQUssSUFBSSxFQUFFLEdBQUcsQ0FBQyxFQUFFO1FBQ2hDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsQ0FBQTtRQUNWLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQTtJQUNkLENBQUMsQ0FBQyxDQUNILENBQUE7QUFDSCxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxlQUFlLENBQUMsUUFBZ0I7SUFDOUMsTUFBTSxFQUFFLEdBQUcsUUFBUSxDQUFDLGVBQWUsQ0FBQztRQUNsQyxLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7UUFDcEIsTUFBTSxFQUFFLE9BQU8sQ0FBQyxNQUFNO0tBQ3ZCLENBQUMsQ0FBQTtJQUVGLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTtRQUM3QixFQUFFLENBQUMsUUFBUSxDQUFDLE9BQU8sS0FBSyxDQUFDLFdBQVcsSUFBSSxRQUFRLE9BQU8sS0FBSyxDQUFDLFVBQVUsc0JBQXNCLEVBQUUsQ0FBQyxNQUFNLEVBQUUsRUFBRTtZQUN4RyxFQUFFLENBQUMsS0FBSyxFQUFFLENBQUE7WUFDVixNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUMsV0FBVyxFQUFFLEtBQUssR0FBRyxJQUFJLE1BQU0sQ0FBQyxXQUFXLEVBQUUsS0FBSyxLQUFLLENBQUE7WUFDaEYsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsS0FBSyxLQUFLLENBQUMsVUFBVSxlQUFlLENBQUMsQ0FBQyxDQUFDLEtBQUssS0FBSyxDQUFDLElBQUksYUFBYSxDQUFDLENBQUE7WUFDcEYsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFBO1FBQ3BCLENBQUMsQ0FBQyxDQUFBO0lBQ0osQ0FBQyxDQUFDLENBQUE7QUFDSixDQUFDO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLHNCQUFzQjtJQUMxQyxJQUFJLE1BQU0sZUFBZSxDQUFDLGNBQWMsQ0FBQyxFQUFFO1FBQ3pDLEdBQUcsQ0FBQyxhQUFhLENBQUMsQ0FBQTtLQUNuQjtTQUFNO1FBQ0wsR0FBRyxDQUFDLGlCQUFpQixDQUFDLENBQUE7S0FDdkI7QUFDSCxDQUFDO0FBRUQ7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxnQkFBZ0IsQ0FBQyxVQUFrQixFQUFFLGVBQXVCO0lBQ2hGLE1BQU0sT0FBTyxDQUFDLFVBQVUsRUFBRSxlQUFlLEVBQUUsS0FBSyxDQUFDLENBQUE7QUFDbkQsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxnQkFBZ0IsQ0FBQyxVQUFrQixFQUFFLGVBQXVCLEVBQUUsdUJBQXVCLEdBQUcsS0FBSztJQUNqSCxNQUFNLE9BQU8sQ0FBQyxVQUFVLEVBQUUsZUFBZSxFQUFFLElBQUksRUFBRSx1QkFBdUIsQ0FBQyxDQUFBO0FBQzNFLENBQUM7QUFFRDs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxlQUFlLENBQUMsVUFBa0IsRUFBRSxlQUF1QixFQUFFLFFBQWtCLEVBQUUsYUFBcUM7SUFDMUksZ0JBQWdCLENBQUMsWUFBWSxFQUFFLFVBQVUsQ0FBQyxDQUFBO0lBQzFDLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLENBQUE7SUFDakQsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsV0FBVyxDQUFDLEVBQUU7UUFDL0IsTUFBTSxlQUFlLENBQUMsV0FBVyxDQUFDLENBQUE7S0FDbkM7SUFFRCxNQUFNLFVBQVUsR0FBRyxrQkFBa0IsQ0FBQyxVQUFVLENBQUMsQ0FBQTtJQUNqRCxNQUFNLE9BQU8sR0FBMEIsZ0JBQWdCLENBQUMsVUFBVSxFQUFFLEdBQUcsQ0FBQyxFQUFFLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFBO0lBRWxHLElBQUksYUFBYSxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRTtRQUMxRCxLQUFLLE1BQU0sQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsRUFBRTtZQUN4RCxPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsS0FBSyxDQUFBO1NBQ3JCO0tBQ0Y7SUFFRCxNQUFNLGFBQWEsR0FBRyxzQkFBc0IsQ0FBQyxPQUFPLENBQUMsQ0FBQTtJQUNyRCxNQUFNLGlCQUFpQixHQUFHLHlCQUF5QixDQUFDLGFBQWEsQ0FBQyxDQUFBO0lBQ2xFLE1BQU0sR0FBRyxDQUFDLFNBQVMsQ0FBQyxlQUFlLEVBQUUsaUJBQWlCLENBQUMsQ0FBQTtBQUN6RCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsZ0JBQWdCLENBQUMsSUFBMkIsRUFBRSxTQUFtQztJQUMvRixpQkFBaUI7SUFDakIsdUVBQXVFO0lBQ3ZFLGtHQUFrRztJQUNsRyw0RUFBNEU7SUFDNUUsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQztTQUNyQixNQUFNLENBQUMsU0FBUyxDQUFDO1NBQ2pCLE1BQU0sQ0FBQyxDQUFDLFdBQVcsRUFBRSxHQUFHLEVBQUUsRUFBRTtRQUMzQixXQUFXLENBQUMsR0FBRyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFBO1FBQzVCLE9BQU8sV0FBVyxDQUFBO0lBQ3BCLENBQUMsRUFBRSxFQUEyQixDQUFDLENBQUE7QUFDbkMsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsc0JBQXNCLENBQUMsSUFBMkI7SUFDaEUsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUU7UUFDdkQsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFO1lBQ1QsT0FBTyxDQUFDLENBQUMsQ0FBQTtTQUNWO1FBQ0QsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFO1lBQ1QsT0FBTyxDQUFDLENBQUE7U0FDVDtRQUNELE9BQU8sQ0FBQyxDQUFBO0lBQ1YsQ0FBQyxDQUFDLENBQUE7SUFFRixPQUFPLE1BQU0sQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLENBQUE7QUFDMUMsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsaUJBQWlCLENBQUMsT0FBZTtJQUNyRCxtRkFBbUY7SUFDbkYsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEtBQUssRUFBRTtRQUN0QyxNQUFNLElBQUksS0FBSyxDQUFDLGlDQUFpQyxPQUFPLEVBQUUsQ0FBQyxDQUFBO0tBQzVEO0lBQ0QsMkRBQTJEO0lBQzNELElBQUksRUFBRSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsRUFBRTtRQUMxQixNQUFNLEdBQUcsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUE7S0FDMUI7QUFDSCxDQUFDO0FBU0Q7Ozs7Ozs7R0FPRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsb0JBQW9CLENBQUMsR0FBVyxFQUFFLGVBQXVCLEVBQUUsT0FBbUM7SUFDbEgsa0NBQWtDLENBQUMsR0FBRyxFQUFFLGVBQWUsQ0FBQyxDQUFBO0lBRXhELE1BQU0sY0FBYyxHQUFxQixFQUFFLFFBQVEsRUFBRSxDQUFDLEVBQUUscUJBQXFCLEVBQUUsRUFBRSxFQUFFLCtCQUErQixFQUFFLEtBQUssRUFBRSxDQUFBO0lBQzNILE1BQU0sYUFBYSxHQUFHLEVBQUUsR0FBRyxjQUFjLEVBQUUsR0FBRyxPQUFPLEVBQUUsQ0FBQTtJQUV2RCxpQ0FBaUM7SUFDakMsTUFBTSxLQUFLLEdBQUcsSUFBSSxNQUFNLENBQUMsR0FBRyxHQUFHLGVBQWUsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxDQUFDLG9CQUFvQixDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFBO0lBRXZHLE1BQU0sT0FBTyxHQUFhLEVBQUUsQ0FBQTtJQUU1QixrREFBa0Q7SUFDbEQsS0FBSyxVQUFVLGVBQWUsQ0FBQyxTQUFpQixFQUFFLEtBQWE7UUFDN0QsSUFBSSxLQUFLLEdBQUcsYUFBYSxDQUFDLFFBQVE7WUFBRSxPQUFNO1FBRTFDLE1BQU0sT0FBTyxHQUFHLE1BQU0sR0FBRyxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsRUFBRSxhQUFhLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQTtRQUVyRSxLQUFLLE1BQU0sS0FBSyxJQUFJLE9BQU8sRUFBRTtZQUMzQixNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsU0FBUyxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQTtZQUUvQyxJQUFJLEtBQUssQ0FBQyxXQUFXLEVBQUUsRUFBRTtnQkFDdkIsNENBQTRDO2dCQUM1QyxJQUFJLENBQUMsYUFBYSxDQUFDLHFCQUFxQixFQUFFLFFBQVEsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUU7b0JBQzlELE1BQU0sZUFBZSxDQUFDLFFBQVEsRUFBRSxLQUFLLEdBQUcsQ0FBQyxDQUFDLENBQUE7aUJBQzNDO2FBQ0Y7aUJBQU0sSUFBSSxLQUFLLENBQUMsTUFBTSxFQUFFLElBQUksS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQ25ELElBQUksYUFBYSxDQUFDLCtCQUErQixFQUFFO29CQUNqRCxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxFQUFFLFFBQVEsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQTtpQkFDL0Q7cUJBQU07b0JBQ0wsT0FBTyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQTtpQkFDdkI7YUFDRjtTQUNGO0lBQ0gsQ0FBQztJQUVELE1BQU0sZUFBZSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQSxDQUFFLG9DQUFvQztJQUVuRSxPQUFPLE9BQU8sQ0FBQTtBQUNoQixDQUFDO0FBRUQsK0RBQStEO0FBQy9ELE1BQU0sVUFBVSxvQkFBb0IsQ0FBQyxHQUFXO0lBQzlDLE9BQU8sR0FBRyxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxNQUFNLENBQUMsQ0FBQTtBQUNuRCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLFVBQVUsUUFBUSxDQUFDLElBQWdCO0lBQ3ZDLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sS0FBSyxDQUFDO1FBQUUsT0FBTTtJQUVyRCxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFBO0lBQ2pDLE1BQU0sWUFBWSxHQUFhLEVBQUUsQ0FBQTtJQUNqQyxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsVUFBVSxFQUFFLENBQUMsRUFBRSxFQUFFO1FBQ25DLFlBQVksQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQTtLQUNwRTtJQUVELE1BQU0sYUFBYSxHQUFHLFlBQVksQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFBO0lBRTlFLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFO1FBQ3BDLE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsTUFBTSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFBO1FBQzVGLEdBQUcsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUE7UUFDL0IsSUFBSSxDQUFDLEtBQUssQ0FBQztZQUFFLEdBQUcsQ0FBQyxhQUFhLENBQUMsQ0FBQTtLQUNoQztBQUNILENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLG9CQUFvQixHQUFHLHdGQUF3RixDQUFBO0FBRTVIOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sVUFBVSxxQkFBcUIsQ0FBQyxPQUFlO0lBQ25ELE9BQU8sQ0FBQyxZQUFZLEVBQUUsa0JBQWtCLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxHQUFHLG9CQUFvQixHQUFHLE9BQU8sRUFBRSxDQUFDLENBQUE7QUFDdEcsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsWUFBWSxDQUFDLFlBQW9CO0lBQy9DLElBQUksS0FBYSxDQUFBO0lBQ2pCLElBQUksSUFBWSxDQUFBO0lBRWhCLElBQUksWUFBWSxHQUFHLElBQUksRUFBRTtRQUN2QixPQUFPLEdBQUcsWUFBWSxLQUFLLENBQUE7S0FDNUI7SUFFRCxJQUFJLFlBQVksR0FBRyxLQUFLLEVBQUU7UUFDeEIsS0FBSyxHQUFHLFlBQVksR0FBRyxJQUFJLENBQUE7UUFDM0IsSUFBSSxHQUFHLFFBQVEsQ0FBQTtLQUNoQjtTQUFNLElBQUksWUFBWSxHQUFHLE9BQU8sRUFBRTtRQUNqQyxLQUFLLEdBQUcsWUFBWSxHQUFHLEtBQUssQ0FBQTtRQUM1QixJQUFJLEdBQUcsUUFBUSxDQUFBO0tBQ2hCO1NBQU07UUFDTCxLQUFLLEdBQUcsWUFBWSxHQUFHLE9BQU8sQ0FBQTtRQUM5QixJQUFJLEdBQUcsTUFBTSxDQUFBO0tBQ2Q7SUFFRCxJQUFJLFdBQVcsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFBO0lBRWxDLElBQUksV0FBVyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRTtRQUMvQixXQUFXLEdBQUcsV0FBVyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQTtLQUN2QztTQUFNLElBQUksV0FBVyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsRUFBRTtRQUNwQyxXQUFXLEdBQUcsV0FBVyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQTtLQUN2QztJQUVELElBQUksV0FBVyxLQUFLLEdBQUcsRUFBRTtRQUN2QixJQUFJLElBQUksR0FBRyxDQUFBO0tBQ1o7SUFFRCxPQUFPLEdBQUcsV0FBVyxJQUFJLElBQUksRUFBRSxDQUFBO0FBQ2pDLENBQUM7QUFFRCxNQUFNLE9BQU8sYUFBYyxTQUFRLEtBQUs7SUFDL0IsVUFBVSxDQUFjO0lBRS9CLFlBQVksT0FBZSxFQUFFLFVBQWtCO1FBQzdDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQTtRQUNkLElBQUksQ0FBQyxVQUFVLEdBQUcsVUFBVSxJQUFJLElBQUksQ0FBQTtRQUNwQyxNQUFNLENBQUMsY0FBYyxDQUFDLElBQUksRUFBRSxhQUFhLENBQUMsU0FBUyxDQUFDLENBQUE7SUFDdEQsQ0FBQztDQUNGO0FBRUQsTUFBTSxVQUFVLFdBQVcsQ0FBQyxHQUFXO0lBQ3JDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUE7SUFDekIsS0FBSyxDQUFDLDBDQUEwQyxHQUFHLEVBQUUsQ0FBQyxDQUFBO0lBQ3RELElBQUk7UUFDRixNQUFNLFVBQVUsR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUE7UUFDakMsTUFBTSxTQUFTLEdBQUcsSUFBSSxHQUFHLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxVQUFVLEdBQUcsVUFBVSxDQUFDLENBQUE7UUFDL0YsS0FBSyxDQUFDLGVBQWUsU0FBUyxFQUFFLENBQUMsQ0FBQTtRQUNqQyxPQUFPLFNBQVMsQ0FBQyxRQUFRLENBQUE7S0FDMUI7SUFBQyxPQUFPLENBQUMsRUFBRTtRQUNWLE1BQU0sSUFBSSxhQUFhLENBQUMsYUFBYSxFQUFFLENBQVUsQ0FBQyxDQUFBO0tBQ25EO0FBQ0gsQ0FBQztBQUVELE1BQU0sQ0FBQyxLQUFLLFVBQVUsV0FBVyxDQUFDLElBQVk7SUFDNUMsSUFBSTtRQUNGLE1BQU0sS0FBSyxHQUFHLE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQTtRQUNsQyxPQUFPLEtBQUssQ0FBQyxXQUFXLEVBQUUsQ0FBQTtLQUMzQjtJQUFDLE9BQU8sR0FBRyxFQUFFO1FBQ1osS0FBSyxDQUFDLDhDQUE4QyxFQUFFLEdBQUcsQ0FBQyxDQUFBO1FBQzFELE9BQU8sS0FBSyxDQUFBO0tBQ2I7QUFDSCxDQUFDO0FBRUQsTUFBTSxVQUFVLGVBQWUsQ0FBQyxJQUFZO0lBQzFDLElBQUk7UUFDRixNQUFNLEtBQUssR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFBO1FBQy9CLE9BQU8sS0FBSyxDQUFDLFdBQVcsRUFBRSxDQUFBO0tBQzNCO0lBQUMsT0FBTyxHQUFHLEVBQUU7UUFDWixLQUFLLENBQUMsOENBQThDLEVBQUUsR0FBRyxDQUFDLENBQUE7UUFDMUQsT0FBTyxLQUFLLENBQUE7S0FDYjtBQUNILENBQUM7QUFJRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsZUFBZTtJQUM3QixJQUFJLGlCQUFpQixFQUFFLEVBQUU7UUFDdkIsT0FBTyxLQUFLLENBQUE7S0FDYjtJQUNELElBQUksYUFBYSxFQUFFLEVBQUU7UUFDbkIsT0FBTyxLQUFLLENBQUE7S0FDYjtJQUNELElBQUksZUFBZSxFQUFFLEVBQUU7UUFDckIsT0FBTyxPQUFPLENBQUE7S0FDZjtJQUNELE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLEdBQUcsUUFBUSxFQUFFLENBQUMsQ0FBQTtBQUN6RCxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsZUFBZSxDQUFDLElBQVk7SUFDaEQsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1FBQzdCLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLFdBQVcsQ0FBQyxDQUFBO1FBRTdDLE1BQU0sQ0FBQyxFQUFFLENBQUMsU0FBUyxFQUFFLEdBQUcsRUFBRTtZQUN4QixNQUFNLENBQUMsT0FBTyxFQUFFLENBQUE7WUFDaEIsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFBLENBQUMsaUJBQWlCO1FBQ2xDLENBQUMsQ0FBQyxDQUFBO1FBRUYsTUFBTSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxHQUEwQixFQUFFLEVBQUU7WUFDaEQsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFBO1lBQ2hCLElBQUksR0FBRyxDQUFDLElBQUksS0FBSyxjQUFjLEVBQUU7Z0JBQy9CLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQSxDQUFDLG9CQUFvQjthQUNuQztpQkFBTTtnQkFDTCxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUEsQ0FBQyxtREFBbUQ7YUFDbkU7UUFDSCxDQUFDLENBQUMsQ0FBQTtJQUNKLENBQUMsQ0FBQyxDQUFBO0FBQ0osQ0FBQztBQUVEOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUFDLE9BQWUsRUFBRSxZQUFZLEdBQUcsSUFBSTtJQUNwRSxhQUFhLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxDQUFBO0lBQ2pDLE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUE7SUFDaEMsSUFBSSxHQUFHLEtBQUssU0FBUyxJQUFJLEdBQUcsS0FBSyxJQUFJLEVBQUU7UUFDckMsTUFBTSxJQUFJLEtBQUssQ0FBQywwQ0FBMEMsT0FBTyxFQUFFLENBQUMsQ0FBQTtLQUNyRTtJQUNELElBQUksWUFBWSxJQUFJLEdBQUcsQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLEVBQUU7UUFDckMsTUFBTSxJQUFJLEtBQUssQ0FBQywyQ0FBMkMsT0FBTyxFQUFFLENBQUMsQ0FBQTtLQUN0RTtJQUNELE9BQU8sR0FBRyxDQUFBO0FBQ1osQ0FBQztBQW1CRCxNQUFNLFVBQVUsa0JBQWtCLENBQUMsR0FBWTtJQUM3QyxJQUFJLGdCQUF1QixDQUFBO0lBQzNCLElBQUksR0FBRyxLQUFLLFNBQVMsSUFBSSxHQUFHLEtBQUssSUFBSSxFQUFFO1FBQ3JDLGdCQUFnQixHQUFHLElBQUksS0FBSyxDQUFDLGlDQUFpQyxDQUFDLENBQUE7S0FDaEU7U0FBTSxJQUFJLEdBQUcsWUFBWSxLQUFLLEVBQUU7UUFDL0IsZ0JBQWdCLEdBQUcsR0FBRyxDQUFBO0tBQ3ZCO1NBQU0sSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRLEVBQUU7UUFDbEMsZ0JBQWdCLEdBQUcsSUFBSSxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUE7S0FDbEM7U0FBTSxJQUFJLEdBQUcsWUFBWSxNQUFNLEVBQUU7UUFDaEMsSUFBSTtZQUNGLGdCQUFnQixHQUFHLElBQUksS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQTtTQUNsRDtRQUFDLE9BQU8sU0FBUyxFQUFFO1lBQ2xCLGdCQUFnQixHQUFHLElBQUksS0FBSyxDQUFDLHNEQUFzRCxDQUFDLENBQUE7U0FDckY7S0FDRjtTQUFNO1FBQ0wsZ0JBQWdCLEdBQUcsSUFBSSxLQUFLLENBQUMseUJBQXlCLE9BQU8sR0FBRyx3QkFBd0IsQ0FBQyxDQUFBO0tBQzFGO0lBQ0QsT0FBTyxnQkFBZ0IsQ0FBQTtBQUN6QixDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxjQUFjLENBQUMsSUFBeUIsRUFBRSxRQUFnQixFQUFFLGlCQUF5QixFQUFFLE9BQW1DO0lBQzlJLElBQUksYUFBYSxHQUFHLENBQUMsQ0FBQTtJQUNyQixJQUFJLFNBQWtCLENBQUE7SUFDdEIsTUFBTSxPQUFPLEdBQUcsUUFBUSxLQUFLLENBQUMsQ0FBQyxDQUFBO0lBRS9CLE1BQU0sY0FBYyxHQUFxQixFQUFFLHdCQUF3QixFQUFFLENBQUMsRUFBRSxZQUFZLEVBQUUsS0FBSyxFQUFFLENBQUE7SUFDN0YsTUFBTSxhQUFhLEdBQXFCLEVBQUUsR0FBRyxjQUFjLEVBQUUsR0FBRyxPQUFPLEVBQUUsQ0FBQTtJQUV6RSxNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUMsWUFBWSxJQUFJLGFBQWEsQ0FBQyxZQUFZLENBQUE7SUFDbkUsTUFBTSxRQUFRLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQTtJQUM1QyxNQUFNLFFBQVEsR0FBRyxhQUFhLENBQUMsYUFBYSxJQUFJLElBQUksQ0FBQyxJQUFJLElBQUksV0FBVyxDQUFBO0lBRXhFLElBQUksYUFBYSxDQUFDLHdCQUF3QixHQUFHLENBQUMsRUFBRTtRQUM5QyxRQUFRLENBQUMsbUNBQW1DLGFBQWEsQ0FBQyx3QkFBd0IsNkJBQTZCLENBQUMsQ0FBQTtRQUNoSCxNQUFNLEtBQUssQ0FBQyxhQUFhLENBQUMsd0JBQXdCLENBQUMsQ0FBQTtLQUNwRDtJQUVELGlEQUFpRDtJQUNqRCxPQUFPLElBQUksRUFBRTtRQUNYLGFBQWEsRUFBRSxDQUFBO1FBQ2YsUUFBUSxDQUFDLFdBQVcsUUFBUSxxQkFBcUIsYUFBYSxFQUFFLENBQUMsQ0FBQTtRQUNqRSxJQUFJO1lBQ0YsTUFBTSxJQUFJLEVBQUUsQ0FBQTtZQUNaLFFBQVEsQ0FBQyxXQUFXLGFBQWEsaUJBQWlCLENBQUMsQ0FBQTtZQUNuRCxNQUFLO1NBQ047UUFBQyxPQUFPLEdBQUcsRUFBRTtZQUNaLElBQUksU0FBUyxFQUFFO2dCQUNiLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUE7YUFDbkI7WUFDRCxTQUFTLEdBQUcsR0FBRyxDQUFBO1NBQ2hCO1FBRUQsSUFBSSxDQUFDLE9BQU8sSUFBSSxhQUFhLEtBQUssUUFBUSxFQUFFO1lBQzFDLE1BQU0sSUFBSSxhQUFhLENBQUMseUNBQXlDLFFBQVEsV0FBVyxFQUFFLGtCQUFrQixDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUE7U0FDckg7UUFFRCxRQUFRLENBQUMsa0JBQWtCLGFBQWEscUJBQXFCLGlCQUFpQixtQ0FBbUMsQ0FBQyxDQUFBO1FBQ2xILE1BQU0sS0FBSyxDQUFDLGlCQUFpQixDQUFDLENBQUE7S0FDL0I7QUFDSCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLFVBQVUsa0JBQWtCLENBQUMsR0FBVztJQUM1QyxPQUFPLEdBQUcsQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQyxDQUFBO0FBQ2pDLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLGNBQWMsQ0FBQyxPQUFlO0lBQzVDLHdGQUF3RjtJQUN4RixNQUFNLFlBQVksR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUE7SUFFbkUsS0FBSyxNQUFNLElBQUksSUFBSSxPQUFPLEVBQUU7UUFDMUIsSUFBSSxZQUFZLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFO1lBQzNELE9BQU8sS0FBSyxDQUFBO1NBQ2I7S0FDRjtJQUVELE9BQU8sSUFBSSxDQUFBO0FBQ2IsQ0FBQztBQUVELE1BQU0sVUFBVSxhQUFhLENBQUMsR0FBVztJQUN2QyxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUE7QUFDdkIsQ0FBQztBQUVELE1BQU0sVUFBVSx3QkFBd0IsQ0FBQyxLQUFhO0lBQ3BELE1BQU0sY0FBYyxHQUFHO1FBQ3JCLElBQUksRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUc7UUFDakQsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHO0tBQzNELENBQUE7SUFDRCxNQUFNLFdBQVcsR0FBRyxDQUFDLEdBQVcsRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxNQUFNLENBQUMsQ0FBQTtJQUMvRSxNQUFNLEtBQUssR0FBRyxJQUFJLE1BQU0sQ0FBQyxJQUFJLGNBQWMsQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUE7SUFDOUUsT0FBTyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQTtBQUNqQyxDQUFDO0FBR0QsTUFBTSxDQUFOLElBQVksU0FRWDtBQVJELFdBQVksU0FBUztJQUNuQixnQ0FBaUIsQ0FBQTtJQUNqQiwrQkFBZ0IsQ0FBQTtJQUNoQixpQ0FBa0IsQ0FBQTtJQUNsQixrQ0FBbUIsQ0FBQTtJQUNuQixnQ0FBaUIsQ0FBQTtJQUNqQixnQ0FBaUIsQ0FBQTtJQUNqQixrQ0FBbUIsQ0FBQTtBQUNyQixDQUFDLEVBUlcsU0FBUyxLQUFULFNBQVMsUUFRcEI7QUFFRCxNQUFNLENBQUMsTUFBTSxLQUFLLEdBQUcsQ0FBQyxHQUFXLEVBQUUsYUFBd0IsRUFBVSxFQUFFO0lBQ3JFLE9BQU8sR0FBRyxhQUFhLEdBQUcsR0FBRyxHQUFHLFNBQVMsQ0FBQyxLQUFLLEVBQUUsQ0FBQTtBQUNuRCxDQUFDLENBQUE7QUFFRCxNQUFNLENBQUMsTUFBTSxHQUFHLEdBQUcsQ0FBQyxHQUFXLEVBQUUsRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFBO0FBQzdELE1BQU0sQ0FBQyxNQUFNLEtBQUssR0FBRyxDQUFDLEdBQVcsRUFBRSxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUE7QUFDakUsTUFBTSxDQUFDLE1BQU0sSUFBSSxHQUFHLENBQUMsR0FBVyxFQUFFLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQTtBQUMvRCxNQUFNLENBQUMsTUFBTSxJQUFJLEdBQUcsQ0FBQyxHQUFXLEVBQUUsRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFBO0FBQy9ELE1BQU0sQ0FBQyxNQUFNLE1BQU0sR0FBRyxDQUFDLEdBQVcsRUFBRSxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUE7QUFDbkUsTUFBTSxDQUFDLE1BQU0sTUFBTSxHQUFHLENBQUMsR0FBVyxFQUFFLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQTtBQUVuRSxNQUFNLENBQU4sSUFBWSxLQWdCWDtBQWhCRCxXQUFZLEtBQUs7SUFDZixvQ0FBaUIsQ0FBQTtJQUNqQixtQ0FBZ0IsQ0FBQTtJQUNoQiw4QkFBZ0IsQ0FBQTtJQUNoQixpQ0FBYyxDQUFBO0lBQ2QsNkJBQWUsQ0FBQTtJQUNmLCtCQUFpQixDQUFBO0lBQ2pCLCtCQUFpQixDQUFBO0lBQ2pCLHdCQUFVLENBQUE7SUFDViw4QkFBVyxDQUFBO0lBQ1gsaUNBQWMsQ0FBQTtJQUNkLHFDQUFhLENBQUE7SUFDYiwyQkFBYSxDQUFBO0lBQ2IsOEJBQVcsQ0FBQTtJQUNYLHFDQUFrQixDQUFBO0lBQ2xCLDZCQUFVLENBQUE7QUFDWixDQUFDLEVBaEJXLEtBQUssS0FBTCxLQUFLLFFBZ0JoQiJ9