@tricoteuses/assemblee
Version:
Retrieve, clean up & handle French Assemblée nationale's open data
167 lines (166 loc) • 36.6 kB
JavaScript
import assert from "assert";
import { formatISO } from "date-fns/formatISO";
import { fr as frLocale } from "date-fns/locale/fr";
import { parse as parseDate } from "date-fns/parse";
import deepEqual from "deep-equal";
import fs from "fs-extra";
import frontMatter from "front-matter";
import jsYaml from "js-yaml";
import { parse as parseHtml } from "node-html-parser";
import path from "path";
import { cleanAmendement } from "../cleaners/index.mjs";
import { Convert as AmendementsConvert } from "../types/amendements.mjs";
export async function* iterRechercheAmendements(amendementsSearchCacheDir, url, incremental, minDateDepot, minDateExamen, options = {}) {
for await (const [amendementUrlPath, amendement] of iterRechercheRawAmendements(amendementsSearchCacheDir, url, incremental, minDateDepot, minDateExamen, options)) {
cleanAmendement(amendement);
// Double conversion, to convert strings to dates.
yield [amendementUrlPath, AmendementsConvert.toAmendement(JSON.stringify(amendement))];
}
}
export async function* iterRechercheRawAmendements(amendementsSearchCacheDir, url, incremental, minDateDepot, minDateExamen, options = {}) {
await fs.ensureDir(amendementsSearchCacheDir);
assert.strictEqual(/[?&]date_depot=/.exec(url), null, `URL ${url} already contains a deposit date`);
assert.strictEqual(/[?&]order=/.exec(url), null, `URL ${url} already contains a sort order`);
assert.strictEqual(/[?&]page=/.exec(url), null, `URL ${url} already contains a page number`);
if (incremental) {
assert.notStrictEqual(/[?&]etat=/.exec(url), null, `In incremental mode, URL ${url} must contain an "etat" query parameter`);
}
url += (url.includes("?") ? "&" : "?") + "order=date_depot,desc&page=1";
if (minDateDepot != null) {
assert.notStrictEqual(/\d{4}-\d{2}-\d{2}/.exec(minDateDepot), null, `Invalid format for minimum date: ${minDateDepot}`);
}
let amendementNumber = 0;
iterSearchPages: while (true) {
if (options.verbose) {
console.log(`Fetching amendements search page at ${url}…`);
}
const response = await fetch(url);
assert(response.ok, `Retrieval of search page at ${url} failed with error: ${response.status} ${response.statusText}`);
const page = await response.text();
const html = parseHtml(page);
if (html.querySelector("div.no-result") !== null) {
break;
}
const amendementsCountDiv = html.querySelector("div.mirror-card-subtitle");
assert.notStrictEqual(amendementsCountDiv, null);
const amendementsCount = parseInt(/\d+/.exec(amendementsCountDiv.text)[0]);
const amendementsDiv = html.querySelector("div.amendement-list--results-table");
assert.notStrictEqual(amendementsDiv, null);
const amendementsTable = amendementsDiv.querySelector("table");
assert.notStrictEqual(amendementsTable, null);
const tfootTr = amendementsTable.querySelector("tfoot > tr");
assert.notStrictEqual(tfootTr, null);
const tfootTrCells = tfootTr.querySelectorAll("td, th").map(cell => cell.text);
assert(deepEqual(tfootTrCells, ["", "n°", "Dossier législatif", "Emplacement", "Auteur", "État", "Sort", "Date d'examen", "Examiné par", "Texte visé", "Date de dépôt"]), `Unexpected columns in ${JSON.stringify(tfootTrCells, null, 2)}`);
const amendementsTr = amendementsTable.querySelectorAll("tbody > tr");
assert.notStrictEqual(amendementsTr.length, 0);
for (const amendementTr of amendementsTr) {
amendementNumber++;
if (minDateDepot != null || minDateExamen != null) {
const amendementTdList = amendementTr.querySelectorAll("td");
assert.strictEqual(amendementTdList.length, 11, `Unexpected number of columns in amendment row: ${amendementTr.outerHTML}`);
if (minDateDepot != null) {
const dateDepotTd = amendementTdList[10];
const dateDepot = formatISO(parseDate(dateDepotTd.text, "d MMMM y", new Date(), {
locale: frLocale
}), {
representation: "date"
});
if (dateDepot < minDateDepot) {
// Since the amendments are sorted by deposit date, once an amendment
// with an old deposit date is encountered, the full search can be stopped.
break iterSearchPages;
}
}
if (minDateExamen != null) {
const dateExamenTd = amendementTdList[7];
if (dateExamenTd.text) {
const dateExamen = formatISO(parseDate(dateExamenTd.text, "d MMMM y", new Date(), {
locale: frLocale
}), {
representation: "date"
});
if (dateExamen < minDateExamen) {
// This amendment has been discussed since a few days.
// => Ignore it, because it should be in Assemblée open data.
continue;
}
}
}
}
const amendementUrlPath = amendementTr.getAttribute("data-href");
assert.notStrictEqual(amendementUrlPath, undefined);
const amendementUrlPathSplitted = amendementUrlPath.split("/");
assert.strictEqual(amendementUrlPathSplitted[0], "", `Unexpected URL path for amendement: ${amendementUrlPath}`);
assert.strictEqual(amendementUrlPathSplitted[1], "dyn", `Unexpected URL path for amendement: ${amendementUrlPath}`);
const amendementSearchCacheFilePath = path.join(amendementsSearchCacheDir, ...amendementUrlPathSplitted.slice(2)) + ".html";
await fs.ensureDir(path.dirname(amendementSearchCacheFilePath));
const existingAmendementSearchCache = (await fs.pathExists(amendementSearchCacheFilePath)) ? await fs.readFile(amendementSearchCacheFilePath, "utf8") : null;
const amendementTrOuterHtml = amendementTr.outerHTML;
const amendementSearchCache = `---\n${jsYaml.dump({
position: amendementsCount - amendementNumber,
search: url.replace(/&page=[\d]+/, "")
}, {
sortKeys: true
})}---\n\n${amendementTrOuterHtml}`;
if (amendementSearchCache === existingAmendementSearchCache) {
if (incremental) {
// The same <tr> is already in cache and in the same position.
// Assume that following amendments are not changed and stop looking
// for amendments to upsert.
break iterSearchPages;
}
continue;
}
if (existingAmendementSearchCache !== null && frontMatter(existingAmendementSearchCache).body === amendementTrOuterHtml) {
// The same <tr> is already in cache, but in a different position.
// Assume the amendment is unchanged.
continue;
}
if (options.verbose) {
if (existingAmendementSearchCache === null) {
console.log(`Adding amendement search cache: ${amendementSearchCacheFilePath}…`);
} else {
console.log(`Updating amendement search cache: ${amendementSearchCacheFilePath}…`);
}
}
const amendementHtmlUrl = new URL(amendementUrlPath, "https://www.assemblee-nationale.fr/").toString();
const amendementHtmlResponse = await fetch(amendementHtmlUrl);
if (amendementHtmlResponse.status === 404) {
console.log(`Amendement HTML page not found at ${amendementHtmlUrl}. Skipping.`);
continue;
}
assert(amendementHtmlResponse.ok, `Retrieval of amendement HTML page at ${amendementHtmlUrl} failed with error: ${amendementHtmlResponse.status} ${amendementHtmlResponse.statusText}`);
const amendementHtmlPage = await amendementHtmlResponse.text();
const amendementHtml = parseHtml(amendementHtmlPage);
const formatsLi = amendementHtml.querySelectorAll("li.mirror-card-header--options--content--item");
assert.notStrictEqual(formatsLi.length, 0);
const jsonLi = formatsLi.find(formatLi => {
const formatSpan = formatLi.querySelector("a > span");
assert.notStrictEqual(formatSpan, null, `No <span> in <a> found in ${formatLi.toString()}`);
return formatSpan.text === "Version JSON";
});
assert.notStrictEqual(jsonLi, undefined, `No JSON version found for amendement at ${amendementHtmlUrl}`);
const jsonA = jsonLi.querySelector("a");
assert.notStrictEqual(jsonA, null, `No <a> found in ${jsonLi.toString()}`);
const amendementJsonUrlPath = jsonA.getAttribute("href");
assert.notStrictEqual(amendementJsonUrlPath, undefined, `No URL found for JSON version of amendement: ${jsonLi.toString()}`);
const amendementJsonUrl = new URL(amendementJsonUrlPath, "https://www.assemblee-nationale.fr/").toString();
const amendementJsonResponse = await fetch(amendementJsonUrl);
assert(amendementJsonResponse.ok, `Retrieval of amendement JSON page at ${amendementJsonUrl} failed with error: ${amendementJsonResponse.status} ${amendementJsonResponse.statusText}`);
const amendement = await amendementJsonResponse.json();
yield [amendementUrlPath, amendement];
// SearchCache must be written at the end to ensure that everything is in a consistent state,
// even if script fails abruptly.
await fs.writeFile(amendementSearchCacheFilePath, amendementSearchCache, "utf8");
}
const paginationA = html.querySelector("div.an-pagination--item > i.an-icons-chevron-right ~ a");
if (paginationA === null) {
break;
}
const urlPath = paginationA.getAttribute("href");
assert.notStrictEqual(urlPath, undefined);
url = new URL(urlPath, "https://www.assemblee-nationale.fr/").toString();
}
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJmb3JtYXRJU08iLCJmciIsImZyTG9jYWxlIiwicGFyc2UiLCJwYXJzZURhdGUiLCJkZWVwRXF1YWwiLCJmcyIsImZyb250TWF0dGVyIiwianNZYW1sIiwicGFyc2VIdG1sIiwicGF0aCIsImNsZWFuQW1lbmRlbWVudCIsIkNvbnZlcnQiLCJBbWVuZGVtZW50c0NvbnZlcnQiLCJpdGVyUmVjaGVyY2hlQW1lbmRlbWVudHMiLCJhbWVuZGVtZW50c1NlYXJjaENhY2hlRGlyIiwidXJsIiwiaW5jcmVtZW50YWwiLCJtaW5EYXRlRGVwb3QiLCJtaW5EYXRlRXhhbWVuIiwib3B0aW9ucyIsImFtZW5kZW1lbnRVcmxQYXRoIiwiYW1lbmRlbWVudCIsIml0ZXJSZWNoZXJjaGVSYXdBbWVuZGVtZW50cyIsInRvQW1lbmRlbWVudCIsIkpTT04iLCJzdHJpbmdpZnkiLCJlbnN1cmVEaXIiLCJzdHJpY3RFcXVhbCIsImV4ZWMiLCJub3RTdHJpY3RFcXVhbCIsImluY2x1ZGVzIiwiYW1lbmRlbWVudE51bWJlciIsIml0ZXJTZWFyY2hQYWdlcyIsInZlcmJvc2UiLCJjb25zb2xlIiwibG9nIiwicmVzcG9uc2UiLCJmZXRjaCIsIm9rIiwic3RhdHVzIiwic3RhdHVzVGV4dCIsInBhZ2UiLCJ0ZXh0IiwiaHRtbCIsInF1ZXJ5U2VsZWN0b3IiLCJhbWVuZGVtZW50c0NvdW50RGl2IiwiYW1lbmRlbWVudHNDb3VudCIsInBhcnNlSW50IiwiYW1lbmRlbWVudHNEaXYiLCJhbWVuZGVtZW50c1RhYmxlIiwidGZvb3RUciIsInRmb290VHJDZWxscyIsInF1ZXJ5U2VsZWN0b3JBbGwiLCJtYXAiLCJjZWxsIiwiYW1lbmRlbWVudHNUciIsImxlbmd0aCIsImFtZW5kZW1lbnRUciIsImFtZW5kZW1lbnRUZExpc3QiLCJvdXRlckhUTUwiLCJkYXRlRGVwb3RUZCIsImRhdGVEZXBvdCIsIkRhdGUiLCJsb2NhbGUiLCJyZXByZXNlbnRhdGlvbiIsImRhdGVFeGFtZW5UZCIsImRhdGVFeGFtZW4iLCJnZXRBdHRyaWJ1dGUiLCJ1bmRlZmluZWQiLCJhbWVuZGVtZW50VXJsUGF0aFNwbGl0dGVkIiwic3BsaXQiLCJhbWVuZGVtZW50U2VhcmNoQ2FjaGVGaWxlUGF0aCIsImpvaW4iLCJzbGljZSIsImRpcm5hbWUiLCJleGlzdGluZ0FtZW5kZW1lbnRTZWFyY2hDYWNoZSIsInBhdGhFeGlzdHMiLCJyZWFkRmlsZSIsImFtZW5kZW1lbnRUck91dGVySHRtbCIsImFtZW5kZW1lbnRTZWFyY2hDYWNoZSIsImR1bXAiLCJwb3NpdGlvbiIsInNlYXJjaCIsInJlcGxhY2UiLCJzb3J0S2V5cyIsImJvZHkiLCJhbWVuZGVtZW50SHRtbFVybCIsIlVSTCIsInRvU3RyaW5nIiwiYW1lbmRlbWVudEh0bWxSZXNwb25zZSIsImFtZW5kZW1lbnRIdG1sUGFnZSIsImFtZW5kZW1lbnRIdG1sIiwiZm9ybWF0c0xpIiwianNvbkxpIiwiZmluZCIsImZvcm1hdExpIiwiZm9ybWF0U3BhbiIsImpzb25BIiwiYW1lbmRlbWVudEpzb25VcmxQYXRoIiwiYW1lbmRlbWVudEpzb25VcmwiLCJhbWVuZGVtZW50SnNvblJlc3BvbnNlIiwianNvbiIsIndyaXRlRmlsZSIsInBhZ2luYXRpb25BIiwidXJsUGF0aCJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wYXJzZXJzL3JlY2hlcmNoZV9hbWVuZGVtZW50cy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgYXNzZXJ0IGZyb20gXCJhc3NlcnRcIlxuaW1wb3J0IHsgZm9ybWF0SVNPIH0gZnJvbSBcImRhdGUtZm5zL2Zvcm1hdElTT1wiXG5pbXBvcnQgeyBmciBhcyBmckxvY2FsZSB9IGZyb20gXCJkYXRlLWZucy9sb2NhbGUvZnJcIlxuaW1wb3J0IHsgcGFyc2UgYXMgcGFyc2VEYXRlIH0gZnJvbSBcImRhdGUtZm5zL3BhcnNlXCJcbmltcG9ydCBkZWVwRXF1YWwgZnJvbSBcImRlZXAtZXF1YWxcIlxuaW1wb3J0IGZzIGZyb20gXCJmcy1leHRyYVwiXG5pbXBvcnQgZnJvbnRNYXR0ZXIgZnJvbSBcImZyb250LW1hdHRlclwiXG5pbXBvcnQganNZYW1sIGZyb20gXCJqcy15YW1sXCJcbmltcG9ydCB7IHBhcnNlIGFzIHBhcnNlSHRtbCB9IGZyb20gXCJub2RlLWh0bWwtcGFyc2VyXCJcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCJcblxuaW1wb3J0IHsgY2xlYW5BbWVuZGVtZW50IH0gZnJvbSBcIi4uL2NsZWFuZXJzXCJcbmltcG9ydCB7IEFtZW5kZW1lbnQgYXMgUmF3QW1lbmRlbWVudCB9IGZyb20gXCIuLi9yYXdfdHlwZXMvYW1lbmRlbWVudHNcIlxuaW1wb3J0IHsgQW1lbmRlbWVudCwgQ29udmVydCBhcyBBbWVuZGVtZW50c0NvbnZlcnQgfSBmcm9tIFwiLi4vdHlwZXMvYW1lbmRlbWVudHNcIlxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24qIGl0ZXJSZWNoZXJjaGVBbWVuZGVtZW50cyhcbiAgYW1lbmRlbWVudHNTZWFyY2hDYWNoZURpcjogc3RyaW5nLFxuICB1cmw6IHN0cmluZyxcbiAgaW5jcmVtZW50YWw6IGJvb2xlYW4sXG4gIG1pbkRhdGVEZXBvdD86IHN0cmluZyB8IG51bGwsXG4gIG1pbkRhdGVFeGFtZW4/OiBzdHJpbmcgfCBudWxsLFxuICBvcHRpb25zOiB7IHNpbGVudD86IGJvb2xlYW47IHZlcmJvc2U/OiBib29sZWFuIH0gPSB7fSxcbik6IEFzeW5jR2VuZXJhdG9yPFtzdHJpbmcsIEFtZW5kZW1lbnRdLCB2b2lkLCB1bmtub3duPiB7XG4gIGZvciBhd2FpdCAoY29uc3QgW1xuICAgIGFtZW5kZW1lbnRVcmxQYXRoLFxuICAgIGFtZW5kZW1lbnQsXG4gIF0gb2YgaXRlclJlY2hlcmNoZVJhd0FtZW5kZW1lbnRzKFxuICAgIGFtZW5kZW1lbnRzU2VhcmNoQ2FjaGVEaXIsXG4gICAgdXJsLFxuICAgIGluY3JlbWVudGFsLFxuICAgIG1pbkRhdGVEZXBvdCxcbiAgICBtaW5EYXRlRXhhbWVuLFxuICAgIG9wdGlvbnMsXG4gICkpIHtcbiAgICBjbGVhbkFtZW5kZW1lbnQoYW1lbmRlbWVudClcbiAgICAvLyBEb3VibGUgY29udmVyc2lvbiwgdG8gY29udmVydCBzdHJpbmdzIHRvIGRhdGVzLlxuICAgIHlpZWxkIFtcbiAgICAgIGFtZW5kZW1lbnRVcmxQYXRoLFxuICAgICAgQW1lbmRlbWVudHNDb252ZXJ0LnRvQW1lbmRlbWVudChKU09OLnN0cmluZ2lmeShhbWVuZGVtZW50KSksXG4gICAgXVxuICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiogaXRlclJlY2hlcmNoZVJhd0FtZW5kZW1lbnRzKFxuICBhbWVuZGVtZW50c1NlYXJjaENhY2hlRGlyOiBzdHJpbmcsXG4gIHVybDogc3RyaW5nLFxuICBpbmNyZW1lbnRhbDogYm9vbGVhbixcbiAgbWluRGF0ZURlcG90Pzogc3RyaW5nIHwgbnVsbCxcbiAgbWluRGF0ZUV4YW1lbj86IHN0cmluZyB8IG51bGwsXG4gIG9wdGlvbnM6IHsgc2lsZW50PzogYm9vbGVhbjsgdmVyYm9zZT86IGJvb2xlYW4gfSA9IHt9LFxuKTogQXN5bmNHZW5lcmF0b3I8W3N0cmluZywgUmF3QW1lbmRlbWVudF0sIHZvaWQsIHVua25vd24+IHtcbiAgYXdhaXQgZnMuZW5zdXJlRGlyKGFtZW5kZW1lbnRzU2VhcmNoQ2FjaGVEaXIpXG5cbiAgYXNzZXJ0LnN0cmljdEVxdWFsKFxuICAgIC9bPyZdZGF0ZV9kZXBvdD0vLmV4ZWModXJsKSxcbiAgICBudWxsLFxuICAgIGBVUkwgJHt1cmx9IGFscmVhZHkgY29udGFpbnMgYSBkZXBvc2l0IGRhdGVgLFxuICApXG4gIGFzc2VydC5zdHJpY3RFcXVhbChcbiAgICAvWz8mXW9yZGVyPS8uZXhlYyh1cmwpLFxuICAgIG51bGwsXG4gICAgYFVSTCAke3VybH0gYWxyZWFkeSBjb250YWlucyBhIHNvcnQgb3JkZXJgLFxuICApXG4gIGFzc2VydC5zdHJpY3RFcXVhbChcbiAgICAvWz8mXXBhZ2U9Ly5leGVjKHVybCksXG4gICAgbnVsbCxcbiAgICBgVVJMICR7dXJsfSBhbHJlYWR5IGNvbnRhaW5zIGEgcGFnZSBudW1iZXJgLFxuICApXG4gIGlmIChpbmNyZW1lbnRhbCkge1xuICAgIGFzc2VydC5ub3RTdHJpY3RFcXVhbChcbiAgICAgIC9bPyZdZXRhdD0vLmV4ZWModXJsKSxcbiAgICAgIG51bGwsXG4gICAgICBgSW4gaW5jcmVtZW50YWwgbW9kZSwgVVJMICR7dXJsfSBtdXN0IGNvbnRhaW4gYW4gXCJldGF0XCIgcXVlcnkgcGFyYW1ldGVyYCxcbiAgICApXG4gIH1cbiAgdXJsICs9ICh1cmwuaW5jbHVkZXMoXCI/XCIpID8gXCImXCIgOiBcIj9cIikgKyBcIm9yZGVyPWRhdGVfZGVwb3QsZGVzYyZwYWdlPTFcIlxuXG4gIGlmIChtaW5EYXRlRGVwb3QgIT0gbnVsbCkge1xuICAgIGFzc2VydC5ub3RTdHJpY3RFcXVhbChcbiAgICAgIC9cXGR7NH0tXFxkezJ9LVxcZHsyfS8uZXhlYyhtaW5EYXRlRGVwb3QpLFxuICAgICAgbnVsbCxcbiAgICAgIGBJbnZhbGlkIGZvcm1hdCBmb3IgbWluaW11bSBkYXRlOiAke21pbkRhdGVEZXBvdH1gLFxuICAgIClcbiAgfVxuXG4gIGxldCBhbWVuZGVtZW50TnVtYmVyID0gMFxuICBpdGVyU2VhcmNoUGFnZXM6IHdoaWxlICh0cnVlKSB7XG4gICAgaWYgKG9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgY29uc29sZS5sb2coYEZldGNoaW5nIGFtZW5kZW1lbnRzIHNlYXJjaCBwYWdlIGF0ICR7dXJsfeKApmApXG4gICAgfVxuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2godXJsKVxuICAgIGFzc2VydChcbiAgICAgIHJlc3BvbnNlLm9rLFxuICAgICAgYFJldHJpZXZhbCBvZiBzZWFyY2ggcGFnZSBhdCAke3VybH0gZmFpbGVkIHdpdGggZXJyb3I6ICR7cmVzcG9uc2Uuc3RhdHVzfSAke3Jlc3BvbnNlLnN0YXR1c1RleHR9YCxcbiAgICApXG4gICAgY29uc3QgcGFnZSA9IGF3YWl0IHJlc3BvbnNlLnRleHQoKVxuICAgIGNvbnN0IGh0bWwgPSBwYXJzZUh0bWwocGFnZSlcbiAgICBpZiAoaHRtbC5xdWVyeVNlbGVjdG9yKFwiZGl2Lm5vLXJlc3VsdFwiKSAhPT0gbnVsbCkge1xuICAgICAgYnJlYWtcbiAgICB9XG4gICAgY29uc3QgYW1lbmRlbWVudHNDb3VudERpdiA9IGh0bWwucXVlcnlTZWxlY3RvcihcImRpdi5taXJyb3ItY2FyZC1zdWJ0aXRsZVwiKVxuICAgIGFzc2VydC5ub3RTdHJpY3RFcXVhbChhbWVuZGVtZW50c0NvdW50RGl2LCBudWxsKVxuICAgIGNvbnN0IGFtZW5kZW1lbnRzQ291bnQgPSBwYXJzZUludCgvXFxkKy8uZXhlYyhhbWVuZGVtZW50c0NvdW50RGl2IS50ZXh0KSFbMF0pXG4gICAgY29uc3QgYW1lbmRlbWVudHNEaXYgPSBodG1sLnF1ZXJ5U2VsZWN0b3IoXG4gICAgICBcImRpdi5hbWVuZGVtZW50LWxpc3QtLXJlc3VsdHMtdGFibGVcIixcbiAgICApXG4gICAgYXNzZXJ0Lm5vdFN0cmljdEVxdWFsKGFtZW5kZW1lbnRzRGl2LCBudWxsKVxuICAgIGNvbnN0IGFtZW5kZW1lbnRzVGFibGUgPSBhbWVuZGVtZW50c0RpdiEucXVlcnlTZWxlY3RvcihcInRhYmxlXCIpXG4gICAgYXNzZXJ0Lm5vdFN0cmljdEVxdWFsKGFtZW5kZW1lbnRzVGFibGUsIG51bGwpXG4gICAgY29uc3QgdGZvb3RUciA9IGFtZW5kZW1lbnRzVGFibGUhLnF1ZXJ5U2VsZWN0b3IoXCJ0Zm9vdCA+IHRyXCIpXG4gICAgYXNzZXJ0Lm5vdFN0cmljdEVxdWFsKHRmb290VHIsIG51bGwpXG4gICAgY29uc3QgdGZvb3RUckNlbGxzID0gdGZvb3RUciFcbiAgICAgIC5xdWVyeVNlbGVjdG9yQWxsKFwidGQsIHRoXCIpXG4gICAgICAubWFwKChjZWxsKSA9PiBjZWxsLnRleHQpXG4gICAgYXNzZXJ0KFxuICAgICAgZGVlcEVxdWFsKHRmb290VHJDZWxscywgW1xuICAgICAgICBcIlwiLFxuICAgICAgICBcIm7CsFwiLFxuICAgICAgICBcIkRvc3NpZXIgbMOpZ2lzbGF0aWZcIixcbiAgICAgICAgXCJFbXBsYWNlbWVudFwiLFxuICAgICAgICBcIkF1dGV1clwiLFxuICAgICAgICBcIsOJdGF0XCIsXG4gICAgICAgIFwiU29ydFwiLFxuICAgICAgICBcIkRhdGUgZCdleGFtZW5cIixcbiAgICAgICAgXCJFeGFtaW7DqSBwYXJcIixcbiAgICAgICAgXCJUZXh0ZSB2aXPDqVwiLFxuICAgICAgICBcIkRhdGUgZGUgZMOpcMO0dFwiLFxuICAgICAgXSksXG4gICAgICBgVW5leHBlY3RlZCBjb2x1bW5zIGluICR7SlNPTi5zdHJpbmdpZnkodGZvb3RUckNlbGxzLCBudWxsLCAyKX1gLFxuICAgIClcbiAgICBjb25zdCBhbWVuZGVtZW50c1RyID0gYW1lbmRlbWVudHNUYWJsZSEucXVlcnlTZWxlY3RvckFsbChcInRib2R5ID4gdHJcIilcbiAgICBhc3NlcnQubm90U3RyaWN0RXF1YWwoYW1lbmRlbWVudHNUci5sZW5ndGgsIDApXG4gICAgZm9yIChjb25zdCBhbWVuZGVtZW50VHIgb2YgYW1lbmRlbWVudHNUcikge1xuICAgICAgYW1lbmRlbWVudE51bWJlcisrXG5cbiAgICAgIGlmIChtaW5EYXRlRGVwb3QgIT0gbnVsbCB8fCBtaW5EYXRlRXhhbWVuICE9IG51bGwpIHtcbiAgICAgICAgY29uc3QgYW1lbmRlbWVudFRkTGlzdCA9IGFtZW5kZW1lbnRUci5xdWVyeVNlbGVjdG9yQWxsKFwidGRcIilcbiAgICAgICAgYXNzZXJ0LnN0cmljdEVxdWFsKFxuICAgICAgICAgIGFtZW5kZW1lbnRUZExpc3QubGVuZ3RoLFxuICAgICAgICAgIDExLFxuICAgICAgICAgIGBVbmV4cGVjdGVkIG51bWJlciBvZiBjb2x1bW5zIGluIGFtZW5kbWVudCByb3c6ICR7YW1lbmRlbWVudFRyLm91dGVySFRNTH1gLFxuICAgICAgICApXG4gICAgICAgIGlmIChtaW5EYXRlRGVwb3QgIT0gbnVsbCkge1xuICAgICAgICAgIGNvbnN0IGRhdGVEZXBvdFRkID0gYW1lbmRlbWVudFRkTGlzdFsxMF1cbiAgICAgICAgICBjb25zdCBkYXRlRGVwb3QgPSBmb3JtYXRJU08oXG4gICAgICAgICAgICBwYXJzZURhdGUoZGF0ZURlcG90VGQudGV4dCwgXCJkIE1NTU0geVwiLCBuZXcgRGF0ZSgpLCB7XG4gICAgICAgICAgICAgIGxvY2FsZTogZnJMb2NhbGUsXG4gICAgICAgICAgICB9KSxcbiAgICAgICAgICAgIHsgcmVwcmVzZW50YXRpb246IFwiZGF0ZVwiIH0sXG4gICAgICAgICAgKVxuICAgICAgICAgIGlmIChkYXRlRGVwb3QgPCBtaW5EYXRlRGVwb3QpIHtcbiAgICAgICAgICAgIC8vIFNpbmNlIHRoZSBhbWVuZG1lbnRzIGFyZSBzb3J0ZWQgYnkgZGVwb3NpdCBkYXRlLCBvbmNlIGFuIGFtZW5kbWVudFxuICAgICAgICAgICAgLy8gd2l0aCBhbiBvbGQgZGVwb3NpdCBkYXRlIGlzIGVuY291bnRlcmVkLCB0aGUgZnVsbCBzZWFyY2ggY2FuIGJlIHN0b3BwZWQuXG4gICAgICAgICAgICBicmVhayBpdGVyU2VhcmNoUGFnZXNcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG1pbkRhdGVFeGFtZW4gIT0gbnVsbCkge1xuICAgICAgICAgIGNvbnN0IGRhdGVFeGFtZW5UZCA9IGFtZW5kZW1lbnRUZExpc3RbN11cbiAgICAgICAgICBpZiAoZGF0ZUV4YW1lblRkLnRleHQpIHtcbiAgICAgICAgICAgIGNvbnN0IGRhdGVFeGFtZW4gPSBmb3JtYXRJU08oXG4gICAgICAgICAgICAgIHBhcnNlRGF0ZShkYXRlRXhhbWVuVGQudGV4dCwgXCJkIE1NTU0geVwiLCBuZXcgRGF0ZSgpLCB7XG4gICAgICAgICAgICAgICAgbG9jYWxlOiBmckxvY2FsZSxcbiAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICAgIHsgcmVwcmVzZW50YXRpb246IFwiZGF0ZVwiIH0sXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBpZiAoZGF0ZUV4YW1lbiA8IG1pbkRhdGVFeGFtZW4pIHtcbiAgICAgICAgICAgICAgLy8gVGhpcyBhbWVuZG1lbnQgaGFzIGJlZW4gZGlzY3Vzc2VkIHNpbmNlIGEgZmV3IGRheXMuXG4gICAgICAgICAgICAgIC8vID0+IElnbm9yZSBpdCwgYmVjYXVzZSBpdCBzaG91bGQgYmUgaW4gQXNzZW1ibMOpZSBvcGVuIGRhdGEuXG4gICAgICAgICAgICAgIGNvbnRpbnVlXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGFtZW5kZW1lbnRVcmxQYXRoID0gYW1lbmRlbWVudFRyLmdldEF0dHJpYnV0ZShcImRhdGEtaHJlZlwiKVxuICAgICAgYXNzZXJ0Lm5vdFN0cmljdEVxdWFsKGFtZW5kZW1lbnRVcmxQYXRoLCB1bmRlZmluZWQpXG4gICAgICBjb25zdCBhbWVuZGVtZW50VXJsUGF0aFNwbGl0dGVkID0gYW1lbmRlbWVudFVybFBhdGghLnNwbGl0KFwiL1wiKVxuICAgICAgYXNzZXJ0LnN0cmljdEVxdWFsKFxuICAgICAgICBhbWVuZGVtZW50VXJsUGF0aFNwbGl0dGVkWzBdLFxuICAgICAgICBcIlwiLFxuICAgICAgICBgVW5leHBlY3RlZCBVUkwgcGF0aCBmb3IgYW1lbmRlbWVudDogJHthbWVuZGVtZW50VXJsUGF0aH1gLFxuICAgICAgKVxuICAgICAgYXNzZXJ0LnN0cmljdEVxdWFsKFxuICAgICAgICBhbWVuZGVtZW50VXJsUGF0aFNwbGl0dGVkWzFdLFxuICAgICAgICBcImR5blwiLFxuICAgICAgICBgVW5leHBlY3RlZCBVUkwgcGF0aCBmb3IgYW1lbmRlbWVudDogJHthbWVuZGVtZW50VXJsUGF0aH1gLFxuICAgICAgKVxuICAgICAgY29uc3QgYW1lbmRlbWVudFNlYXJjaENhY2hlRmlsZVBhdGggPVxuICAgICAgICBwYXRoLmpvaW4oXG4gICAgICAgICAgYW1lbmRlbWVudHNTZWFyY2hDYWNoZURpcixcbiAgICAgICAgICAuLi5hbWVuZGVtZW50VXJsUGF0aFNwbGl0dGVkLnNsaWNlKDIpLFxuICAgICAgICApICsgXCIuaHRtbFwiXG4gICAgICBhd2FpdCBmcy5lbnN1cmVEaXIocGF0aC5kaXJuYW1lKGFtZW5kZW1lbnRTZWFyY2hDYWNoZUZpbGVQYXRoKSlcbiAgICAgIGNvbnN0IGV4aXN0aW5nQW1lbmRlbWVudFNlYXJjaENhY2hlID0gKGF3YWl0IGZzLnBhdGhFeGlzdHMoXG4gICAgICAgIGFtZW5kZW1lbnRTZWFyY2hDYWNoZUZpbGVQYXRoLFxuICAgICAgKSlcbiAgICAgICAgPyBhd2FpdCBmcy5yZWFkRmlsZShhbWVuZGVtZW50U2VhcmNoQ2FjaGVGaWxlUGF0aCwgXCJ1dGY4XCIpXG4gICAgICAgIDogbnVsbFxuICAgICAgY29uc3QgYW1lbmRlbWVudFRyT3V0ZXJIdG1sID0gYW1lbmRlbWVudFRyLm91dGVySFRNTFxuICAgICAgY29uc3QgYW1lbmRlbWVudFNlYXJjaENhY2hlID0gYC0tLVxcbiR7anNZYW1sLmR1bXAoXG4gICAgICAgIHtcbiAgICAgICAgICBwb3NpdGlvbjogYW1lbmRlbWVudHNDb3VudCAtIGFtZW5kZW1lbnROdW1iZXIsXG4gICAgICAgICAgc2VhcmNoOiB1cmwucmVwbGFjZSgvJnBhZ2U9W1xcZF0rLywgXCJcIiksXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBzb3J0S2V5czogdHJ1ZSxcbiAgICAgICAgfSxcbiAgICAgICl9LS0tXFxuXFxuJHthbWVuZGVtZW50VHJPdXRlckh0bWx9YFxuICAgICAgaWYgKGFtZW5kZW1lbnRTZWFyY2hDYWNoZSA9PT0gZXhpc3RpbmdBbWVuZGVtZW50U2VhcmNoQ2FjaGUpIHtcbiAgICAgICAgaWYgKGluY3JlbWVudGFsKSB7XG4gICAgICAgICAgLy8gVGhlIHNhbWUgPHRyPiBpcyBhbHJlYWR5IGluIGNhY2hlIGFuZCBpbiB0aGUgc2FtZSBwb3NpdGlvbi5cbiAgICAgICAgICAvLyBBc3N1bWUgdGhhdCBmb2xsb3dpbmcgYW1lbmRtZW50cyBhcmUgbm90IGNoYW5nZWQgYW5kIHN0b3AgbG9va2luZ1xuICAgICAgICAgIC8vIGZvciBhbWVuZG1lbnRzIHRvIHVwc2VydC5cbiAgICAgICAgICBicmVhayBpdGVyU2VhcmNoUGFnZXNcbiAgICAgICAgfVxuICAgICAgICBjb250aW51ZVxuICAgICAgfVxuICAgICAgaWYgKFxuICAgICAgICBleGlzdGluZ0FtZW5kZW1lbnRTZWFyY2hDYWNoZSAhPT0gbnVsbCAmJlxuICAgICAgICBmcm9udE1hdHRlcihleGlzdGluZ0FtZW5kZW1lbnRTZWFyY2hDYWNoZSkuYm9keSA9PT1cbiAgICAgICAgICBhbWVuZGVtZW50VHJPdXRlckh0bWxcbiAgICAgICkge1xuICAgICAgICAvLyBUaGUgc2FtZSA8dHI+IGlzIGFscmVhZHkgaW4gY2FjaGUsIGJ1dCBpbiBhIGRpZmZlcmVudCBwb3NpdGlvbi5cbiAgICAgICAgLy8gQXNzdW1lIHRoZSBhbWVuZG1lbnQgaXMgdW5jaGFuZ2VkLlxuICAgICAgICBjb250aW51ZVxuICAgICAgfVxuICAgICAgaWYgKG9wdGlvbnMudmVyYm9zZSkge1xuICAgICAgICBpZiAoZXhpc3RpbmdBbWVuZGVtZW50U2VhcmNoQ2FjaGUgPT09IG51bGwpIHtcbiAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgIGBBZGRpbmcgYW1lbmRlbWVudCBzZWFyY2ggY2FjaGU6ICR7YW1lbmRlbWVudFNlYXJjaENhY2hlRmlsZVBhdGh94oCmYCxcbiAgICAgICAgICApXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICBgVXBkYXRpbmcgYW1lbmRlbWVudCBzZWFyY2ggY2FjaGU6ICR7YW1lbmRlbWVudFNlYXJjaENhY2hlRmlsZVBhdGh94oCmYCxcbiAgICAgICAgICApXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGNvbnN0IGFtZW5kZW1lbnRIdG1sVXJsID0gbmV3IFVSTChcbiAgICAgICAgYW1lbmRlbWVudFVybFBhdGghLFxuICAgICAgICBcImh0dHBzOi8vd3d3LmFzc2VtYmxlZS1uYXRpb25hbGUuZnIvXCIsXG4gICAgICApLnRvU3RyaW5nKClcbiAgICAgIGNvbnN0IGFtZW5kZW1lbnRIdG1sUmVzcG9uc2UgPSBhd2FpdCBmZXRjaChhbWVuZGVtZW50SHRtbFVybClcbiAgICAgIGlmIChhbWVuZGVtZW50SHRtbFJlc3BvbnNlLnN0YXR1cyA9PT0gNDA0KSB7XG4gICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgIGBBbWVuZGVtZW50IEhUTUwgcGFnZSBub3QgZm91bmQgYXQgJHthbWVuZGVtZW50SHRtbFVybH0uIFNraXBwaW5nLmAsXG4gICAgICAgIClcbiAgICAgICAgY29udGludWVcbiAgICAgIH1cbiAgICAgIGFzc2VydChcbiAgICAgICAgYW1lbmRlbWVudEh0bWxSZXNwb25zZS5vayxcbiAgICAgICAgYFJldHJpZXZhbCBvZiBhbWVuZGVtZW50IEhUTUwgcGFnZSBhdCAke2FtZW5kZW1lbnRIdG1sVXJsfSBmYWlsZWQgd2l0aCBlcnJvcjogJHthbWVuZGVtZW50SHRtbFJlc3BvbnNlLnN0YXR1c30gJHthbWVuZGVtZW50SHRtbFJlc3BvbnNlLnN0YXR1c1RleHR9YCxcbiAgICAgIClcbiAgICAgIGNvbnN0IGFtZW5kZW1lbnRIdG1sUGFnZSA9IGF3YWl0IGFtZW5kZW1lbnRIdG1sUmVzcG9uc2UudGV4dCgpXG4gICAgICBjb25zdCBhbWVuZGVtZW50SHRtbCA9IHBhcnNlSHRtbChhbWVuZGVtZW50SHRtbFBhZ2UpXG4gICAgICBjb25zdCBmb3JtYXRzTGkgPSBhbWVuZGVtZW50SHRtbC5xdWVyeVNlbGVjdG9yQWxsKFxuICAgICAgICBcImxpLm1pcnJvci1jYXJkLWhlYWRlci0tb3B0aW9ucy0tY29udGVudC0taXRlbVwiLFxuICAgICAgKVxuICAgICAgYXNzZXJ0Lm5vdFN0cmljdEVxdWFsKGZvcm1hdHNMaS5sZW5ndGgsIDApXG4gICAgICBjb25zdCBqc29uTGkgPSBmb3JtYXRzTGkuZmluZCgoZm9ybWF0TGkpID0+IHtcbiAgICAgICAgY29uc3QgZm9ybWF0U3BhbiA9IGZvcm1hdExpLnF1ZXJ5U2VsZWN0b3IoXCJhID4gc3BhblwiKVxuICAgICAgICBhc3NlcnQubm90U3RyaWN0RXF1YWwoXG4gICAgICAgICAgZm9ybWF0U3BhbixcbiAgICAgICAgICBudWxsLFxuICAgICAgICAgIGBObyA8c3Bhbj4gaW4gPGE+IGZvdW5kIGluICR7Zm9ybWF0TGkudG9TdHJpbmcoKX1gLFxuICAgICAgICApXG4gICAgICAgIHJldHVybiBmb3JtYXRTcGFuIS50ZXh0ID09PSBcIlZlcnNpb24gSlNPTlwiXG4gICAgICB9KVxuICAgICAgYXNzZXJ0Lm5vdFN0cmljdEVxdWFsKFxuICAgICAgICBqc29uTGksXG4gICAgICAgIHVuZGVmaW5lZCxcbiAgICAgICAgYE5vIEpTT04gdmVyc2lvbiBmb3VuZCBmb3IgYW1lbmRlbWVudCBhdCAke2FtZW5kZW1lbnRIdG1sVXJsfWAsXG4gICAgICApXG4gICAgICBjb25zdCBqc29uQSA9IGpzb25MaSEucXVlcnlTZWxlY3RvcihcImFcIilcbiAgICAgIGFzc2VydC5ub3RTdHJpY3RFcXVhbChcbiAgICAgICAganNvbkEsXG4gICAgICAgIG51bGwsXG4gICAgICAgIGBObyA8YT4gZm91bmQgaW4gJHtqc29uTGkhLnRvU3RyaW5nKCl9YCxcbiAgICAgIClcbiAgICAgIGNvbnN0IGFtZW5kZW1lbnRKc29uVXJsUGF0aCA9IGpzb25BIS5nZXRBdHRyaWJ1dGUoXCJocmVmXCIpXG4gICAgICBhc3NlcnQubm90U3RyaWN0RXF1YWwoXG4gICAgICAgIGFtZW5kZW1lbnRKc29uVXJsUGF0aCxcbiAgICAgICAgdW5kZWZpbmVkLFxuICAgICAgICBgTm8gVVJMIGZvdW5kIGZvciBKU09OIHZlcnNpb24gb2YgYW1lbmRlbWVudDogJHtqc29uTGkhLnRvU3RyaW5nKCl9YCxcbiAgICAgIClcbiAgICAgIGNvbnN0IGFtZW5kZW1lbnRKc29uVXJsID0gbmV3IFVSTChcbiAgICAgICAgYW1lbmRlbWVudEpzb25VcmxQYXRoISxcbiAgICAgICAgXCJodHRwczovL3d3dy5hc3NlbWJsZWUtbmF0aW9uYWxlLmZyL1wiLFxuICAgICAgKS50b1N0cmluZygpXG4gICAgICBjb25zdCBhbWVuZGVtZW50SnNvblJlc3BvbnNlID0gYXdhaXQgZmV0Y2goYW1lbmRlbWVudEpzb25VcmwpXG4gICAgICBhc3NlcnQoXG4gICAgICAgIGFtZW5kZW1lbnRKc29uUmVzcG9uc2Uub2ssXG4gICAgICAgIGBSZXRyaWV2YWwgb2YgYW1lbmRlbWVudCBKU09OIHBhZ2UgYXQgJHthbWVuZGVtZW50SnNvblVybH0gZmFpbGVkIHdpdGggZXJyb3I6ICR7YW1lbmRlbWVudEpzb25SZXNwb25zZS5zdGF0dXN9ICR7YW1lbmRlbWVudEpzb25SZXNwb25zZS5zdGF0dXNUZXh0fWAsXG4gICAgICApXG4gICAgICBjb25zdCBhbWVuZGVtZW50ID0gYXdhaXQgYW1lbmRlbWVudEpzb25SZXNwb25zZS5qc29uKClcbiAgICAgIHlpZWxkIFthbWVuZGVtZW50VXJsUGF0aCEsIGFtZW5kZW1lbnRdXG5cbiAgICAgIC8vIFNlYXJjaENhY2hlIG11c3QgYmUgd3JpdHRlbiBhdCB0aGUgZW5kIHRvIGVuc3VyZSB0aGF0IGV2ZXJ5dGhpbmcgaXMgaW4gYSBjb25zaXN0ZW50IHN0YXRlLFxuICAgICAgLy8gZXZlbiBpZiBzY3JpcHQgZmFpbHMgYWJydXB0bHkuXG4gICAgICBhd2FpdCBmcy53cml0ZUZpbGUoXG4gICAgICAgIGFtZW5kZW1lbnRTZWFyY2hDYWNoZUZpbGVQYXRoLFxuICAgICAgICBhbWVuZGVtZW50U2VhcmNoQ2FjaGUsXG4gICAgICAgIFwidXRmOFwiLFxuICAgICAgKVxuICAgIH1cblxuICAgIGNvbnN0IHBhZ2luYXRpb25BID0gaHRtbC5xdWVyeVNlbGVjdG9yKFxuICAgICAgXCJkaXYuYW4tcGFnaW5hdGlvbi0taXRlbSA+IGkuYW4taWNvbnMtY2hldnJvbi1yaWdodCB+IGFcIixcbiAgICApXG4gICAgaWYgKHBhZ2luYXRpb25BID09PSBudWxsKSB7XG4gICAgICBicmVha1xuICAgIH1cbiAgICBjb25zdCB1cmxQYXRoID0gcGFnaW5hdGlvbkEuZ2V0QXR0cmlidXRlKFwiaHJlZlwiKVxuICAgIGFzc2VydC5ub3RTdHJpY3RFcXVhbCh1cmxQYXRoLCB1bmRlZmluZWQpXG4gICAgdXJsID0gbmV3IFVSTCh1cmxQYXRoISwgXCJodHRwczovL3d3dy5hc3NlbWJsZWUtbmF0aW9uYWxlLmZyL1wiKS50b1N0cmluZygpXG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBT0EsTUFBTSxNQUFNLFFBQVE7QUFDM0IsU0FBU0MsU0FBUyxRQUFRLG9CQUFvQjtBQUM5QyxTQUFTQyxFQUFFLElBQUlDLFFBQVEsUUFBUSxvQkFBb0I7QUFDbkQsU0FBU0MsS0FBSyxJQUFJQyxTQUFTLFFBQVEsZ0JBQWdCO0FBQ25ELE9BQU9DLFNBQVMsTUFBTSxZQUFZO0FBQ2xDLE9BQU9DLEVBQUUsTUFBTSxVQUFVO0FBQ3pCLE9BQU9DLFdBQVcsTUFBTSxjQUFjO0FBQ3RDLE9BQU9DLE1BQU0sTUFBTSxTQUFTO0FBQzVCLFNBQVNMLEtBQUssSUFBSU0sU0FBUyxRQUFRLGtCQUFrQjtBQUNyRCxPQUFPQyxJQUFJLE1BQU0sTUFBTTtBQUFBLFNBRWRDLGVBQWU7QUFBQSxTQUVIQyxPQUFPLElBQUlDLGtCQUFrQjtBQUVsRCxPQUFPLGdCQUFnQkMsd0JBQXdCQSxDQUM3Q0MseUJBQWlDLEVBQ2pDQyxHQUFXLEVBQ1hDLFdBQW9CLEVBQ3BCQyxZQUE0QixFQUM1QkMsYUFBNkIsRUFDN0JDLE9BQWdELEdBQUcsQ0FBQyxDQUFDLEVBQ0E7RUFDckQsV0FBVyxNQUFNLENBQ2ZDLGlCQUFpQixFQUNqQkMsVUFBVSxDQUNYLElBQUlDLDJCQUEyQixDQUM5QlIseUJBQXlCLEVBQ3pCQyxHQUFHLEVBQ0hDLFdBQVcsRUFDWEMsWUFBWSxFQUNaQyxhQUFhLEVBQ2JDLE9BQ0YsQ0FBQyxFQUFFO0lBQ0RULGVBQWUsQ0FBQ1csVUFBVSxDQUFDO0lBQzNCO0lBQ0EsTUFBTSxDQUNKRCxpQkFBaUIsRUFD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