UNPKG

@valueflows/vf-graphql-holochain

Version:

GraphQL schema bindings for the Holochain implementation of ValueFlows

297 lines 43.6 kB
/** * Connection wrapper for Holochain DNA method calls * * :TODO: :WARNING: * * This layer is currently unsuitable for mixing with DNAs that use dna-local identifier formats, and * will cause encoding errors if 2-element lists of identifiers are passed. * * Such tuples are interpreted as [`DnaHash`, `AnyDhtHash`] pairs by the GraphQL <-> Holochain * serialisation layer and transformed into compound IDs at I/O time. So, this adapter should * *only* be used to wrap DNAs explicitly developed with multi-DNA references in mind. * * Also :TODO: - standardise a binary format for universally unique Holochain entry/header identifiers. * * @package: hREA * @since: 2019-05-20 */ import { AppWebsocket, AdminWebsocket, CellType } from '@holochain/client'; import deepForEach from 'deep-for-each'; import isObject from 'is-object'; import { Buffer } from 'buffer'; import { format, parse } from 'fecha'; import b64 from "js-base64"; const { Base64 } = b64; //---------------------------------------------------------------------------------------------------------------------- // Connection persistence and multi-conductor / multi-agent handling //---------------------------------------------------------------------------------------------------------------------- // :NOTE: when calling AppWebsocket.connect for the Launcher Context // it just expects an empty string for the socketURI. Other environments require it. let ENV_CONNECTION_URI = process.env.REACT_APP_HC_CONN_URL || ''; let ENV_ADMIN_CONNECTION_URI = process.env.REACT_APP_HC_ADMIN_CONN_URL || ''; let ENV_HOLOCHAIN_APP_ID = process.env.REACT_APP_HC_APP_ID || ''; const CONNECTION_CACHE = {}; /** * If no `conductorUri` is provided or is otherwise empty or undefined, * a connection is attempted via the `REACT_APP_HC_CONN_URL` environment variable. * Only if running in a Holochain Launcher context, can both of the before-mentioned values * be left undefined or empty, and the websocket connection can still be established. */ export async function autoConnect(conductorUri, adminConductorUri, appID, traceAppSignals) { conductorUri = conductorUri || ENV_CONNECTION_URI; adminConductorUri = adminConductorUri || ENV_ADMIN_CONNECTION_URI; const conn = await openConnection(conductorUri, traceAppSignals); const { dnaConfig, appId: realAppId, } = await sniffHolochainAppCells(conn, appID); let adminConn = null; if (adminConductorUri) { adminConn = await AdminWebsocket.connect(adminConductorUri); for await (let cellId of Object.values(dnaConfig)) { await adminConn.authorizeSigningCredentials(cellId); } } return { conn, adminConn, dnaConfig, conductorUri, adminConductorUri, appId: realAppId }; } /** * Inits a connection for the given websocket URI. * * This method gives calling code an opportunity to register globals for all future * instances of a connection of the same `socketURI`. To ensure this is done reliably, * a runtime error will be thrown by `getConnection` if no `openConnection` has * been previously performed for the same `socketURI`. */ export const openConnection = (appSocketURI, traceAppSignals) => { console.log(`Init Holochain connection: ${appSocketURI}`); CONNECTION_CACHE[appSocketURI] = AppWebsocket.connect(appSocketURI) .then((client) => { console.log(`Holochain connection to ${appSocketURI} OK`); if (traceAppSignals) { client.on('signal', traceAppSignals); } return client; }); return CONNECTION_CACHE[appSocketURI]; }; const getConnection = (appSocketURI) => { if (!CONNECTION_CACHE[appSocketURI]) { throw new Error(`Connection for ${appSocketURI} not initialised! Please call openConnection() first.`); } return CONNECTION_CACHE[appSocketURI]; }; /** * Introspect an active Holochain connection's app cells to determine cell IDs * for mapping to the schema resolvers. * If no `appId` is provided or is otherwise empty or undefined, * it will try to use the `REACT_APP_HC_APP_ID` environment variable. * Only if running in a Holochain Launcher context, can both of the before-mentioned values * be left undefined or empty, and the AppWebsocket will know which appId to introspect into. */ export async function sniffHolochainAppCells(conn, appId) { // use the default set by the environment variable // and furthermore, note that both of these will be ignored // in the Holochain Launcher context // which will override any given value to the AppWebsocket // for installed_app_id appId = appId || ENV_HOLOCHAIN_APP_ID; const appInfo = await conn.appInfo({ installed_app_id: appId }); if (!appInfo) { throw new Error(`appInfo call failed for Holochain app '${appId}' - ensure the name is correct and that the app installation has succeeded`); } let dnaConfig = {}; Object.entries(appInfo.cell_info).forEach(([roleName, cellInfos]) => { // this is the "magic pattern" of having for // example the "agreement" DNA, it should have // an assigned "role_name" in the happ of // "hrea_agreement_1" or "hrea_observation_2" // and the middle section should match the expected name // for DNAIdMappings, which are also used during zome calls const hrea_cell_match = roleName.match(/hrea_(\w+)_\d+/); if (!hrea_cell_match) return; const hreaRole = hrea_cell_match[1]; const firstCell = cellInfos[0]; if (CellType.Provisioned in firstCell) { dnaConfig[hreaRole] = firstCell[CellType.Provisioned].cell_id; } }); console.info('Connecting to detected Holochain cells:', dnaConfig); return { dnaConfig, appId, }; } //---------------------------------------------------------------------------------------------------------------------- // Holochain / GraphQL type translation layer //---------------------------------------------------------------------------------------------------------------------- // @see https://crates.io/crates/holo_hash const HOLOCHAIN_IDENTIFIER_LEN = 39; // @see holo_hash::hash_type::primitive const HOLOHASH_PREFIX_DNA = [0x84, 0x2d, 0x24]; // uhC0k const HOLOHASH_PREFIX_ENTRY = [0x84, 0x21, 0x24]; // uhCEk const HOLOHASH_PREFIX_HEADER = [0x84, 0x29, 0x24]; // uhCkk const HOLOHASH_PREFIX_AGENT = [0x84, 0x20, 0x24]; // uhCAk const serializedHashMatchRegex = /^[A-Za-z0-9_+\-/]{53}={0,2}$/; const idMatchRegex = /^[A-Za-z0-9_+\-/]{53}={0,2}:[A-Za-z0-9_+\-/]{53}={0,2}$/; // something like // $:uhC0k1mcUqQIbtT0mkdTldhBaAvR6KlKxIV2IYwJemHt-NO92uXG5 // or kg:uhC0k1mcUqQIbtT0mkdTldhBaAvR6KlKxIV2IYwJemHt-NO92uXG5 // but not 9:uhC0k1mcUqQIbtT0mkdTldhBaAvR6KlKxIV2IYwJemHt-NO92uXG5 (i.e. no digits in the id) const stringIdRegex = /^\D+?:[A-Za-z0-9_+\-/]{53}={0,2}$/; // @see https://github.com/holochain-open-dev/core-types/blob/main/src/utils.ts export function deserializeHash(hash) { return Base64.toUint8Array(hash.slice(1)); } export function deserializeId(field) { const matches = field.split(':'); return [ Buffer.from(deserializeHash(matches[1])), Buffer.from(deserializeHash(matches[0])), ]; } function deserializeStringId(field) { const matches = field.split(':'); return [ Buffer.from(deserializeHash(matches[1])), matches[0], ]; } // @see https://github.com/holochain-open-dev/core-types/blob/main/src/utils.ts export function serializeHash(hash) { return `u${Base64.fromUint8Array(hash, true)}`; } function serializeId(id) { return `${serializeHash(id[1])}:${serializeHash(id[0])}`; } function seralizeStringId(id) { return `${id[1]}:${serializeHash(id[0])}`; } // Construct appropriate IDs for records in associated DNAs by substituting // the CellId portion of the ID with that of an appropriate destination record export function remapCellId(originalId, newCellId) { const [origId, _origCell] = originalId.split(':'); return `${origId}:${newCellId.split(':')[1]}`; } const LONG_DATETIME_FORMAT = 'YYYY-MM-DDTHH:mm:ss.SSSZ'; const SHORT_DATETIME_FORMAT = 'YYYY-MM-DDTHH:mm:ssZ'; const isoDateRegex = /^\d{4}-\d\d-\d\d(T\d\d:\d\d:\d\d(\.\d\d\d)?)?([+-]\d\d:\d\d)?$/; /** * Decode raw data input coming from Holochain API websocket. * * Mutates in place- we have no need for the non-normalised primitive format and this saves memory. */ const decodeFields = (result) => { deepForEach(result, (value, prop, subject) => { // ActionHash or AgentPubKey if ((value instanceof Buffer || value instanceof Uint8Array) && value.length === HOLOCHAIN_IDENTIFIER_LEN && (checkLeadingBytes(value, HOLOHASH_PREFIX_HEADER) || checkLeadingBytes(value, HOLOHASH_PREFIX_AGENT))) { subject[prop] = serializeHash(value); } // RecordId | StringId (Agent, for now) if (Array.isArray(value) && value.length == 2 && (value[0] instanceof Buffer || value[0] instanceof Uint8Array) && value[0].length === HOLOCHAIN_IDENTIFIER_LEN && checkLeadingBytes(value[0], HOLOHASH_PREFIX_DNA)) { // Match 2-element arrays of Buffer objects as IDs. // Since we check the hash prefixes, this should make it safe to mix with fields which reference arrays of plain EntryHash / ActionHash data. if ((value[1] instanceof Buffer || value[1] instanceof Uint8Array) && value[1].length === HOLOCHAIN_IDENTIFIER_LEN && (checkLeadingBytes(value[1], HOLOHASH_PREFIX_ENTRY) || checkLeadingBytes(value[1], HOLOHASH_PREFIX_HEADER) || checkLeadingBytes(value[1], HOLOHASH_PREFIX_AGENT))) { subject[prop] = serializeId(value); // Match 2-element pairs of Buffer/String as a "DNA-scoped identifier" (eg. UnitId) // :TODO: This one probably isn't safe for regular ID field mixing. // Custom serde de/serializer would make bind this handling to the appropriate fields without duck-typing issues. } else { subject[prop] = seralizeStringId(value); } } // recursively check for Date strings and convert to JS date objects upon receiving if (value && value.match && value.match(isoDateRegex)) { subject[prop] = parse(value, LONG_DATETIME_FORMAT); if (subject[prop] === null) { subject[prop] = parse(value, SHORT_DATETIME_FORMAT); } } }); }; function checkLeadingBytes(ofVar, against) { return ofVar[0] === against[0] && ofVar[1] === against[1] && ofVar[2] === against[2]; } /** * Encode application runtime data into serialisable format for transmitting to API websocket. * * Clones data in order to keep input data pristine. */ const encodeFields = (args) => { if (!args) return args; let res = args; // encode dates as ISO8601 DateTime strings if (args instanceof Date) { return format(args, LONG_DATETIME_FORMAT); } // deserialise any identifiers back to their binary format else if (args.match && args.match(serializedHashMatchRegex)) { return deserializeHash(args); } else if (args.match && args.match(idMatchRegex)) { return deserializeId(args); } else if (args.match && args.match(stringIdRegex)) { return deserializeStringId(args); } // recurse into child fields else if (Array.isArray(args)) { res = []; args.forEach((value, key) => { res[key] = encodeFields(value); }); } else if (isObject(args)) { res = {}; for (const key in args) { res[key] = encodeFields(args[key]); } } return res; }; /** * Higher-order function to generate async functions for calling zome RPC methods */ const zomeFunction = (socketURI, cell_id, zome_name, fn_name, skipEncodeDecode) => async (args) => { const { callZome } = await getConnection(socketURI); const res = await callZome({ cell_id, zome_name, fn_name, provenance: cell_id[1], payload: skipEncodeDecode ? args : encodeFields(args), }, 60000); if (!skipEncodeDecode) decodeFields(res); return res; }; /** * External API for accessing zome methods, passing them through an optional intermediary DNA ID mapping * * @param mappings DNAIdMappings to use for this collaboration space. * `instance` must be present in the mapping, and the mapped CellId will be used instead of `instance` itself. * @param socketURI If provided, connects to the Holochain conductor on a different URI. * * @return bound async zome function which can be called directly */ export const mapZomeFn = (mappings, socketURI, instance, zome, fn, skipEncodeDecode) => zomeFunction(socketURI, (mappings && mappings[instance]), zome, fn, skipEncodeDecode); export const extractEdges = (withEdges) => { if (!withEdges.edges || !withEdges.edges.length) { return []; } return withEdges.edges.map(({ node }) => node); }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29ubmVjdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL2Nvbm5lY3Rpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFFSCxPQUFPLEVBQWUsWUFBWSxFQUFFLGNBQWMsRUFBVSxRQUFRLEVBQVksTUFBTSxtQkFBbUIsQ0FBQTtBQUN6RyxPQUFPLFdBQVcsTUFBTSxlQUFlLENBQUE7QUFDdkMsT0FBTyxRQUFRLE1BQU0sV0FBVyxDQUFBO0FBQ2hDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxRQUFRLENBQUE7QUFDL0IsT0FBTyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsTUFBTSxPQUFPLENBQUE7QUFFckMsT0FBTyxHQUFHLE1BQU0sV0FBVyxDQUFBO0FBRTNCLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxHQUFHLENBQUE7QUFJdEIsd0hBQXdIO0FBQ3hILG9FQUFvRTtBQUNwRSx3SEFBd0g7QUFFeEgsb0VBQW9FO0FBQ3BFLG9GQUFvRjtBQUNwRixJQUFJLGtCQUFrQixHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMscUJBQStCLElBQUksRUFBRSxDQUFBO0FBQzFFLElBQUksd0JBQXdCLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQywyQkFBcUMsSUFBSSxFQUFFLENBQUE7QUFDdEYsSUFBSSxvQkFBb0IsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLG1CQUE2QixJQUFJLEVBQUUsQ0FBQTtBQUUxRSxNQUFNLGdCQUFnQixHQUEyQyxFQUFFLENBQUE7QUFFbkU7Ozs7O0dBS0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLFdBQVcsQ0FBQyxZQUFxQixFQUFFLGlCQUEwQixFQUFFLEtBQWMsRUFBRSxlQUE2QjtJQUNoSSxZQUFZLEdBQUcsWUFBWSxJQUFJLGtCQUFrQixDQUFBO0lBQ2pELGlCQUFpQixHQUFHLGlCQUFpQixJQUFJLHdCQUF3QixDQUFBO0lBRWpFLE1BQU0sSUFBSSxH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LEVBQUUsUUFBZ0IsRUFBRSxJQUFZLEVBQUUsRUFBVSxFQUFFLGdCQUEwQixFQUFFLEVBQUUsQ0FDckssWUFBWSxDQUF3QixTQUFTLEVBQUUsQ0FBQyxRQUFRLElBQUksUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxnQkFBZ0IsQ0FBQyxDQUFBO0FBRzlHLE1BQU0sQ0FBQyxNQUFNLFlBQVksR0FBRyxDQUFJLFNBQW1DLEVBQU8sRUFBRTtJQUMxRSxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFO1FBQy9DLE9BQU8sRUFBRSxDQUFBO0tBQ1Y7SUFDRCxPQUFPLFNBQVMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUE7QUFDaEQsQ0FBQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb25uZWN0aW9uIHdyYXBwZXIgZm9yIEhvbG9jaGFpbiBETkEgbWV0aG9kIGNhbGxzXG4gKlxuICogOlRPRE86IDpXQVJOSU5HOlxuICpcbiAqIFRoaXMgbGF5ZXIgaXMgY3VycmVudGx5IHVuc3VpdGFibGUgZm9yIG1peGluZyB3aXRoIEROQXMgdGhhdCB1c2UgZG5hLWxvY2FsIGlkZW50aWZpZXIgZm9ybWF0cywgYW5kXG4gKiB3aWxsIGNhdXNlIGVuY29kaW5nIGVycm9ycyBpZiAyLWVsZW1lbnQgbGlzdHMgb2YgaWRlbnRpZmllcnMgYXJlIHBhc3NlZC5cbiAqXG4gKiBTdWNoIHR1cGxlcyBhcmUgaW50ZXJwcmV0ZWQgYXMgW2BEbmFIYXNoYCwgYEFueURodEhhc2hgXSBwYWlycyBieSB0aGUgR3JhcGhRTCA8LT4gSG9sb2NoYWluXG4gKiBzZXJpYWxpc2F0aW9uIGxheWVyIGFuZCB0cmFuc2Zvcm1lZCBpbnRvIGNvbXBvdW5kIElEcyBhdCBJL08gdGltZS4gU28sIHRoaXMgYWRhcHRlciBzaG91bGRcbiAqICpvbmx5KiBiZSB1c2VkIHRvIHdyYXAgRE5BcyBleHBsaWNpdGx5IGRldmVsb3BlZCB3aXRoIG11bHRpLUROQSByZWZlcmVuY2VzIGluIG1pbmQuXG4gKlxuICogQWxzbyA6VE9ETzogLSBzdGFuZGFyZGlzZSBhIGJpbmFyeSBmb3JtYXQgZm9yIHVuaXZlcnNhbGx5IHVuaXF1ZSBIb2xvY2hhaW4gZW50cnkvaGVhZGVyIGlkZW50aWZpZXJzLlxuICpcbiAqIEBwYWNrYWdlOiBoUkVBXG4gKiBAc2luY2U6ICAgMjAxOS0wNS0yMFxuICovXG5cbmltcG9ydCB7IEFwcFNpZ25hbENiLCBBcHBXZWJzb2NrZXQsIEFkbWluV2Vic29ja2V0LCBDZWxsSWQsIENlbGxUeXBlLCBIb2xvSGFzaCB9IGZyb20gJ0Bob2xvY2hhaW4vY2xpZW50J1xuaW1wb3J0IGRlZXBGb3JFYWNoIGZyb20gJ2RlZXAtZm9yLWVhY2gnXG5pbXBvcnQgaXNPYmplY3QgZnJvbSAnaXMtb2JqZWN0J1xuaW1wb3J0IHsgQnVmZmVyIH0gZnJvbSAnYnVmZmVyJ1xuaW1wb3J0IHsgZm9ybWF0LCBwYXJzZSB9IGZyb20gJ2ZlY2hhJ1xuaW1wb3J0IHsgRE5BSWRNYXBwaW5ncyB9IGZyb20gJy4vdHlwZXMnXG5pbXBvcnQgYjY0IGZyb20gXCJqcy1iYXNlNjRcIlxuXG5jb25zdCB7IEJhc2U2NCB9ID0gYjY0XG5cbnR5cGUgUmVjb3JkSWQgPSBbSG9sb0hhc2gsIEhvbG9IYXNoXVxuXG4vLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIENvbm5lY3Rpb24gcGVyc2lzdGVuY2UgYW5kIG11bHRpLWNvbmR1Y3RvciAvIG11bHRpLWFnZW50IGhhbmRsaW5nXG4vLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLy8gOk5PVEU6IHdoZW4gY2FsbGluZyBBcHBXZWJzb2NrZXQuY29ubmVjdCBmb3IgdGhlIExhdW5jaGVyIENvbnRleHRcbi8vIGl0IGp1c3QgZXhwZWN0cyBhbiBlbXB0eSBzdHJpbmcgZm9yIHRoZSBzb2NrZXRVUkkuIE90aGVyIGVudmlyb25tZW50cyByZXF1aXJlIGl0LlxubGV0IEVOVl9DT05ORUNUSU9OX1VSSSA9IHByb2Nlc3MuZW52LlJFQUNUX0FQUF9IQ19DT05OX1VSTCBhcyBzdHJpbmcgfHwgJydcbmxldCBFTlZfQURNSU5fQ09OTkVDVElPTl9VUkkgPSBwcm9jZXNzLmVudi5SRUFDVF9BUFBfSENfQURNSU5fQ09OTl9VUkwgYXMgc3RyaW5nIHx8ICcnXG5sZXQgRU5WX0hPTE9DSEFJTl9BUFBfSUQgPSBwcm9jZXNzLmVudi5SRUFDVF9BUFBfSENfQVBQX0lEIGFzIHN0cmluZyB8fCAnJ1xuXG5jb25zdCBDT05ORUNUSU9OX0NBQ0hFOiB7IFtpOiBzdHJpbmddOiBQcm9taXNlPEFwcFdlYnNvY2tldD4gfSA9IHt9XG5cbi8qKlxuICogSWYgbm8gYGNvbmR1Y3RvclVyaWAgaXMgcHJvdmlkZWQgb3IgaXMgb3RoZXJ3aXNlIGVtcHR5IG9yIHVuZGVmaW5lZCxcbiAqIGEgY29ubmVjdGlvbiBpcyBhdHRlbXB0ZWQgdmlhIHRoZSBgUkVBQ1RfQVBQX0hDX0NPTk5fVVJMYCBlbnZpcm9ubWVudCB2YXJpYWJsZS5cbiAqIE9ubHkgaWYgcnVubmluZyBpbiBhIEhvbG9jaGFpbiBMYXVuY2hlciBjb250ZXh0LCBjYW4gYm90aCBvZiB0aGUgYmVmb3JlLW1lbnRpb25lZCB2YWx1ZXNcbiAqIGJlIGxlZnQgdW5kZWZpbmVkIG9yIGVtcHR5LCBhbmQgdGhlIHdlYnNvY2tldCBjb25uZWN0aW9uIGNhbiBzdGlsbCBiZSBlc3RhYmxpc2hlZC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGF1dG9Db25uZWN0KGNvbmR1Y3RvclVyaT86IHN0cmluZywgYWRtaW5Db25kdWN0b3JVcmk/OiBzdHJpbmcsIGFwcElEPzogc3RyaW5nLCB0cmFjZUFwcFNpZ25hbHM/OiBBcHBTaWduYWxDYikge1xuICBjb25kdWN0b3JVcmkgPSBjb25kdWN0b3JVcmkgfHwgRU5WX0NPTk5FQ1RJT05fVVJJXG4gIGFkbWluQ29uZHVjdG9yVXJpID0gYWRtaW5Db25kdWN0b3JVcmkgfHwgRU5WX0FETUlOX0NPTk5FQ1RJT05fVVJJXG5cbiAgY29uc3QgY29ubiA9IGF3YWl0IG9wZW5Db25uZWN0aW9uKGNvbmR1Y3RvclVyaSwgdHJhY2VBcHBTaWduYWxzKVxuICBjb25zdCB7XG4gICAgZG5hQ29uZmlnLFxuICAgIGFwcElkOiByZWFsQXBwSWQsXG4gICB9ID0gYXdhaXQgc25pZmZIb2xvY2hhaW5BcHBDZWxscyhjb25uLCBhcHBJRClcbiAgbGV0IGFkbWluQ29ubjogQWRtaW5XZWJzb2NrZXQgfCBudWxsID0gbnVsbFxuICBpZiAoYWRtaW5Db25kdWN0b3JVcmkpIHtcbiAgICBhZG1pbkNvbm4gPSBhd2FpdCBBZG1pbldlYnNvY2tldC5jb25uZWN0KGFkbWluQ29uZHVjdG9yVXJpKVxuICAgIGZvciBhd2FpdCAobGV0IGNlbGxJZCBvZiBPYmplY3QudmFsdWVzKGRuYUNvbmZpZykpIHtcbiAgICAgIGF3YWl0IGFkbWluQ29ubi5hdXRob3JpemVTaWduaW5nQ3JlZGVudGlhbHMoY2VsbElkKVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiB7XG4gICAgY29ubixcbiAgICBhZG1pbkNvbm4sXG4gICAgZG5hQ29uZmlnLFxuICAgIGNvbmR1Y3RvclVyaSxcbiAgICBhZG1pbkNvbmR1Y3RvclVyaSxcbiAgICBhcHBJZDogcmVhbEFwcElkXG4gIH1cbn1cblxuLyoqXG4gKiBJbml0cyBhIGNvbm5lY3Rpb24gZm9yIHRoZSBnaXZlbiB3ZWJzb2NrZXQgVVJJLlxuICpcbiAqIFRoaXMgbWV0aG9kIGdpdmVzIGNhbGxpbmcgY29kZSBhbiBvcHBvcnR1bml0eSB0byByZWdpc3RlciBnbG9iYWxzIGZvciBhbGwgZnV0dXJlXG4gKiBpbnN0YW5jZXMgb2YgYSBjb25uZWN0aW9uIG9mIHRoZSBzYW1lIGBzb2NrZXRVUklgLiBUbyBlbnN1cmUgdGhpcyBpcyBkb25lIHJlbGlhYmx5LFxuICogYSBydW50aW1lIGVycm9yIHdpbGwgYmUgdGhyb3duIGJ5IGBnZXRDb25uZWN0aW9uYCBpZiBubyBgb3BlbkNvbm5lY3Rpb25gIGhhc1xuICogYmVlbiBwcmV2aW91c2x5IHBlcmZvcm1lZCBmb3IgdGhlIHNhbWUgYHNvY2tldFVSSWAuXG4gKi9cbmV4cG9ydCBjb25zdCBvcGVuQ29ubmVjdGlvbiA9IChhcHBTb2NrZXRVUkk6IHN0cmluZywgdHJhY2VBcHBTaWduYWxzPzogQXBwU2lnbmFsQ2IpID0+IHtcbiAgY29uc29sZS5sb2coYEluaXQgSG9sb2NoYWluIGNvbm5lY3Rpb246ICR7YXBwU29ja2V0VVJJfWApXG5cbiAgQ09OTkVDVElPTl9DQUNIRVthcHBTb2NrZXRVUkldID0gQXBwV2Vic29ja2V0LmNvbm5lY3QoYXBwU29ja2V0VVJJKVxuICAgIC50aGVuKChjbGllbnQpID0+IHtcbiAgICAgICAgY29uc29sZS5sb2coYEhvbG9jaGFpbiBjb25uZWN0aW9uIHRvICR7YXBwU29ja2V0VVJJfSBPS2ApXG4gICAgICAgIGlmICh0cmFjZUFwcFNpZ25hbHMpIHtcbiAgICAgICAgICBjbGllbnQub24oJ3NpZ25hbCcsIHRyYWNlQXBwU2lnbmFscylcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gY2xpZW50XG4gICAgICB9KVxuXG4gIHJldHVybiBDT05ORUNUSU9OX0NBQ0hFW2FwcFNvY2tldFVSSV1cbn1cblxuY29uc3QgZ2V0Q29ubmVjdGlvbiA9IChhcHBTb2NrZXRVUkk6IHN0cmluZykgPT4ge1xuICBpZiAoIUNPTk5FQ1RJT05fQ0FDSEVbYXBwU29ja2V0VVJJXSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgQ29ubmVjdGlvbiBmb3IgJHthcHBTb2NrZXRVUkl9IG5vdCBpbml0aWFsaXNlZCEgUGxlYXNlIGNhbGwgb3BlbkNvbm5lY3Rpb24oKSBmaXJzdC5gKVxuICB9XG5cbiAgcmV0dXJuIENPTk5FQ1RJT05fQ0FDSEVbYXBwU29ja2V0VVJJXVxufVxuXG4vKipcbiAqIEludHJvc3BlY3QgYW4gYWN0aXZlIEhvbG9jaGFpbiBjb25uZWN0aW9uJ3MgYXBwIGNlbGxzIHRvIGRldGVybWluZSBjZWxsIElEc1xuICogZm9yIG1hcHBpbmcgdG8gdGhlIHNjaGVtYSByZXNvbHZlcnMuXG4gKiBJZiBubyBgYXBwSWRgIGlzIHByb3ZpZGVkIG9yIGlzIG90aGVyd2lzZSBlbXB0eSBvciB1bmRlZmluZWQsXG4gKiBpdCB3aWxsIHRyeSB0byB1c2UgdGhlIGBSRUFDVF9BUFBfSENfQVBQX0lEYCBlbnZpcm9ubWVudCB2YXJpYWJsZS5cbiAqIE9ubHkgaWYgcnVubmluZyBpbiBhIEhvbG9jaGFpbiBMYXVuY2hlciBjb250ZXh0LCBjYW4gYm90aCBvZiB0aGUgYmVmb3JlLW1lbnRpb25lZCB2YWx1ZXNcbiAqIGJlIGxlZnQgdW5kZWZpbmVkIG9yIGVtcHR5LCBhbmQgdGhlIEFwcFdlYnNvY2tldCB3aWxsIGtub3cgd2hpY2ggYXBwSWQgdG8gaW50cm9zcGVjdCBpbnRvLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc25pZmZIb2xvY2hhaW5BcHBDZWxscyhjb25uOiBBcHBXZWJzb2NrZXQsIGFwcElkPzogc3RyaW5nKSB7XG4gIC8vIHVzZSB0aGUgZGVmYXVsdCBzZXQgYnkgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlXG4gIC8vIGFuZCBmdXJ0aGVybW9yZSwgbm90ZSB0aGF0IGJvdGggb2YgdGhlc2Ugd2lsbCBiZSBpZ25vcmVkXG4gIC8vIGluIHRoZSBIb2xvY2hhaW4gTGF1bmNoZXIgY29udGV4dFxuICAvLyB3aGljaCB3aWxsIG92ZXJyaWRlIGFueSBnaXZlbiB2YWx1ZSB0byB0aGUgQXBwV2Vic29ja2V0XG4gIC8vIGZvciBpbnN0YWxsZWRfYXBwX2lkXG4gIGFwcElkID0gYXBwSWQgfHwgRU5WX0hPTE9DSEFJTl9BUFBfSURcbiAgY29uc3QgYXBwSW5mbyA9IGF3YWl0IGNvbm4uYXBwSW5mbyh7IGluc3RhbGxlZF9hcHBfaWQ6IGFwcElkIH0pXG4gIGlmICghYXBwSW5mbykge1xuICAgIHRocm93IG5ldyBFcnJvcihgYXBwSW5mbyBjYWxsIGZhaWxlZCBmb3IgSG9sb2NoYWluIGFwcCAnJHthcHBJZH0nIC0gZW5zdXJlIHRoZSBuYW1lIGlzIGNvcnJlY3QgYW5kIHRoYXQgdGhlIGFwcCBpbnN0YWxsYXRpb24gaGFzIHN1Y2NlZWRlZGApXG4gIH1cblxuICBsZXQgZG5hQ29uZmlnOiBETkFJZE1hcHBpbmdzID0ge31cbiAgT2JqZWN0LmVudHJpZXMoYXBwSW5mby5jZWxsX2luZm8pLmZvckVhY2goKFtyb2xlTmFtZSwgY2VsbEluZm9zXSkgPT4ge1xuICAgIC8vIHRoaXMgaXMgdGhlIFwibWFnaWMgcGF0dGVyblwiIG9mIGhhdmluZyBmb3JcbiAgICAvLyBleGFtcGxlIHRoZSBcImFncmVlbWVudFwiIEROQSwgaXQgc2hvdWxkIGhhdmVcbiAgICAvLyBhbiBhc3NpZ25lZCBcInJvbGVfbmFtZVwiIGluIHRoZSBoYXBwIG9mXG4gICAgLy8gXCJocmVhX2FncmVlbWVudF8xXCIgb3IgXCJocmVhX29ic2VydmF0aW9uXzJcIlxuICAgIC8vIGFuZCB0aGUgbWlkZGxlIHNlY3Rpb24gc2hvdWxkIG1hdGNoIHRoZSBleHBlY3RlZCBuYW1lXG4gICAgLy8gZm9yIEROQUlkTWFwcGluZ3MsIHdoaWNoIGFyZSBhbHNvIHVzZWQgZHVyaW5nIHpvbWUgY2FsbHNcbiAgICBjb25zdCBocmVhX2NlbGxfbWF0Y2ggPSByb2xlTmFtZS5tYXRjaCgvaHJlYV8oXFx3KylfXFxkKy8pXG4gICAgaWYgKCFocmVhX2NlbGxfbWF0Y2gpIHJldHVyblxuICAgIGNvbnN0IGhyZWFSb2xlID0gaHJlYV9jZWxsX21hdGNoWzFdIGFzIGtleW9mIEROQUlkTWFwcGluZ3NcbiAgICBjb25zdCBmaXJzdENlbGwgPSBjZWxsSW5mb3NbMF1cbiAgICBpZiAoQ2VsbFR5cGUuUHJvdmlzaW9uZWQgaW4gZmlyc3RDZWxsKSB7XG4gICAgICBkbmFDb25maWdbaHJlYVJvbGVdID0gZmlyc3RDZWxsW0NlbGxUeXBlLlByb3Zpc2lvbmVkXS5jZWxsX2lkXG4gICAgfVxuICB9KVxuXG4gIGNvbnNvbGUuaW5mbygnQ29ubmVjdGluZyB0byBkZXRlY3RlZCBIb2xvY2hhaW4gY2VsbHM6JywgZG5hQ29uZmlnKVxuXG4gIHJldHVybiB7XG4gICAgZG5hQ29uZmlnLFxuICAgIGFwcElkLFxuICB9XG59XG5cblxuLy8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4vLyBIb2xvY2hhaW4gLyBHcmFwaFFMIHR5cGUgdHJhbnNsYXRpb24gbGF5ZXJcbi8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4vLyBAc2VlIGh0dHBzOi8vY3JhdGVzLmlvL2NyYXRlcy9ob2xvX2hhc2hcbmNvbnN0IEhPTE9DSEFJTl9JREVOVElGSUVSX0xFTiA9IDM5XG4vLyBAc2VlIGhvbG9faGFzaDo6aGFzaF90eXBlOjpwcmltaXRpdmVcbmNvbnN0IEhPTE9IQVNIX1BSRUZJWF9ETkEgPSBbMHg4NCwgMHgyZCwgMHgyNF0gICAgIC8vIHVoQzBrXG5jb25zdCBIT0xPSEFTSF9QUkVGSVhfRU5UUlkgPSBbMHg4NCwgMHgyMSwgMHgyNF0gICAvLyB1aENFa1xuY29uc3QgSE9MT0hBU0hfUFJFRklYX0hFQURFUiA9IFsweDg0LCAweDI5LCAweDI0XSAgLy8gdWhDa2tcbmNvbnN0IEhPTE9IQVNIX1BSRUZJWF9BR0VOVCA9IFsweDg0LCAweDIwLCAweDI0XSAgIC8vIHVoQ0FrXG5cbmNvbnN0IHNlcmlhbGl6ZWRIYXNoTWF0Y2hSZWdleCA9IC9eW0EtWmEtejAtOV8rXFwtL117NTN9PXswLDJ9JC9cbmNvbnN0IGlkTWF0Y2hSZWdleCA9IC9eW0EtWmEtejAtOV8rXFwtL117NTN9PXswLDJ9OltBLVphLXowLTlfK1xcLS9dezUzfT17MCwyfSQvXG4vLyBzb21ldGhpbmcgbGlrZVxuLy8gJDp1aEMwazFtY1VxUUlidFQwbWtkVGxkaEJhQXZSNktsS3hJVjJJWXdKZW1IdC1OTzkydVhHNVxuLy8gb3Iga2c6dWhDMGsxbWNVcVFJYnRUMG1rZFRsZGhCYUF2UjZLbEt4SVYySVl3SmVtSHQtTk85MnVYRzVcbi8vIGJ1dCBub3QgOTp1aEMwazFtY1VxUUlidFQwbWtkVGxkaEJhQXZSNktsS3hJVjJJWXdKZW1IdC1OTzkydVhHNSAoaS5lLiBubyBkaWdpdHMgaW4gdGhlIGlkKVxuY29uc3Qgc3RyaW5nSWRSZWdleCA9IC9eXFxEKz86W0EtWmEtejAtOV8rXFwtL117NTN9PXswLDJ9JC9cblxuLy8gQHNlZSBodHRwczovL2dpdGh1Yi5jb20vaG9sb2NoYWluLW9wZW4tZGV2L2NvcmUtdHlwZXMvYmxvYi9tYWluL3NyYy91dGlscy50c1xuZXhwb3J0IGZ1bmN0aW9uIGRlc2VyaWFsaXplSGFzaChoYXNoOiBzdHJpbmcpOiBVaW50OEFycmF5IHtcbiAgcmV0dXJuIEJhc2U2NC50b1VpbnQ4QXJyYXkoaGFzaC5zbGljZSgxKSlcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRlc2VyaWFsaXplSWQoZmllbGQ6IHN0cmluZyk6IFJlY29yZElkIHtcbiAgY29uc3QgbWF0Y2hlcyA9IGZpZWxkLnNwbGl0KCc6JylcbiAgcmV0dXJuIFtcbiAgICBCdWZmZXIuZnJvbShkZXNlcmlhbGl6ZUhhc2gobWF0Y2hlc1sxXSkpLFxuICAgIEJ1ZmZlci5mcm9tKGRlc2VyaWFsaXplSGFzaChtYXRjaGVzWzBdKSksXG4gIF1cbn1cblxuZnVuY3Rpb24gZGVzZXJpYWxpemVTdHJpbmdJZChmaWVsZDogc3RyaW5nKTogW0J1ZmZlcixzdHJpbmddIHtcbiAgY29uc3QgbWF0Y2hlcyA9IGZpZWxkLnNwbGl0KCc6JylcbiAgcmV0dXJuIFtcbiAgICBCdWZmZXIuZnJvbShkZXNlcmlhbGl6ZUhhc2gobWF0Y2hlc1sxXSkpLFxuICAgIG1hdGNoZXNbMF0sXG4gIF1cbn1cblxuLy8gQHNlZSBodHRwczovL2dpdGh1Yi5jb20vaG9sb2NoYWluLW9wZW4tZGV2L2NvcmUtdHlwZXMvYmxvYi9tYWluL3NyYy91dGlscy50c1xuZXhwb3J0IGZ1bmN0aW9uIHNlcmlhbGl6ZUhhc2goaGFzaDogVWludDhBcnJheSk6IHN0cmluZyB7XG4gIHJldHVybiBgdSR7QmFzZTY0LmZyb21VaW50OEFycmF5KGhhc2gsIHRydWUpfWBcbn1cblxuZnVuY3Rpb24gc2VyaWFsaXplSWQoaWQ6IFJlY29yZElkKTogc3RyaW5nIHtcbiAgcmV0dXJuIGAke3NlcmlhbGl6ZUhhc2goaWRbMV0pfToke3NlcmlhbGl6ZUhhc2goaWRbMF0pfWBcbn1cblxuZnVuY3Rpb24gc2VyYWxpemVTdHJpbmdJZChpZDogW0J1ZmZlcixzdHJpbmddKTogc3RyaW5nIHtcbiAgcmV0dXJuIGAke2lkWzFdfToke3NlcmlhbGl6ZUhhc2goaWRbMF0pfWBcbn1cblxuLy8gQ29uc3RydWN0IGFwcHJvcHJpYXRlIElEcyBmb3IgcmVjb3JkcyBpbiBhc3NvY2lhdGVkIEROQXMgYnkgc3Vic3RpdHV0aW5nXG4vLyB0aGUgQ2VsbElkIHBvcnRpb24gb2YgdGhlIElEIHdpdGggdGhhdCBvZiBhbiBhcHByb3ByaWF0ZSBkZXN0aW5hdGlvbiByZWNvcmRcbmV4cG9ydCBmdW5jdGlvbiByZW1hcENlbGxJZChvcmlnaW5hbElkLCBuZXdDZWxsSWQpIHtcbiAgY29uc3QgW29yaWdJZCwgX29yaWdDZWxsXSA9IG9yaWdpbmFsSWQuc3BsaXQoJzonKVxuICByZXR1cm4gYCR7b3JpZ0lkfToke25ld0NlbGxJZC5zcGxpdCgnOicpWzFdfWBcbn1cblxuY29uc3QgTE9OR19EQVRFVElNRV9GT1JNQVQgPSAnWVlZWS1NTS1ERFRISDptbTpzcy5TU1NaJ1xuY29uc3QgU0hPUlRfREFURVRJTUVfRk9STUFUID0gJ1lZWVktTU0tRERUSEg6bW06c3NaJ1xuY29uc3QgaXNvRGF0ZVJlZ2V4ID0gL15cXGR7NH0tXFxkXFxkLVxcZFxcZChUXFxkXFxkOlxcZFxcZDpcXGRcXGQoXFwuXFxkXFxkXFxkKT8pPyhbKy1dXFxkXFxkOlxcZFxcZCk/JC9cblxuLyoqXG4gKiBEZWNvZGUgcmF3IGRhdGEgaW5wdXQgY29taW5nIGZyb20gSG9sb2NoYWluIEFQSSB3ZWJzb2NrZXQuXG4gKlxuICogTXV0YXRlcyBpbiBwbGFjZS0gd2UgaGF2ZSBubyBuZWVkIGZvciB0aGUgbm9uLW5vcm1hbGlzZWQgcHJpbWl0aXZlIGZvcm1hdCBhbmQgdGhpcyBzYXZlcyBtZW1vcnkuXG4gKi9cbmNvbnN0IGRlY29kZUZpZWxkcyA9IChyZXN1bHQ6IGFueSk6IHZvaWQgPT4ge1xuICBkZWVwRm9yRWFjaChyZXN1bHQsICh2YWx1ZSwgcHJvcCwgc3ViamVjdCkgPT4ge1xuXG4gICAgLy8gQWN0aW9uSGFzaCBvciBBZ2VudFB1YktleVxuICAgIGlmICgodmFsdWUgaW5zdGFuY2VvZiBCdWZmZXIgfHwgdmFsdWUgaW5zdGFuY2VvZiBVaW50OEFycmF5KSAmJiB2YWx1ZS5sZW5ndGggPT09IEhPTE9DSEFJTl9JREVOVElGSUVSX0xFTiAmJlxuICAgICAgKGNoZWNrTGVhZGluZ0J5dGVzKHZhbHVlLCBIT0xPSEFTSF9QUkVGSVhfSEVBREVSKSB8fCBjaGVja0xlYWRpbmdCeXRlcyh2YWx1ZSwgSE9MT0hBU0hfUFJFRklYX0FHRU5UKSkpIHtcbiAgICAgIHN1YmplY3RbcHJvcF0gPSBzZXJpYWxpemVIYXNoKHZhbHVlIGFzIHVua25vd24gYXMgVWludDhBcnJheSlcbiAgICB9XG5cbiAgICAvLyBSZWNvcmRJZCB8IFN0cmluZ0lkIChBZ2VudCwgZm9yIG5vdylcbiAgICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkgJiYgdmFsdWUubGVuZ3RoID09IDIgJiZcbiAgICAodmFsdWVbMF0gaW5zdGFuY2VvZiBCdWZmZXIgfHwgdmFsdWVbMF0gaW5zdGFuY2VvZiBVaW50OEFycmF5KSAmJlxuICAgIHZhbHVlWzBdLmxlbmd0aCA9PT0gSE9MT0NIQUlOX0lERU5USUZJRVJfTEVOICYmXG4gICAgY2hlY2tMZWFkaW5nQnl0ZXModmFsdWVbMF0sIEhPTE9IQVNIX1BSRUZJWF9ETkEpKVxuICAgIHtcbiAgICAgIC8vIE1hdGNoIDItZWxlbWVudCBhcnJheXMgb2YgQnVmZmVyIG9iamVjdHMgYXMgSURzLlxuICAgICAgLy8gU2luY2Ugd2UgY2hlY2sgdGhlIGhhc2ggcHJlZml4ZXMsIHRoaXMgc2hvdWxkIG1ha2UgaXQgc2FmZSB0byBtaXggd2l0aCBmaWVsZHMgd2hpY2ggcmVmZXJlbmNlIGFycmF5cyBvZiBwbGFpbiBFbnRyeUhhc2ggLyBBY3Rpb25IYXNoIGRhdGEuXG4gICAgICBpZiAoKHZhbHVlWzFdIGluc3RhbmNlb2YgQnVmZmVyIHx8IHZhbHVlWzFdIGluc3RhbmNlb2YgVWludDhBcnJheSkgJiYgdmFsdWVbMV0ubGVuZ3RoID09PSBIT0xPQ0hBSU5fSURFTlRJRklFUl9MRU4gJiZcbiAgICAgICAgKGNoZWNrTGVhZGluZ0J5dGVzKHZhbHVlWzFdLCBIT0xPSEFTSF9QUkVGSVhfRU5UUlkpIHx8IGNoZWNrTGVhZGluZ0J5dGVzKHZhbHVlWzFdLCBIT0xPSEFTSF9QUkVGSVhfSEVBREVSKSB8fCBjaGVja0xlYWRpbmdCeXRlcyh2YWx1ZVsxXSwgSE9MT0hBU0hfUFJFRklYX0FHRU5UKSkpXG4gICAgICB7XG4gICAgICAgIHN1YmplY3RbcHJvcF0gPSBzZXJpYWxpemVJZCh2YWx1ZSBhcyBSZWNvcmRJZClcbiAgICAgIC8vIE1hdGNoIDItZWxlbWVudCBwYWlycyBvZiBCdWZmZXIvU3RyaW5nIGFzIGEgXCJETkEtc2NvcGVkIGlkZW50aWZpZXJcIiAoZWcuIFVuaXRJZClcbiAgICAgIC8vIDpUT0RPOiBUaGlzIG9uZSBwcm9iYWJseSBpc24ndCBzYWZlIGZvciByZWd1bGFyIElEIGZpZWxkIG1peGluZy5cbiAgICAgIC8vICAgICAgICBDdXN0b20gc2VyZGUgZGUvc2VyaWFsaXplciB3b3VsZCBtYWtlIGJpbmQgdGhpcyBoYW5kbGluZyB0byB0aGUgYXBwcm9wcmlhdGUgZmllbGRzIHdpdGhvdXQgZHVjay10eXBpbmcgaXNzdWVzLlxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc3ViamVjdFtwcm9wXSA9IHNlcmFsaXplU3RyaW5nSWQodmFsdWUgYXMgW0J1ZmZlciwgc3RyaW5nXSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyByZWN1cnNpdmVseSBjaGVjayBmb3IgRGF0ZSBzdHJpbmdzIGFuZCBjb252ZXJ0IHRvIEpTIGRhdGUgb2JqZWN0cyB1cG9uIHJlY2VpdmluZ1xuICAgIGlmICh2YWx1ZSAmJiB2YWx1ZS5tYXRjaCAmJiB2YWx1ZS5tYXRjaChpc29EYXRlUmVnZXgpKSB7XG4gICAgICBzdWJqZWN0W3Byb3BdID0gcGFyc2UodmFsdWUsIExPTkdfREFURVRJTUVfRk9STUFUKVxuICAgICAgaWYgKHN1YmplY3RbcHJvcF0gPT09IG51bGwpIHtcbiAgICAgICAgc3ViamVjdFtwcm9wXSA9IHBhcnNlKHZhbHVlLCBTSE9SVF9EQVRFVElNRV9GT1JNQVQpXG4gICAgICB9XG4gICAgfVxuXG4gIH0pXG59XG5cbmZ1bmN0aW9uIGNoZWNrTGVhZGluZ0J5dGVzKG9mVmFyLCBhZ2FpbnN0KSB7XG4gIHJldHVybiBvZlZhclswXSA9PT0gYWdhaW5zdFswXSAmJlxuICAgIG9mVmFyWzFdID09PSBhZ2FpbnN0WzFdICYmXG4gICAgb2ZWYXJbMl0gPT09IGFnYWluc3RbMl1cbn1cblxuLyoqXG4gKiBFbmNvZGUgYXBwbGljYXRpb24gcnVudGltZSBkYXRhIGludG8gc2VyaWFsaXNhYmxlIGZvcm1hdCBmb3IgdHJhbnNtaXR0aW5nIHRvIEFQSSB3ZWJzb2NrZXQuXG4gKlxuICogQ2xvbmVzIGRhdGEgaW4gb3JkZXIgdG8ga2VlcCBpbnB1dCBkYXRhIHByaXN0aW5lLlxuICovXG5jb25zdCBlbmNvZGVGaWVsZHMgPSAoYXJnczogYW55KTogYW55ID0+IHtcbiAgaWYgKCFhcmdzKSByZXR1cm4gYXJnc1xuICBsZXQgcmVzID0gYXJnc1xuXG4gIC8vIGVuY29kZSBkYXRlcyBhcyBJU084NjAxIERhdGVUaW1lIHN0cmluZ3NcbiAgaWYgKGFyZ3MgaW5zdGFuY2VvZiBEYXRlKSB7XG4gICAgcmV0dXJuIGZvcm1hdChhcmdzLCBMT05HX0RBVEVUSU1FX0ZPUk1BVClcbiAgfVxuXG4gIC8vIGRlc2VyaWFsaXNlIGFueSBpZGVudGlmaWVycyBiYWNrIHRvIHRoZWlyIGJpbmFyeSBmb3JtYXRcbiAgZWxzZSBpZiAoYXJncy5tYXRjaCAmJiBhcmdzLm1hdGNoKHNlcmlhbGl6ZWRIYXNoTWF0Y2hSZWdleCkpIHtcbiAgICByZXR1cm4gZGVzZXJpYWxpemVIYXNoKGFyZ3MpXG4gIH1cbiAgZWxzZSBpZiAoYXJncy5tYXRjaCAmJiBhcmdzLm1hdGNoKGlkTWF0Y2hSZWdleCkpIHtcbiAgICByZXR1cm4gZGVzZXJpYWxpemVJZChhcmdzKVxuICB9XG4gIGVsc2UgaWYgKGFyZ3MubWF0Y2ggJiYgYXJncy5tYXRjaChzdHJpbmdJZFJlZ2V4KSkge1xuICAgIHJldHVybiBkZXNlcmlhbGl6ZVN0cmluZ0lkKGFyZ3MpXG4gIH1cblxuICAvLyByZWN1cnNlIGludG8gY2hpbGQgZmllbGRzXG4gIGVsc2UgaWYgKEFycmF5LmlzQXJyYXkoYXJncykpIHtcbiAgICByZXMgPSBbXVxuICAgIGFyZ3MuZm9yRWFjaCgodmFsdWUsIGtleSkgPT4ge1xuICAgICAgcmVzW2tleV0gPSBlbmNvZGVGaWVsZHModmFsdWUpXG4gICAgfSlcbiAgfSBlbHNlIGlmIChpc09iamVjdChhcmdzKSkge1xuICAgIHJlcyA9IHt9XG4gICAgZm9yIChjb25zdCBrZXkgaW4gYXJncykge1xuICAgICAgcmVzW2tleV0gPSBlbmNvZGVGaWVsZHMoYXJnc1trZXldKVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZXNcbn1cblxuXG4vLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIEhvbG9jaGFpbiBjZWxsIEFQSSBtZXRob2QgYmluZGluZyBBUElcbi8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4vLyBleHBsaWNpdCB0eXBlLWxvc3MgYXQgdGhlIGJvdW5kYXJ5XG5leHBvcnQgdHlwZSBCb3VuZFpvbWVGbjxJbnB1dFR5cGUsIE91dHB1dFR5cGU+ID0gKGFyZ3M6IElucHV0VHlwZSkgPT4gT3V0cHV0VHlwZTtcblxuLyoqXG4gKiBIaWdoZXItb3JkZXIgZnVuY3Rpb24gdG8gZ2VuZXJhdGUgYXN5bmMgZnVuY3Rpb25zIGZvciBjYWxsaW5nIHpvbWUgUlBDIG1ldGhvZHNcbiAqL1xuY29uc3Qgem9tZUZ1bmN0aW9uID0gPElucHV0VHlwZSwgT3V0cHV0VHlwZT4oc29ja2V0VVJJOiBzdHJpbmcsIGNlbGxfaWQ6IENlbGxJZCwgem9tZV9uYW1lOiBzdHJpbmcsIGZuX25hbWU6IHN0cmluZywgc2tpcEVuY29kZURlY29kZT86IGJvb2xlYW4pOiBCb3VuZFpvbWVGbjxJbnB1dFR5cGUsIFByb21pc2U8T3V0cHV0VHlwZT4+ID0+IGFzeW5jIChhcmdzKTogUHJvbWlzZTxPdXRwdXRUeXBlPiA9PiB7XG4gIGNvbnN0IHsgY2FsbFpvbWUgfSA9IGF3YWl0IGdldENvbm5lY3Rpb24oc29ja2V0VVJJKVxuICBjb25zdCByZXMgPSBhd2FpdCBjYWxsWm9tZSh7XG4gICAgY2VsbF9pZCxcbiAgICB6b21lX25hbWUsXG4gICAgZm5fbmFtZSxcbiAgICBwcm92ZW5hbmNlOiBjZWxsX2lkWzFdLFxuICAgIHBheWxvYWQ6IHNraXBFbmNvZGVEZWNvZGUgPyBhcmdzIDogZW5jb2RlRmllbGRzKGFyZ3MpLFxuICB9LCA2MDAwMClcbiAgaWYgKCFza2lwRW5jb2RlRGVjb2RlKSBkZWNvZGVGaWVsZHMocmVzKVxuICByZXR1cm4gcmVzXG59XG5cbi8qKlxuICogRXh0ZXJuYWwgQVBJIGZvciBhY2Nlc3Npbmcgem9tZSBtZXRob2RzLCBwYXNzaW5nIHRoZW0gdGhyb3VnaCBhbiBvcHRpb25hbCBpbnRlcm1lZGlhcnkgRE5BIElEIG1hcHBpbmdcbiAqXG4gKiBAcGFyYW0gbWFwcGluZ3MgIEROQUlkTWFwcGluZ3MgdG8gdXNlIGZvciB0aGlzIGNvbGxhYm9yYXRpb24gc3BhY2UuXG4gKiAgICAgICAgICAgICAgICAgIGBpbnN0YW5jZWAgbXVzdCBiZSBwcmVzZW50IGluIHRoZSBtYXBwaW5nLCBhbmQgdGhlIG1hcHBlZCBDZWxsSWQgd2lsbCBiZSB1c2VkIGluc3RlYWQgb2YgYGluc3RhbmNlYCBpdHNlbGYuXG4gKiBAcGFyYW0gc29ja2V0VVJJIElmIHByb3ZpZGVkLCBjb25uZWN0cyB0byB0aGUgSG9sb2NoYWluIGNvbmR1Y3RvciBvbiBhIGRpZmZlcmVudCBVUkkuXG4gKlxuICogQHJldHVybiBib3VuZCBhc3luYyB6b21lIGZ1bmN0aW9uIHdoaWNoIGNhbiBiZSBjYWxsZWQgZGlyZWN0bHlcbiAqL1xuZXhwb3J0IGNvbnN0IG1hcFpvbWVGbiA9IDxJbnB1dFR5cGUsIE91dHB1dFR5cGU+KG1hcHBpbmdzOiBETkFJZE1hcHBpbmdzLCBzb2NrZXRVUkk6IHN0cmluZywgaW5zdGFuY2U6IHN0cmluZywgem9tZTogc3RyaW5nLCBmbjogc3RyaW5nLCBza2lwRW5jb2RlRGVjb2RlPzogYm9vbGVhbikgPT5cbiAgem9tZUZ1bmN0aW9uPElucHV0VHlwZSwgT3V0cHV0VHlwZT4oc29ja2V0VVJJLCAobWFwcGluZ3MgJiYgbWFwcGluZ3NbaW5zdGFuY2VdKSwgem9tZSwgZm4sIHNraXBFbmNvZGVEZWNvZGUpXG5cblxuZXhwb3J0IGNvbnN0IGV4dHJhY3RFZGdlcyA9IDxUPih3aXRoRWRnZXM6IHsgZWRnZXM6IHsgbm9kZTogVCB9W10gfSk6IFRbXSA9PiB7XG4gIGlmICghd2l0aEVkZ2VzLmVkZ2VzIHx8ICF3aXRoRWRnZXMuZWRnZXMubGVuZ3RoKSB7XG4gICAgcmV0dXJuIFtdXG4gIH1cbiAgcmV0dXJuIHdpdGhFZGdlcy5lZGdlcy5tYXAoKHsgbm9kZSB9KSA9PiBub2RlKVxufVxuIl19