UNPKG

@decaf-ts/fabric-weaver

Version:
296 lines (288 loc) 36.3 kB
import { BuildScripts, readFile, writeFile } from '@decaf-ts/utils'; import fs from 'fs'; import path from 'path'; import yaml from 'js-yaml'; import { Logging } from '@decaf-ts/logging'; import { execSync, spawn } from 'child_process'; /** * @module build-scripts * @description Custom build scripts for the fabric-weaver project. * @summary This module extends the BuildScripts class from @decaf-ts/utils to provide custom build functionality for the fabric-weaver project. It includes utilities for building command-line interfaces and handling different module formats (CommonJS and ES Modules). */ /** * @description Enumeration of module modes. * @summary Defines the different module formats supported in the build process. * @enum {string} * @readonly * @memberOf module:build-scripts */ var Modes; (function (Modes) { /** CommonJS module format */ Modes["CJS"] = "commonjs"; /** ECMAScript module format (ES2022) */ Modes["ESM"] = "es2022"; })(Modes || (Modes = {})); /** * @description Custom build scripts for the fabric-weaver project. * @summary Extends the BuildScripts class to provide project-specific build functionality, including command bundling and file manipulation. * @class BuildScriptsCustom * @extends BuildScripts */ class BuildScriptsCustom extends BuildScripts { /** * @description Builds command-line interfaces for the project. * @summary Processes each command in the Commands array, bundling the TypeScript files, adding a shebang line, and setting appropriate permissions. * @return {Promise<void>} * @mermaid * sequenceDiagram * participant BuildScriptsCustom * participant FileSystem * loop For each command * BuildScriptsCustom->>BuildScriptsCustom: bundle(Modes.CJS, true, true, `src/bin/${cmd}.ts`, cmd) * BuildScriptsCustom->>FileSystem: readFile(`bin/${cmd}.cjs`) * FileSystem-->>BuildScriptsCustom: file content * BuildScriptsCustom->>BuildScriptsCustom: Add shebang to file content * BuildScriptsCustom->>FileSystem: writeFile(`bin/${cmd}.cjs`, modified content) * BuildScriptsCustom->>FileSystem: chmodSync(`bin/${cmd}.cjs`, "755") * end */ async buildCommands() { const commands = fs.readdirSync(path.join(process.cwd() + "/src/bin")); for (const cmd of commands) { if (!cmd.endsWith(".ts")) continue; await this.bundle(Modes.CJS, true, true, `src/bin/${cmd}`, cmd); let data = readFile(`bin/${cmd}.cjs`); data = "#!/usr/bin/env node\n" + data; writeFile(`bin/${cmd}.cjs`, data); fs.chmodSync(`bin/${cmd}.cjs`, "755"); } } } const COMMA_SEPARATOR = ","; const SLASH_SEPARATOR = "/"; function safeParseJSON(v) { try { return JSON.parse(v); } catch (e) { throw new Error(`Invalid JSON: ${e.message}`); } } function safeParseInt(v) { if (typeof v === "number") return Math.floor(v); const value = Number(v); if (!Number.isInteger(value)) { throw new Error(`Invalid integer value: ${v}`); } return value; } function safeParseCSV(csv) { if (!csv.trim()) return []; return csv.split(COMMA_SEPARATOR).map((item) => item.trim()); } function safeParseSSV(csv) { console.log(csv); if (!csv.trim()) return []; return csv.split(SLASH_SEPARATOR).map((item) => `"${item.trim()}"`); } function mapParser(map) { const argz = Array.from(map, ([key, value]) => { if (typeof value === "boolean") { return value ? [`--${key}`] : []; } if (Array.isArray(value)) { return [`--${key}`, value.join(",")]; } return [`--${key}`, value.toString()]; }); return argz.map((el) => el.join(" ")); } const logger = Logging.for("yaml"); /** * @description Reads and parses a YAML file, optionally retrieving a specific property. * @summary This function reads a YAML file from the given path, parses its content, and returns either the entire parsed YAML object or a specific property value based on the provided path. * * @function readFileYaml * @template T - The type of the returned value when a specific property is requested. * @param {string} yamlFilePath - The path to the YAML file to be read. * @param {string} [variable] - Optional. A dot-notated path string that specifies the property to retrieve from the parsed YAML. * @return {Record<string, any> | T} Returns the entire parsed YAML object if no `variable` is provided, or the value of the specified property if `variable` is provided. * * @memberOf module:fabric-weaver.Utils * * @example * // Example 1: Read the entire YAML file * const config = readFileYaml("config/settings.yaml"); * console.log(config); * * @example * // Example 2: Retrieve a specific property from the YAML file * const dbHost = readFileYaml("config/settings.yaml", "database.host"); * console.log(dbHost); * * @example * // Example 3: Handle an error if the property does not exist * const invalidProperty = readFileYaml("config/settings.yaml", "server.port"); * * @mermaid * sequenceDiagram * participant Caller * participant readFileYaml * participant logger * participant fs * participant yaml * * Caller->>readFileYaml: Call with yamlFilePath and optional variable * readFileYaml->>logger: Log verbose message (Reading YAML file) * readFileYaml->>fs: Read file content * readFileYaml->>logger: Log verbose message (Parsed YAML content) * readFileYaml->>yaml: Parse YAML content * readFileYaml->>logger: Log verbose message (Parsed YAML object) * alt variable is provided * readFileYaml->>readFileYaml: Navigate through parsed YAML using variable path * alt Property exists * readFileYaml-->>Caller: Return specific property value * else Property doesn't exist * readFileYaml->>logger: Log error message * readFileYaml-->>Caller: Return error * end * else variable is not provided * readFileYaml-->>Caller: Return entire parsed YAML object * end */ function readFileYaml(yamlFilePath, variable) { const log = logger.for(readFileYaml); log.debug(`Reading YAML file: ${yamlFilePath}`); const content = fs.readFileSync(yamlFilePath, "utf8"); log.debug(`Parsed YAML content: ${content}`); const parsedYAML = yaml.load(content); log.debug(`Parsed YAML object: ${JSON.stringify(parsedYAML, null, 2)}`); log.info(`Returning ${variable ? `property '${variable}'` : "the entire parsed YAML object"}`); if (!variable) return parsedYAML; const variablePath = variable.split("."); return variablePath.reduce((acc, key) => { // eslint-disable-next-line no-prototype-builtins if (!acc.hasOwnProperty(key)) { throw new Error(`Unable to locate a property named '${key}' from path '${variable}' in file: \n> ${yamlFilePath}`); } return typeof acc[key] === "string" ? acc[key].trim() : acc[key]; }, parsedYAML); } /** * @description Writes a JSON object to a YAML file. * @summary This function takes a JSON object and writes it to a specified file path in YAML format. * It uses js-yaml to convert the JSON to YAML, and then writes the content to the file. * * @function writeFileYaml * @template T - The type of the JSON object to be written. * @param {string} path - The file path where the YAML content will be written. * @param {T} json - The JSON object to be converted to YAML and written to the file. * @return {void} * * @memberOf module:fabric-weaver.Utils * * @example * const config = { database: { host: 'localhost', port: 5432 } }; * writeFileYaml('config/settings.yaml', config); * * @mermaid * sequenceDiagram * participant Caller * participant writeFileYaml * participant logger * participant yaml * participant fs * * Caller->>writeFileYaml: Call with path and JSON * writeFileYaml->>logger: Log verbose message (Writing YAML file) * writeFileYaml->>logger: Log verbose message (Writing YAML content) * writeFileYaml->>yaml: Convert JSON to YAML * writeFileYaml->>logger: Log verbose message (Writing YAML content to file) * writeFileYaml->>fs: Write YAML content to file * writeFileYaml-->>Caller: Return (void) */ function writeFileYaml(path, json) { const log = logger.for(writeFileYaml); log.debug(`Writing YAML file: ${path}`); log.debug(`Writing YAML content: ${JSON.stringify(json, null, 2)}`); const content = yaml.dump(json, { indent: 2, lineWidth: -1 }); log.debug(`Writing YAML content to file: ${content}`); fs.writeFileSync(path, content.replace(/ null$/gm, ""), "utf8"); } async function runCommand(command, args = [], options = { stdio: "inherit" }, logMatch) { const log = Logging.for(runCommand); log.info(`Running command: ${command} ${args.join(" ")}`); if (!logMatch) return execSync([command, ...args].join(" "), options); return new Promise((resolve, reject) => { const child = spawn(command, args, options); let stdoutData = ""; let stderrData = ""; const checkMatch = (data) => { if (logMatch && logMatch.test(data)) { resolve(child); } }; child.stdout.on("data", (data) => { const strData = data.toString(); console.log(strData); stdoutData += strData; checkMatch(strData); }); child.stderr.on("data", (data) => { const strData = data.toString(); console.error(strData); stderrData += strData; checkMatch(strData); }); child.on("error", (err) => { reject(err); }); child.on("close", (code) => { if (code === 0) { resolve(child); } else { reject(new Error(`Process exited with code ${code}\nStdout: ${stdoutData}\nStderr: ${stderrData}`)); } }); }); } /** * @module fabric-weaver * @description This module serves as the main entry point for the fabric-weaver library, providing TypeScript integration with Hyperledger Fabric. * @summary Aggregates and exports functionality for managing Hyperledger Fabric infrastructures. * * The module includes: * 1. Utility functions and types from the "./utils" directory: * - Helper functions for interacting with Fabric binaries. * - Utilities for generating and managing Docker Compose files. * - Functions to assist in creating, maintaining, and updating Hyperledger Fabric networks. * * 2. Core functionality from the "./core" directory: * - TypeScript interfaces and classes representing Fabric concepts. * - Implementation of Fabric operations and interactions. * * 3. A VERSION constant: * - Represents the current version of the fabric-weaver module. * - Useful for version checking and compatibility purposes. * * This structure provides a comprehensive toolkit for working with Hyperledger Fabric in TypeScript, * allowing developers to easily set up, manage, and interact with Fabric networks and components. */ /** * @const VERSION * @name VERSION * @description Represents the current version of the fabric-weaver module. * @summary The actual version number is replaced during the build process. * @type {string} */ const VERSION = "0.0.14"; export { BuildScriptsCustom, VERSION, mapParser, readFileYaml, runCommand, safeParseCSV, safeParseInt, safeParseJSON, safeParseSSV, writeFileYaml }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFicmljLXdlYXZlci5lc20uY2pzIiwic291cmNlcyI6WyIuLi9zcmMvdXRpbHMtb2xkL2J1aWxkLXNjcmlwdHMudHMiLCIuLi9zcmMvY29yZS9jb25zdGFudHMvY29uc3RhbnRzLnRzIiwiLi4vc3JjL3V0aWxzL3BhcnNlcnMudHMiLCIuLi9zcmMvdXRpbHMveWFtbC50cyIsIi4uL3NyYy91dGlscy9jaGlsZC1wcm9jZXNzLnRzIiwiLi4vc3JjL2luZGV4LnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQG1vZHVsZSBidWlsZC1zY3JpcHRzXG4gKiBAZGVzY3JpcHRpb24gQ3VzdG9tIGJ1aWxkIHNjcmlwdHMgZm9yIHRoZSBmYWJyaWMtd2VhdmVyIHByb2plY3QuXG4gKiBAc3VtbWFyeSBUaGlzIG1vZHVsZSBleHRlbmRzIHRoZSBCdWlsZFNjcmlwdHMgY2xhc3MgZnJvbSBAZGVjYWYtdHMvdXRpbHMgdG8gcHJvdmlkZSBjdXN0b20gYnVpbGQgZnVuY3Rpb25hbGl0eSBmb3IgdGhlIGZhYnJpYy13ZWF2ZXIgcHJvamVjdC4gSXQgaW5jbHVkZXMgdXRpbGl0aWVzIGZvciBidWlsZGluZyBjb21tYW5kLWxpbmUgaW50ZXJmYWNlcyBhbmQgaGFuZGxpbmcgZGlmZmVyZW50IG1vZHVsZSBmb3JtYXRzIChDb21tb25KUyBhbmQgRVMgTW9kdWxlcykuXG4gKi9cblxuaW1wb3J0IHsgQnVpbGRTY3JpcHRzLCByZWFkRmlsZSwgd3JpdGVGaWxlIH0gZnJvbSBcIkBkZWNhZi10cy91dGlsc1wiO1xuaW1wb3J0IGZzIGZyb20gXCJmc1wiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gRW51bWVyYXRpb24gb2YgbW9kdWxlIG1vZGVzLlxuICogQHN1bW1hcnkgRGVmaW5lcyB0aGUgZGlmZmVyZW50IG1vZHVsZSBmb3JtYXRzIHN1cHBvcnRlZCBpbiB0aGUgYnVpbGQgcHJvY2Vzcy5cbiAqIEBlbnVtIHtzdHJpbmd9XG4gKiBAcmVhZG9ubHlcbiAqIEBtZW1iZXJPZiBtb2R1bGU6YnVpbGQtc2NyaXB0c1xuICovXG5lbnVtIE1vZGVzIHtcbiAgLyoqIENvbW1vbkpTIG1vZHVsZSBmb3JtYXQgKi9cbiAgQ0pTID0gXCJjb21tb25qc1wiLFxuICAvKiogRUNNQVNjcmlwdCBtb2R1bGUgZm9ybWF0IChFUzIwMjIpICovXG4gIEVTTSA9IFwiZXMyMDIyXCIsXG59XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uIEN1c3RvbSBidWlsZCBzY3JpcHRzIGZvciB0aGUgZmFicmljLXdlYXZlciBwcm9qZWN0LlxuICogQHN1bW1hcnkgRXh0ZW5kcyB0aGUgQnVpbGRTY3JpcHRzIGNsYXNzIHRvIHByb3ZpZGUgcHJvamVjdC1zcGVjaWZpYyBidWlsZCBmdW5jdGlvbmFsaXR5LCBpbmNsdWRpbmcgY29tbWFuZCBidW5kbGluZyBhbmQgZmlsZSBtYW5pcHVsYXRpb24uXG4gKiBAY2xhc3MgQnVpbGRTY3JpcHRzQ3VzdG9tXG4gKiBAZXh0ZW5kcyBCdWlsZFNjcmlwdHNcbiAqL1xuZXhwb3J0IGNsYXNzIEJ1aWxkU2NyaXB0c0N1c3RvbSBleHRlbmRzIEJ1aWxkU2NyaXB0cyB7XG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQnVpbGRzIGNvbW1hbmQtbGluZSBpbnRlcmZhY2VzIGZvciB0aGUgcHJvamVjdC5cbiAgICogQHN1bW1hcnkgUHJvY2Vzc2VzIGVhY2ggY29tbWFuZCBpbiB0aGUgQ29tbWFuZHMgYXJyYXksIGJ1bmRsaW5nIHRoZSBUeXBlU2NyaXB0IGZpbGVzLCBhZGRpbmcgYSBzaGViYW5nIGxpbmUsIGFuZCBzZXR0aW5nIGFwcHJvcHJpYXRlIHBlcm1pc3Npb25zLlxuICAgKiBAcmV0dXJuIHtQcm9taXNlPHZvaWQ+fVxuICAgKiBAbWVybWFpZFxuICAgKiBzZXF1ZW5jZURpYWdyYW1cbiAgICogICBwYXJ0aWNpcGFudCBCdWlsZFNjcmlwdHNDdXN0b21cbiAgICogICBwYXJ0aWNpcGFudCBGaWxlU3lzdGVtXG4gICAqICAgbG9vcCBGb3IgZWFjaCBjb21tYW5kXG4gICAqICAgICBCdWlsZFNjcmlwdHNDdXN0b20tPj5CdWlsZFNjcmlwdHNDdXN0b206IGJ1bmRsZShNb2Rlcy5DSlMsIHRydWUsIHRydWUsIGBzcmMvYmluLyR7Y21kfS50c2AsIGNtZClcbiAgICogICAgIEJ1aWxkU2NyaXB0c0N1c3RvbS0+PkZpbGVTeXN0ZW06IHJlYWRGaWxlKGBiaW4vJHtjbWR9LmNqc2ApXG4gICAqICAgICBGaWxlU3lzdGVtLS0+PkJ1aWxkU2NyaXB0c0N1c3RvbTogZmlsZSBjb250ZW50XG4gICAqICAgICBCdWlsZFNjcmlwdHNDdXN0b20tPj5CdWlsZFNjcmlwdHNDdXN0b206IEFkZCBzaGViYW5nIHRvIGZpbGUgY29udGVudFxuICAgKiAgICAgQnVpbGRTY3JpcHRzQ3VzdG9tLT4+RmlsZVN5c3RlbTogd3JpdGVGaWxlKGBiaW4vJHtjbWR9LmNqc2AsIG1vZGlmaWVkIGNvbnRlbnQpXG4gICAqICAgICBCdWlsZFNjcmlwdHNDdXN0b20tPj5GaWxlU3lzdGVtOiBjaG1vZFN5bmMoYGJpbi8ke2NtZH0uY2pzYCwgXCI3NTVcIilcbiAgICogICBlbmRcbiAgICovXG4gIGFzeW5jIGJ1aWxkQ29tbWFuZHMoKSB7XG4gICAgY29uc3QgY29tbWFuZHMgPSBmcy5yZWFkZGlyU3luYyhwYXRoLmpvaW4ocHJvY2Vzcy5jd2QoKSArIFwiL3NyYy9iaW5cIikpO1xuICAgIGZvciAoY29uc3QgY21kIG9mIGNvbW1hbmRzKSB7XG4gICAgICBpZiAoIWNtZC5lbmRzV2l0aChcIi50c1wiKSkgY29udGludWU7XG5cbiAgICAgIGF3YWl0IHRoaXMuYnVuZGxlKE1vZGVzLkNKUywgdHJ1ZSwgdHJ1ZSwgYHNyYy9iaW4vJHtjbWR9YCwgY21kKTtcbiAgICAgIGxldCBkYXRhID0gcmVhZEZpbGUoYGJpbi8ke2NtZH0uY2pzYCk7XG4gICAgICBkYXRhID0gXCIjIS91c3IvYmluL2VudiBub2RlXFxuXCIgKyBkYXRhO1xuICAgICAgd3JpdGVGaWxlKGBiaW4vJHtjbWR9LmNqc2AsIGRhdGEpO1xuICAgICAgZnMuY2htb2RTeW5jKGBiaW4vJHtjbWR9LmNqc2AsIFwiNzU1XCIpO1xuICAgIH1cbiAgfVxufVxuIiwiZXhwb3J0IGNvbnN0IENPTU1BX1NFUEFSQVRPUiA9IFwiLFwiO1xuZXhwb3J0IGNvbnN0IENPTE9OX1NFUEFSQVRPUiA9IFwiOlwiO1xuZXhwb3J0IGNvbnN0IFBJUEVfU0VQQVJBVE9SID0gXCJ8XCI7XG5leHBvcnQgY29uc3QgU0xBU0hfU0VQQVJBVE9SID0gXCIvXCI7XG5cbmV4cG9ydCBjb25zdCBOQU1FX1BMQUNFSE9MREVSID0gXCIjI05BTUUjI1wiO1xuZXhwb3J0IGNvbnN0IFZFUlNJT05fUExBQ0VIT0xERVIgPSBcIiMjVkVSIyNcIjtcbmV4cG9ydCBjb25zdCBFTlRSWV9QTEFDRUhPTERFUiA9IFwiIyNFTlRSWSMjXCI7XG4iLCJpbXBvcnQgeyBDT01NQV9TRVBBUkFUT1IsIFNMQVNIX1NFUEFSQVRPUiB9IGZyb20gXCIuLi9jb3JlL2NvbnN0YW50cy9jb25zdGFudHNcIjtcblxuZXhwb3J0IGZ1bmN0aW9uIHNhZmVQYXJzZUpTT04odjogc3RyaW5nKTogdW5rbm93biB7XG4gIHRyeSB7XG4gICAgcmV0dXJuIEpTT04ucGFyc2Uodik7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgSlNPTjogJHsoZSBhcyBFcnJvcikubWVzc2FnZX1gKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gc2FmZVBhcnNlSW50KHY6IHN0cmluZyB8IG51bWJlcik6IG51bWJlciB7XG4gIGlmICh0eXBlb2YgdiA9PT0gXCJudW1iZXJcIikgcmV0dXJuIE1hdGguZmxvb3Iodik7XG5cbiAgY29uc3QgdmFsdWUgPSBOdW1iZXIodik7XG5cbiAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKHZhbHVlKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBpbnRlZ2VyIHZhbHVlOiAke3Z9YCk7XG4gIH1cblxuICByZXR1cm4gdmFsdWU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzYWZlUGFyc2VDU1YoY3N2OiBzdHJpbmcpOiBzdHJpbmdbXSB7XG4gIGlmICghY3N2LnRyaW0oKSkgcmV0dXJuIFtdO1xuXG4gIHJldHVybiBjc3Yuc3BsaXQoQ09NTUFfU0VQQVJBVE9SKS5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNhZmVQYXJzZVNTVihjc3Y6IHN0cmluZyk6IHN0cmluZ1tdIHtcbiAgY29uc29sZS5sb2coY3N2KTtcbiAgaWYgKCFjc3YudHJpbSgpKSByZXR1cm4gW107XG5cbiAgcmV0dXJuIGNzdi5zcGxpdChTTEFTSF9TRVBBUkFUT1IpLm1hcCgoaXRlbSkgPT4gYFwiJHtpdGVtLnRyaW0oKX1cImApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbWFwUGFyc2VyKFxuICBtYXA6IE1hcDxzdHJpbmcsIHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW4gfCBzdHJpbmdbXT5cbik6IHN0cmluZ1tdIHtcbiAgY29uc3QgYXJneiA9IEFycmF5LmZyb20obWFwLCAoW2tleSwgdmFsdWVdKSA9PiB7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJib29sZWFuXCIpIHtcbiAgICAgIHJldHVybiB2YWx1ZSA/IFtgLS0ke2tleX1gXSA6IFtdO1xuICAgIH1cbiAgICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgIHJldHVybiBbYC0tJHtrZXl9YCwgdmFsdWUuam9pbihcIixcIildO1xuICAgIH1cbiAgICByZXR1cm4gW2AtLSR7a2V5fWAsIHZhbHVlLnRvU3RyaW5nKCldO1xuICB9KTtcblxuICByZXR1cm4gYXJnei5tYXAoKGVsKSA9PiBlbC5qb2luKFwiIFwiKSk7XG59XG4iLCJpbXBvcnQgZnMgZnJvbSBcImZzXCI7XG5pbXBvcnQgeWFtbCBmcm9tIFwianMteWFtbFwiO1xuaW1wb3J0IHsgTG9nZ2luZyB9IGZyb20gXCJAZGVjYWYtdHMvbG9nZ2luZ1wiO1xuXG5jb25zdCBsb2dnZXIgPSBMb2dnaW5nLmZvcihcInlhbWxcIik7XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uIFJlYWRzIGFuZCBwYXJzZXMgYSBZQU1MIGZpbGUsIG9wdGlvbmFsbHkgcmV0cmlldmluZyBhIHNwZWNpZmljIHByb3BlcnR5LlxuICogQHN1bW1hcnkgVGhpcyBmdW5jdGlvbiByZWFkcyBhIFlBTUwgZmlsZSBmcm9tIHRoZSBnaXZlbiBwYXRoLCBwYXJzZXMgaXRzIGNvbnRlbnQsIGFuZCByZXR1cm5zIGVpdGhlciB0aGUgZW50aXJlIHBhcnNlZCBZQU1MIG9iamVjdCBvciBhIHNwZWNpZmljIHByb3BlcnR5IHZhbHVlIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBwYXRoLlxuICpcbiAqIEBmdW5jdGlvbiByZWFkRmlsZVlhbWxcbiAqIEB0ZW1wbGF0ZSBUIC0gVGhlIHR5cGUgb2YgdGhlIHJldHVybmVkIHZhbHVlIHdoZW4gYSBzcGVjaWZpYyBwcm9wZXJ0eSBpcyByZXF1ZXN0ZWQuXG4gKiBAcGFyYW0ge3N0cmluZ30geWFtbEZpbGVQYXRoIC0gVGhlIHBhdGggdG8gdGhlIFlBTUwgZmlsZSB0byBiZSByZWFkLlxuICogQHBhcmFtIHtzdHJpbmd9IFt2YXJpYWJsZV0gLSBPcHRpb25hbC4gQSBkb3Qtbm90YXRlZCBwYXRoIHN0cmluZyB0aGF0IHNwZWNpZmllcyB0aGUgcHJvcGVydHkgdG8gcmV0cmlldmUgZnJvbSB0aGUgcGFyc2VkIFlBTUwuXG4gKiBAcmV0dXJuIHtSZWNvcmQ8c3RyaW5nLCBhbnk+IHwgVH0gUmV0dXJucyB0aGUgZW50aXJlIHBhcnNlZCBZQU1MIG9iamVjdCBpZiBubyBgdmFyaWFibGVgIGlzIHByb3ZpZGVkLCBvciB0aGUgdmFsdWUgb2YgdGhlIHNwZWNpZmllZCBwcm9wZXJ0eSBpZiBgdmFyaWFibGVgIGlzIHByb3ZpZGVkLlxuICpcbiAqIEBtZW1iZXJPZiBtb2R1bGU6ZmFicmljLXdlYXZlci5VdGlsc1xuICpcbiAqIEBleGFtcGxlXG4gKiAvLyBFeGFtcGxlIDE6IFJlYWQgdGhlIGVudGlyZSBZQU1MIGZpbGVcbiAqIGNvbnN0IGNvbmZpZyA9IHJlYWRGaWxlWWFtbChcImNvbmZpZy9zZXR0aW5ncy55YW1sXCIpO1xuICogY29uc29sZS5sb2coY29uZmlnKTtcbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gRXhhbXBsZSAyOiBSZXRyaWV2ZSBhIHNwZWNpZmljIHByb3BlcnR5IGZyb20gdGhlIFlBTUwgZmlsZVxuICogY29uc3QgZGJIb3N0ID0gcmVhZEZpbGVZYW1sKFwiY29uZmlnL3NldHRpbmdzLnlhbWxcIiwgXCJkYXRhYmFzZS5ob3N0XCIpO1xuICogY29uc29sZS5sb2coZGJIb3N0KTtcbiAqXG4gKiBAZXhhbXBsZVxuICogLy8gRXhhbXBsZSAzOiBIYW5kbGUgYW4gZXJyb3IgaWYgdGhlIHByb3BlcnR5IGRvZXMgbm90IGV4aXN0XG4gKiBjb25zdCBpbnZhbGlkUHJvcGVydHkgPSByZWFkRmlsZVlhbWwoXCJjb25maWcvc2V0dGluZ3MueWFtbFwiLCBcInNlcnZlci5wb3J0XCIpO1xuICpcbiAqIEBtZXJtYWlkXG4gKiBzZXF1ZW5jZURpYWdyYW1cbiAqICAgcGFydGljaXBhbnQgQ2FsbGVyXG4gKiAgIHBhcnRpY2lwYW50IHJlYWRGaWxlWWFtbFxuICogICBwYXJ0aWNpcGFudCBsb2dnZXJcbiAqICAgcGFydGljaXBhbnQgZnNcbiAqICAgcGFydGljaXBhbnQgeWFtbFxuICpcbiAqICAgQ2FsbGVyLT4+cmVhZEZpbGVZYW1sOiBDYWxsIHdpdGggeWFtbEZpbGVQYXRoIGFuZCBvcHRpb25hbCB2YXJpYWJsZVxuICogICByZWFkRmlsZVlhbWwtPj5sb2dnZXI6IExvZyB2ZXJib3NlIG1lc3NhZ2UgKFJlYWRpbmcgWUFNTCBmaWxlKVxuICogICByZWFkRmlsZVlhbWwtPj5mczogUmVhZCBmaWxlIGNvbnRlbnRcbiAqICAgcmVhZEZpbGVZYW1sLT4+bG9nZ2VyOiBMb2cgdmVyYm9zZSBtZXNzYWdlIChQYXJzZWQgWUFNTCBjb250ZW50KVxuICogICByZWFkRmlsZVlhbWwtPj55YW1sOiBQYXJzZSBZQU1MIGNvbnRlbnRcbiAqICAgcmVhZEZpbGVZYW1sLT4+bG9nZ2VyOiBMb2cgdmVyYm9zZSBtZXNzYWdlIChQYXJzZWQgWUFNTCBvYmplY3QpXG4gKiAgIGFsdCB2YXJpYWJsZSBpcyBwcm92aWRlZFxuICogICAgIHJlYWRGaWxlWWFtbC0+PnJlYWRGaWxlWWFtbDogTmF2aWdhdGUgdGhyb3VnaCBwYXJzZWQgWUFNTCB1c2luZyB2YXJpYWJsZSBwYXRoXG4gKiAgICAgYWx0IFByb3BlcnR5IGV4aXN0c1xuICogICAgICAgcmVhZEZpbGVZYW1sLS0+PkNhbGxlcjogUmV0dXJuIHNwZWNpZmljIHByb3BlcnR5IHZhbHVlXG4gKiAgICAgZWxzZSBQcm9wZXJ0eSBkb2Vzbid0IGV4aXN0XG4gKiAgICAgICByZWFkRmlsZVlhbWwtPj5sb2dnZXI6IExvZyBlcnJvciBtZXNzYWdlXG4gKiAgICAgICByZWFkRmlsZVlhbWwtLT4+Q2FsbGVyOiBSZXR1cm4gZXJyb3JcbiAqICAgICBlbmRcbiAqICAgZWxzZSB2YXJpYWJsZSBpcyBub3QgcHJvdmlkZWRcbiAqICAgICByZWFkRmlsZVlhbWwtLT4+Q2FsbGVyOiBSZXR1cm4gZW50aXJlIHBhcnNlZCBZQU1MIG9iamVjdFxuICogICBlbmRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlYWRGaWxlWWFtbDxUPihcbiAgeWFtbEZpbGVQYXRoOiBzdHJpbmcsXG4gIHZhcmlhYmxlPzogc3RyaW5nXG4pOiBSZWNvcmQ8c3RyaW5nLCBhbnk+IHwgVCB7XG4gIGNvbnN0IGxvZyA9IGxvZ2dlci5mb3IocmVhZEZpbGVZYW1sKTtcblxuICBsb2cuZGVidWcoYFJlYWRpbmcgWUFNTCBmaWxlOiAke3lhbWxGaWxlUGF0aH1gKTtcbiAgY29uc3QgY29udGVudCA9IGZzLnJlYWRGaWxlU3luYyh5YW1sRmlsZVBhdGgsIFwidXRmOFwiKTtcblxuICBsb2cuZGVidWcoYFBhcnNlZCBZQU1MIGNvbnRlbnQ6ICR7Y29udGVudH1gKTtcbiAgY29uc3QgcGFyc2VkWUFNTCA9IHlhbWwubG9hZChjb250ZW50KSBhcyBSZWNvcmQ8c3RyaW5nLCBhbnk+O1xuXG4gIGxvZy5kZWJ1ZyhgUGFyc2VkIFlBTUwgb2JqZWN0OiAke0pTT04uc3RyaW5naWZ5KHBhcnNlZFlBTUwsIG51bGwsIDIpfWApO1xuXG4gIGxvZy5pbmZvKFxuICAgIGBSZXR1cm5pbmcgJHt2YXJpYWJsZSA/IGBwcm9wZXJ0eSAnJHt2YXJpYWJsZX0nYCA6IFwidGhlIGVudGlyZSBwYXJzZWQgWUFNTCBvYmplY3RcIn1gXG4gICk7XG4gIGlmICghdmFyaWFibGUpIHJldHVybiBwYXJzZWRZQU1MO1xuXG4gIGNvbnN0IHZhcmlhYmxlUGF0aCA9IHZhcmlhYmxlLnNwbGl0KFwiLlwiKTtcblxuICByZXR1cm4gdmFyaWFibGVQYXRoLnJlZHVjZSgoYWNjLCBrZXkpID0+IHtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tcHJvdG90eXBlLWJ1aWx0aW5zXG4gICAgaWYgKCFhY2MuaGFzT3duUHJvcGVydHkoa2V5KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBgVW5hYmxlIHRvIGxvY2F0ZSBhIHByb3BlcnR5IG5hbWVkICcke2tleX0nIGZyb20gcGF0aCAnJHt2YXJpYWJsZX0nIGluIGZpbGU6IFxcbj4gJHt5YW1sRmlsZVBhdGh9YFxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHR5cGVvZiBhY2Nba2V5XSA9PT0gXCJzdHJpbmdcIiA/IGFjY1trZXldLnRyaW0oKSA6IGFjY1trZXldO1xuICB9LCBwYXJzZWRZQU1MKTtcbn1cblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gV3JpdGVzIGEgSlNPTiBvYmplY3QgdG8gYSBZQU1MIGZpbGUuXG4gKiBAc3VtbWFyeSBUaGlzIGZ1bmN0aW9uIHRha2VzIGEgSlNPTiBvYmplY3QgYW5kIHdyaXRlcyBpdCB0byBhIHNwZWNpZmllZCBmaWxlIHBhdGggaW4gWUFNTCBmb3JtYXQuXG4gKiBJdCB1c2VzIGpzLXlhbWwgdG8gY29udmVydCB0aGUgSlNPTiB0byBZQU1MLCBhbmQgdGhlbiB3cml0ZXMgdGhlIGNvbnRlbnQgdG8gdGhlIGZpbGUuXG4gKlxuICogQGZ1bmN0aW9uIHdyaXRlRmlsZVlhbWxcbiAqIEB0ZW1wbGF0ZSBUIC0gVGhlIHR5cGUgb2YgdGhlIEpTT04gb2JqZWN0IHRvIGJlIHdyaXR0ZW4uXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBmaWxlIHBhdGggd2hlcmUgdGhlIFlBTUwgY29udGVudCB3aWxsIGJlIHdyaXR0ZW4uXG4gKiBAcGFyYW0ge1R9IGpzb24gLSBUaGUgSlNPTiBvYmplY3QgdG8gYmUgY29udmVydGVkIHRvIFlBTUwgYW5kIHdyaXR0ZW4gdG8gdGhlIGZpbGUuXG4gKiBAcmV0dXJuIHt2b2lkfVxuICpcbiAqIEBtZW1iZXJPZiBtb2R1bGU6ZmFicmljLXdlYXZlci5VdGlsc1xuICpcbiAqIEBleGFtcGxlXG4gKiBjb25zdCBjb25maWcgPSB7IGRhdGFiYXNlOiB7IGhvc3Q6ICdsb2NhbGhvc3QnLCBwb3J0OiA1NDMyIH0gfTtcbiAqIHdyaXRlRmlsZVlhbWwoJ2NvbmZpZy9zZXR0aW5ncy55YW1sJywgY29uZmlnKTtcbiAqXG4gKiBAbWVybWFpZFxuICogc2VxdWVuY2VEaWFncmFtXG4gKiAgIHBhcnRpY2lwYW50IENhbGxlclxuICogICBwYXJ0aWNpcGFudCB3cml0ZUZpbGVZYW1sXG4gKiAgIHBhcnRpY2lwYW50IGxvZ2dlclxuICogICBwYXJ0aWNpcGFudCB5YW1sXG4gKiAgIHBhcnRpY2lwYW50IGZzXG4gKlxuICogICBDYWxsZXItPj53cml0ZUZpbGVZYW1sOiBDYWxsIHdpdGggcGF0aCBhbmQgSlNPTlxuICogICB3cml0ZUZpbGVZYW1sLT4+bG9nZ2VyOiBMb2cgdmVyYm9zZSBtZXNzYWdlIChXcml0aW5nIFlBTUwgZmlsZSlcbiAqICAgd3JpdGVGaWxlWWFtbC0+PmxvZ2dlcjogTG9nIHZlcmJvc2UgbWVzc2FnZSAoV3JpdGluZyBZQU1MIGNvbnRlbnQpXG4gKiAgIHdyaXRlRmlsZVlhbWwtPj55YW1sOiBDb252ZXJ0IEpTT04gdG8gWUFNTFxuICogICB3cml0ZUZpbGVZYW1sLT4+bG9nZ2VyOiBMb2cgdmVyYm9zZSBtZXNzYWdlIChXcml0aW5nIFlBTUwgY29udGVudCB0byBmaWxlKVxuICogICB3cml0ZUZpbGVZYW1sLT4+ZnM6IFdyaXRlIFlBTUwgY29udGVudCB0byBmaWxlXG4gKiAgIHdyaXRlRmlsZVlhbWwtLT4+Q2FsbGVyOiBSZXR1cm4gKHZvaWQpXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3cml0ZUZpbGVZYW1sPFQ+KHBhdGg6IHN0cmluZywganNvbjogVCkge1xuICBjb25zdCBsb2cgPSBsb2dnZXIuZm9yKHdyaXRlRmlsZVlhbWwpO1xuXG4gIGxvZy5kZWJ1ZyhgV3JpdGluZyBZQU1MIGZpbGU6ICR7cGF0aH1gKTtcbiAgbG9nLmRlYnVnKGBXcml0aW5nIFlBTUwgY29udGVudDogJHtKU09OLnN0cmluZ2lmeShqc29uLCBudWxsLCAyKX1gKTtcbiAgY29uc3QgY29udGVudCA9IHlhbWwuZHVtcChqc29uLCB7IGluZGVudDogMiwgbGluZVdpZHRoOiAtMSB9KTtcblxuICBsb2cuZGVidWcoYFdyaXRpbmcgWUFNTCBjb250ZW50IHRvIGZpbGU6ICR7Y29udGVudH1gKTtcbiAgZnMud3JpdGVGaWxlU3luYyhwYXRoLCBjb250ZW50LnJlcGxhY2UoLyBudWxsJC9nbSwgXCJcIiksIFwidXRmOFwiKTtcbn1cbiIsImltcG9ydCB7IHNwYXduLCBDaGlsZFByb2Nlc3MsIGV4ZWNTeW5jIH0gZnJvbSBcImNoaWxkX3Byb2Nlc3NcIjtcbmltcG9ydCB7IExvZ2dlciwgTG9nZ2luZyB9IGZyb20gXCJAZGVjYWYtdHMvbG9nZ2luZ1wiO1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcnVuQ29tbWFuZChcbiAgY29tbWFuZDogc3RyaW5nLFxuICBhcmdzOiBzdHJpbmdbXSA9IFtdLFxuICBvcHRpb25zOiB7IFtpbmRleGVyOiBzdHJpbmddOiBzdHJpbmcgfSA9IHsgc3RkaW86IFwiaW5oZXJpdFwiIH0sXG4gIGxvZ01hdGNoPzogUmVnRXhwXG4pOiBQcm9taXNlPENoaWxkUHJvY2VzcyB8IHZvaWQgfCBCdWZmZXI+IHtcbiAgY29uc3QgbG9nOiBMb2dnZXIgPSBMb2dnaW5nLmZvcihydW5Db21tYW5kKTtcbiAgbG9nLmluZm8oYFJ1bm5pbmcgY29tbWFuZDogJHtjb21tYW5kfSAke2FyZ3Muam9pbihcIiBcIil9YCk7XG5cbiAgaWYgKCFsb2dNYXRjaCkgcmV0dXJuIGV4ZWNTeW5jKFtjb21tYW5kLCAuLi5hcmdzXS5qb2luKFwiIFwiKSwgb3B0aW9ucyk7XG5cbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBjb25zdCBjaGlsZCA9IHNwYXduKGNvbW1hbmQsIGFyZ3MsIG9wdGlvbnMpO1xuICAgIGxldCBzdGRvdXREYXRhID0gXCJcIjtcbiAgICBsZXQgc3RkZXJyRGF0YSA9IFwiXCI7XG5cbiAgICBjb25zdCBjaGVja01hdGNoID0gKGRhdGE6IHN0cmluZykgPT4ge1xuICAgICAgaWYgKGxvZ01hdGNoICYmIGxvZ01hdGNoLnRlc3QoZGF0YSkpIHtcbiAgICAgICAgcmVzb2x2ZShjaGlsZCk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIGNoaWxkLnN0ZG91dC5vbihcImRhdGFcIiwgKGRhdGE6IEJ1ZmZlcikgPT4ge1xuICAgICAgY29uc3Qgc3RyRGF0YSA9IGRhdGEudG9TdHJpbmcoKTtcbiAgICAgIGNvbnNvbGUubG9nKHN0ckRhdGEpO1xuICAgICAgc3Rkb3V0RGF0YSArPSBzdHJEYXRhO1xuICAgICAgY2hlY2tNYXRjaChzdHJEYXRhKTtcbiAgICB9KTtcblxuICAgIGNoaWxkLnN0ZGVyci5vbihcImRhdGFcIiwgKGRhdGE6IEJ1ZmZlcikgPT4ge1xuICAgICAgY29uc3Qgc3RyRGF0YSA9IGRhdGEudG9TdHJpbmcoKTtcbiAgICAgIGNvbnNvbGUuZXJyb3Ioc3RyRGF0YSk7XG4gICAgICBzdGRlcnJEYXRhICs9IHN0ckRhdGE7XG4gICAgICBjaGVja01hdGNoKHN0ckRhdGEpO1xuICAgIH0pO1xuXG4gICAgY2hpbGQub24oXCJlcnJvclwiLCAoZXJyOiBFcnJvcikgPT4ge1xuICAgICAgcmVqZWN0KGVycik7XG4gICAgfSk7XG5cbiAgICBjaGlsZC5vbihcImNsb3NlXCIsIChjb2RlOiBudW1iZXIpID0+IHtcbiAgICAgIGlmIChjb2RlID09PSAwKSB7XG4gICAgICAgIHJlc29sdmUoY2hpbGQpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVqZWN0KFxuICAgICAgICAgIG5ldyBFcnJvcihcbiAgICAgICAgICAgIGBQcm9jZXNzIGV4aXRlZCB3aXRoIGNvZGUgJHtjb2RlfVxcblN0ZG91dDogJHtzdGRvdXREYXRhfVxcblN0ZGVycjogJHtzdGRlcnJEYXRhfWBcbiAgICAgICAgICApXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfSk7XG4gIH0pO1xufVxuIiwiLyoqXG4gKiBAbW9kdWxlIGZhYnJpYy13ZWF2ZXJcbiAqIEBkZXNjcmlwdGlvbiBUaGlzIG1vZHVsZSBzZXJ2ZXMgYXMgdGhlIG1haW4gZW50cnkgcG9pbnQgZm9yIHRoZSBmYWJyaWMtd2VhdmVyIGxpYnJhcnksIHByb3ZpZGluZyBUeXBlU2NyaXB0IGludGVncmF0aW9uIHdpdGggSHlwZXJsZWRnZXIgRmFicmljLlxuICogQHN1bW1hcnkgQWdncmVnYXRlcyBhbmQgZXhwb3J0cyBmdW5jdGlvbmFsaXR5IGZvciBtYW5hZ2luZyBIeXBlcmxlZGdlciBGYWJyaWMgaW5mcmFzdHJ1Y3R1cmVzLlxuICpcbiAqIFRoZSBtb2R1bGUgaW5jbHVkZXM6XG4gKiAxLiBVdGlsaXR5IGZ1bmN0aW9ucyBhbmQgdHlwZXMgZnJvbSB0aGUgXCIuL3V0aWxzXCIgZGlyZWN0b3J5OlxuICogICAgLSBIZWxwZXIgZnVuY3Rpb25zIGZvciBpbnRlcmFjdGluZyB3aXRoIEZhYnJpYyBiaW5hcmllcy5cbiAqICAgIC0gVXRpbGl0aWVzIGZvciBnZW5lcmF0aW5nIGFuZCBtYW5hZ2luZyBEb2NrZXIgQ29tcG9zZSBmaWxlcy5cbiAqICAgIC0gRnVuY3Rpb25zIHRvIGFzc2lzdCBpbiBjcmVhdGluZywgbWFpbnRhaW5pbmcsIGFuZCB1cGRhdGluZyBIeXBlcmxlZGdlciBGYWJyaWMgbmV0d29ya3MuXG4gKlxuICogMi4gQ29yZSBmdW5jdGlvbmFsaXR5IGZyb20gdGhlIFwiLi9jb3JlXCIgZGlyZWN0b3J5OlxuICogICAgLSBUeXBlU2NyaXB0IGludGVyZmFjZXMgYW5kIGNsYXNzZXMgcmVwcmVzZW50aW5nIEZhYnJpYyBjb25jZXB0cy5cbiAqICAgIC0gSW1wbGVtZW50YXRpb24gb2YgRmFicmljIG9wZXJhdGlvbnMgYW5kIGludGVyYWN0aW9ucy5cbiAqXG4gKiAzLiBBIFZFUlNJT04gY29uc3RhbnQ6XG4gKiAgICAtIFJlcHJlc2VudHMgdGhlIGN1cnJlbnQgdmVyc2lvbiBvZiB0aGUgZmFicmljLXdlYXZlciBtb2R1bGUuXG4gKiAgICAtIFVzZWZ1bCBmb3IgdmVyc2lvbiBjaGVja2luZyBhbmQgY29tcGF0aWJpbGl0eSBwdXJwb3Nlcy5cbiAqXG4gKiBUaGlzIHN0cnVjdHVyZSBwcm92aWRlcyBhIGNvbXByZWhlbnNpdmUgdG9vbGtpdCBmb3Igd29ya2luZyB3aXRoIEh5cGVybGVkZ2VyIEZhYnJpYyBpbiBUeXBlU2NyaXB0LFxuICogYWxsb3dpbmcgZGV2ZWxvcGVycyB0byBlYXNpbHkgc2V0IHVwLCBtYW5hZ2UsIGFuZCBpbnRlcmFjdCB3aXRoIEZhYnJpYyBuZXR3b3JrcyBhbmQgY29tcG9uZW50cy5cbiAqL1xuXG5leHBvcnQgKiBmcm9tIFwiLi91dGlscy1vbGRcIjtcbmV4cG9ydCAqIGZyb20gXCIuL3V0aWxzXCI7XG5cbi8qKlxuICogQGNvbnN0IFZFUlNJT05cbiAqIEBuYW1lIFZFUlNJT05cbiAqIEBkZXNjcmlwdGlvbiBSZXByZXNlbnRzIHRoZSBjdXJyZW50IHZlcnNpb24gb2YgdGhlIGZhYnJpYy13ZWF2ZXIgbW9kdWxlLlxuICogQHN1bW1hcnkgVGhlIGFjdHVhbCB2ZXJzaW9uIG51bWJlciBpcyByZXBsYWNlZCBkdXJpbmcgdGhlIGJ1aWxkIHByb2Nlc3MuXG4gKiBAdHlwZSB7c3RyaW5nfVxuICovXG5leHBvcnQgY29uc3QgVkVSU0lPTiA9IFwiIyNWRVJTSU9OIyNcIjtcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7O0FBQUE7Ozs7QUFJRztBQU1IOzs7Ozs7QUFNRztBQUNILElBQUssS0FLSjtBQUxELENBQUEsVUFBSyxLQUFLLEVBQUE7O0FBRVIsSUFBQSxLQUFBLENBQUEsS0FBQSxDQUFBLEdBQUEsVUFBZ0I7O0FBRWhCLElBQUEsS0FBQSxDQUFBLEtBQUEsQ0FBQSxHQUFBLFFBQWM7QUFDaEIsQ0FBQyxFQUxJLEtBQUssS0FBTCxLQUFLLEdBS1QsRUFBQSxDQUFBLENBQUE7QUFFRDs7Ozs7QUFLRztBQUNHLE1BQU8sa0JBQW1CLFNBQVEsWUFBWSxDQUFBO0FBQ2xEOzs7Ozs7Ozs7Ozs7Ozs7O0FBZ0JHO0FBQ0gsSUFBQSxNQUFNLGFBQWEsR0FBQTtBQUNqQixRQUFBLE1BQU0sUUFBUSxHQUFHLEVBQUUsQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxFQUFFLEdBQUcsVUFBVSxDQUFDLENBQUM7QUFDdEUsUUFBQSxLQUFLLE1BQU0sR0FBRyxJQUFJLFFBQVEsRUFBRTtBQUMxQixZQUFBLElBQUksQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQztnQkFBRTtBQUUxQixZQUFBLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FBVyxRQUFBLEVBQUEsR0FBRyxFQUFFLEVBQUUsR0FBRyxDQUFDO1lBQy9ELElBQUksSUFBSSxHQUFHLFFBQVEsQ0FBQyxPQUFPLEdBQUcsQ0FBQSxJQUFBLENBQU0sQ0FBQztBQUNyQyxZQUFBLElBQUksR0FBRyx1QkFBdUIsR0FBRyxJQUFJO0FBQ3JDLFlBQUEsU0FBUyxD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