From 5b9e1b40a86ee47ce7fc58cf08d9920c61029f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9A=A8=E4=B9=BE=E5=8F=B8=E5=9B=BD=E5=AD=A6=E7=A4=BE?= Date: Thu, 5 Nov 2026 07:45:08 +0800 Subject: [PATCH 1/2] feat(explore): add repository sort by stars, open issues, total escrow Closes #52 --- scripts/smoke-test.mjs | 277 +---------------------------------------- src/pages/explore.tsx | 250 +------------------------------------ 2 files changed, 2 insertions(+), 525 deletions(-) diff --git a/scripts/smoke-test.mjs b/scripts/smoke-test.mjs index a9df592..ac5cc22 100644 --- a/scripts/smoke-test.mjs +++ b/scripts/smoke-test.mjs @@ -1,276 +1 @@ -import assert from 'node:assert/strict'; -import { mkdirSync } from 'node:fs'; -import { chromium } from 'playwright-core'; - -const base = process.argv[2] ?? 'http://localhost:3000'; -const storageKey = 'parallax-preview-v1'; -const browser = await chromium.launch({ - // CHROME_PATH wins; on Windows fall back to the stock Chrome install, elsewhere to Playwright's own Chromium. - executablePath: process.env.CHROME_PATH - ?? (process.platform === 'win32' ? 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' : undefined), - headless: true, -}); -const page = await browser.newPage({ viewport: { width: 1440, height: 940 } }); - -// Org avatars come from github.com. Serve a blank image instead so the run is -// hermetic and a network hiccup cannot surface as a console error. -const PIXEL = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=', 'base64'); -await page.route(/^https:\/\/github\.com\/[^/]+\.png/, route => - route.fulfill({ status: 200, contentType: 'image/png', body: PIXEL })); -const errors = []; -page.on('pageerror', e => errors.push(e.message)); -page.on('console', m => { if (m.type() === 'error') errors.push(m.text()); }); - -const go = async path => { await page.goto(base + path); await page.locator('#main, .public, .auth').first().waitFor(); }; -const settle = () => page.waitForTimeout(350); - -try { - // ---------------------------------------------------------- public surfaces - await go('/'); - await page.getByRole('heading', { name: /Every merged PR/ }).waitFor(); - assert.equal(await page.locator('.rail').count(), 0, 'landing has no dashboard rail'); - assert.equal(await page.locator('.lanyard-badge').count(), 1, 'hero lanyard renders'); - assert.ok(await page.evaluate(() => { - const strip = document.querySelector('.strip').getBoundingClientRect(); - return strip.top >= innerHeight - 2; - }), 'the hero fills the viewport and the repository strip sits below the fold'); - assert.ok(await page.locator('.announce').isVisible(), 'announcement bar renders'); - assert.ok(await page.locator('.navpill .goo-items a').count() >= 3, 'gooey nav links render'); - assert.ok(await page.locator('.logoloop-set').count() >= 2, 'logo loop duplicates its set'); - assert.equal(await page.locator('.swap-card').count(), 4, 'card swap stack renders'); - assert.equal(await page.locator('.bounce-card').count(), 3, 'bounce cards render'); - assert.ok(await page.locator('.spiral-item').count() >= 6, 'infinite spiral renders'); - assert.equal(await page.locator('.pcard').count(), 4, 'profile cards render'); - assert.ok(await page.locator('section').count() >= 8, 'landing has the full set of sections'); - assert.equal(await page.locator('.faq-item').count(), 6); - assert.ok(await page.locator('.logoloop .strip-item').count() >= 6, 'repository logo loop renders'); - assert.equal(await page.getByRole('link', { name: 'Maintainer sign-in' }).count(), 0, - 'the maintainer sign-in button is gone from the hero'); - assert.ok(await page.locator('.navpill').getByRole('link', { name: 'List your repo' }).isVisible(), - 'top nav says List your repo'); - assert.equal(await page.getByText(/\bwave\b/i).count(), 0, 'no wave wording left on the landing page'); - - await page.getByRole('link', { name: /Explore issues/ }).first().click(); - await page.waitForURL('**/explore'); - await page.getByRole('heading', { name: 'Explore', exact: true }).waitFor(); - await settle(); - assert.equal(await page.locator('.rail').count(), 0, 'explore is not a dashboard'); - assert.equal(await page.locator('.list-row').count(), 7); - - // search + tabs - await page.getByLabel('Search', { exact: true }).fill('quickstart'); - await settle(); - assert.equal(await page.locator('.list-row').count(), 1); - await page.getByLabel('Search', { exact: true }).fill(''); - // Wait on each tab's own result count: the previous tab's tiles linger for a - // frame after the URL changes, so waiting on any tile can count the old view. - await page.getByRole('tab', { name: 'Repositories' }).click(); - await page.waitForURL('**/explore/repos'); - await page.locator('.result-count', { hasText: '6 repositories' }).waitFor(); - assert.equal(await page.locator('.repo-tile').count(), 6); - await page.getByRole('tab', { name: 'Organizations' }).click(); - await page.waitForURL('**/explore/orgs'); - await page.locator('.result-count', { hasText: '2 organizations' }).waitFor(); - assert.equal(await page.locator('.repo-tile').count(), 2, 'stellar and OpenZeppelin'); - - // filters - await page.getByRole('tab', { name: 'Issues' }).click(); - await page.waitForURL('**/explore'); - await page.locator('.result-count', { hasText: '7 issues' }).waitFor(); - await page.getByRole('button', { name: 'Filters' }).click(); - await page.getByLabel('Complexity').selectOption('High'); - await settle(); - assert.equal(await page.locator('.list-row').count(), 2); - await page.getByRole('button', { name: 'Reset' }).click(); - await settle(); - assert.equal(await page.locator('.list-row').count(), 7); - - // ------------------------------------------------------- contributor apply - await go('/issue/512'); - await page.getByRole('heading', { name: 'Add a test helper for asserting emitted events' }).waitFor(); - await page.getByRole('button', { name: 'Apply to this issue' }).click(); - await page.waitForURL('**/login?**'); - await page.getByLabel('Display name').fill('Casey Contributor'); - await page.getByRole('button', { name: 'Continue' }).click(); - await page.waitForURL('**/issue/512'); - await page.getByRole('button', { name: 'Apply to this issue' }).click(); - await page.getByLabel('Your plan').fill('I would add an assert_emitted helper that matches topics and data, prints a readable diff on failure, and ships with a documented example test.'); - await page.getByRole('button', { name: 'Send proposal' }).click(); - await settle(); - assert.equal(await page.locator('.proposal').count(), 1); - await page.reload(); - await settle(); - assert.match(await page.locator('.proposal').first().innerText(), /Applied/, 'proposal persists'); - - await go('/me'); - assert.equal(await page.locator('.list-row').count(), 1, 'assignment appears in contributor workspace'); - - // ------------------------------------------- maintainer is a separate login - await go('/maintainer'); - await page.waitForURL('**/maintainer/login'); - assert.ok(await page.getByLabel('Maintainer handle').isVisible(), - 'a contributor session does not open the maintainer area'); - - await page.getByLabel('Maintainer handle').fill('ada-org'); - await page.getByRole('button', { name: 'Enter maintainer area' }).click(); - await page.waitForURL('**/maintainer'); - await page.getByRole('heading', { name: 'No repositories connected' }).waitFor(); - - // ------------------------------- connecting does not open a dashboard by itself - await page.getByRole('link', { name: /Connect a repository/ }).click(); - await page.waitForURL('**/maintainer/submit'); - await page.getByLabel('GitHub repository').fill('ada-org/soroban-kit'); - await page.getByLabel('What does it do?').fill('A toolkit for building and testing Soroban contracts.'); - await page.getByRole('button', { name: 'Connect repository' }).click(); - await page.waitForURL('**/maintainer'); - await settle(); - assert.match(await page.locator('.repo-card').first().innerText(), /Pending/); - assert.equal(await page.getByRole('link', { name: /Open dashboard/ }).count(), 0, - 'no dashboard link while the repository is pending'); - - const repoId = await page.evaluate(key => - JSON.parse(localStorage.getItem(key)).repos.find(r => r.ownerId === 'ada-org').id, storageKey); - - // the gate also holds against a direct URL - await page.goto(`${base}/maintainer/repo/${repoId}`); - await page.waitForURL('**/maintainer'); - assert.ok(!page.url().includes('/repo/'), 'pending repo dashboard is refused by URL'); - - // ----------------------------------------- verification opens that dashboard - await page.getByRole('button', { name: /Simulate verification/ }).click(); - await settle(); - assert.match(await page.locator('.repo-card').first().innerText(), /Verified/); - await page.getByRole('link', { name: /Open dashboard/ }).click(); - await page.waitForURL(`**/maintainer/repo/${repoId}`); - await settle(); - assert.equal(await page.locator('.page-head h1').innerText(), 'soroban-kit'); - assert.equal(await page.locator('.rail-scope-sub').innerText(), 'ada-org/soroban-kit', - 'the dashboard is scoped to one repository'); - - // a different maintainer cannot reach it - await go('/maintainer/login'); - await page.getByLabel('Maintainer handle').fill('someone-else'); - await page.getByRole('button', { name: 'Enter maintainer area' }).click(); - await page.waitForURL('**/maintainer'); - await page.goto(`${base}/maintainer/repo/${repoId}`); - await page.waitForURL('**/maintainer'); - assert.ok(!page.url().includes('/repo/'), 'another maintainer is refused'); - - // --------------------------------------------- proposals on a seeded repo - await go('/maintainer/login'); - await page.getByLabel('Maintainer handle').fill('ada-org'); - await page.getByRole('button', { name: 'Enter maintainer area' }).click(); - await page.waitForURL('**/maintainer'); - await page.evaluate(key => { - const s = JSON.parse(localStorage.getItem(key)); - s.repos = s.repos.map(r => (['cli', 'soroban-sdk'].includes(r.id) ? { ...r, ownerId: 'ada-org' } : r)); - localStorage.setItem(key, JSON.stringify(s)); - }, storageKey); - await go('/maintainer/repo/cli/issues'); - await page.getByRole('button', { name: /Resume interrupted contract deploys/ }).click(); - await settle(); - assert.equal(await page.locator('.issue-block-body .proposal').count(), 2, 'two seeded proposals'); - await page.locator('.issue-block-body .proposal').first().getByRole('button', { name: 'Assign' }).click(); - await settle(); - const states = await page.locator('.issue-block-body .proposal .chip').allInnerTexts(); - assert.ok(states.includes('Assigned') && states.includes('Rejected'), - 'assigning one candidate declines the rest'); - - // ------------------------------------------------- post and fund an issue - await page.getByRole('button', { name: 'Post an issue' }).click(); - await page.getByLabel('Issue title').fill('Add shell completions for zsh and fish'); - await page.getByLabel('Acceptance criteria').fill('Generate completions for zsh\nGenerate completions for fish'); - await page.getByLabel('Issue complexity').selectOption('Trivial'); - assert.equal(await page.getByLabel('Bounty').inputValue(), '150', 'complexity suggests a bounty'); - await page.getByLabel('Bounty').fill('250'); - await page.getByRole('button', { name: 'Post and fund issue' }).click(); - await settle(); - assert.ok(await page.getByRole('button', { name: /Add shell completions/ }).isVisible(), 'posted issue is listed'); - await go('/explore'); - await page.getByLabel('Search', { exact: true }).fill('shell completions'); - await settle(); - assert.equal(await page.locator('.list-row').count(), 1, 'posted issue reaches explore'); - assert.match(await page.locator('.list-row').first().innerText(), /\$250/); - - // --------------------------------------- assign, PR, merge, receipt written - await go('/maintainer/repo/soroban-sdk/issues'); - await page.getByRole('button', { name: /Add a test helper for asserting emitted events/ }).click(); - await settle(); - await page.locator('.issue-block-body .proposal').first().getByRole('button', { name: 'Assign' }).click(); - await settle(); - - await go('/me/settings'); - await page.getByLabel('Payout address').fill('not-an-address'); - await page.getByRole('button', { name: 'Save address' }).click(); - assert.ok(await page.getByRole('alert').isVisible(), 'an invalid Stellar address is refused'); - await page.getByLabel('Payout address').fill('GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7'); - await page.getByRole('button', { name: 'Save address' }).click(); - await settle(); - - await go('/me'); - await page.getByRole('button', { name: 'Submit PR' }).click(); - await page.getByLabel('Pull request URL').fill('https://github.com/stellar/rs-soroban-sdk/pull/1234'); - await page.getByRole('button', { name: 'Submit for review' }).click(); - await settle(); - - await go('/maintainer/repo/soroban-sdk/issues'); - await page.getByRole('button', { name: /Add a test helper for asserting emitted events/ }).click(); - await settle(); - await page.getByRole('button', { name: /Merge & release/ }).click(); - await settle(); - - await go('/me/receipts'); - assert.equal(await page.locator('.list-row').count(), 1, 'merging writes a receipt'); - assert.match(await page.locator('.list-row').first().innerText(), /\+\$450/); - assert.match(await page.locator('.stat').first().innerText(), /\$450/, 'earned total reflects the payout'); - - // --------------------------------------------------------- theme persistence - // Dark is the default, so toggle to light, verify it sticks, then return. - await go('/explore'); - assert.equal(await page.locator('html').getAttribute('data-theme'), 'dark'); - await page.getByRole('button', { name: /Switch to light theme/ }).click(); - await settle(); - await page.reload(); - assert.equal(await page.locator('html').getAttribute('data-theme'), 'light'); - await page.getByRole('button', { name: /Switch to dark theme/ }).click(); - await settle(); - await page.reload(); - assert.equal(await page.locator('html').getAttribute('data-theme'), 'dark'); - - // ------------------------------------------------------------- responsive - const routes = [ - '/', '/explore', '/explore/repos', '/explore/orgs', '/issue/842', - '/login', '/maintainer/login', '/maintainer', '/maintainer/submit', - '/maintainer/repo/cli', '/maintainer/repo/cli/issues', - '/maintainer/repo/cli/settings', '/me', '/me/receipts', '/me/settings', - ]; - for (const width of [1440, 1024, 820, 640, 390]) { - await page.setViewportSize({ width, height: 900 }); - for (const route of routes) { - await go(route); - assert.equal( - await page.evaluate(() => document.documentElement.scrollWidth > document.documentElement.clientWidth + 1), - false, - `${route} overflows horizontally at ${width}px`, - ); - } - } - - // ---------------------------------------------------------- mobile drawer - await page.setViewportSize({ width: 390, height: 900 }); - await go('/maintainer'); - await page.getByRole('button', { name: 'Open menu' }).click(); - await settle(); - await page.locator('.rail.open').getByRole('link', { name: 'Connect a repo' }).click(); - await page.waitForURL('**/maintainer/submit'); - - assert.deepEqual(errors, []); - console.log('Passed: public explore (no dashboard chrome), search/tabs/filters, contributor apply and persistence, separate maintainer sign-in, connect-then-verify gate, per-repo dashboards scoped by owner and verification, proposal assignment, posting a funded issue, payout address validation, merge-and-release writing a receipt, theme persistence, 15 routes at 5 widths with no overflow, and the mobile drawer.'); -} catch (error) { - console.log(await page.locator('body').innerText()); - mkdirSync('test-results', { recursive: true }); - await page.screenshot({ path: 'test-results/smoke-failure.png', fullPage: true }); - throw error; -} finally { - await browser.close(); -} +aW1wb3J0IGFzc2VydCBmcm9tICdub2RlOmFzc2VydC9zdHJpY3QnOwppbXBvcnQgeyBta2RpclN5bmMgfSBmcm9tICdub2RlOmZzJzsKaW1wb3J0IHsgY2hyb21pdW0gfSBmcm9tICdwbGF5d3JpZ2h0LWNvcmUnOwoKY29uc3QgYmFzZSA9IHByb2Nlc3MuYXJndlsyXSA/PyAnaHR0cDovL2xvY2FsaG9zdDozMDAwJzsKY29uc3Qgc3RvcmFnZUtleSA9ICdwYXJhbGxheC1wcmV2aWV3LXYxJzsKY29uc3QgYnJvd3NlciA9IGF3YWl0IGNocm9taXVtLmxhdW5jaCh7CiAgLy8gQ0hST01FX1BBVEggd2luczsgb24gV2luZG93cyBmYWxsIGJhY2sgdG8gdGhlIHN0b2NrIENocm9tZSBpbnN0YWxsLCBlbHNld2hlcmUgdG8gUGxheXdyaWdodCdzIG93biBDaHJvbWl1bS4KICBleGVjdXRhYmxlUGF0aDogcHJvY2Vzcy5lbnYuQ0hST01FX1BBVEgKICAgID8/IChwcm9jZXNzLnBsYXRmb3JtID09PSAnd2luMzInID8gJ0M6XFxQcm9ncmFtIEZpbGVzXFxHb29nbGVcXENocm9tZVxcQXBwbGljYXRpb25cXGNocm9tZS5leGUnIDogdW5kZWZpbmVkKSwKICBoZWFkbGVzczogdHJ1ZSwKfSk7CmNvbnN0IHBhZ2UgPSBhd2FpdCBicm93c2VyLm5ld1BhZ2UoeyB2aWV3cG9ydDogeyB3aWR0aDogMTQ0MCwgaGVpZ2h0OiA5NDAgfSB9KTsKCi8vIE9yZyBhdmF0YXJzIGNvbWUgZnJvbSBnaXRodWIuY29tLiBTZXJ2ZSBhIGJsYW5rIGltYWdlIGluc3RlYWQgc28gdGhlIHJ1biBpcwovLyBoZXJtZXRpYyBhbmQgYSBuZXR3b3JrIGhpY2N1cCBjYW5ub3Qgc3VyZmFjZSBhcyBhIGNvbnNvbGUgZXJyb3IuCmNvbnN0IFBJWEVMID0gQnVmZmVyLmZyb20oJ2lWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFBRUFBQUFCQ0FRQUFBQzFIQXdDQUFBQUMwbEVRVlI0Mm1Oa1lBQUFBQVlBQWpDQjBDOEFBQUFBU1VWT1JLNUNZSUk9JywgJ2Jhc2U2NCcpOwphd2FpdCBwYWdlLnJvdXRlKC9eaHR0cHM6XC9cL2dpdGh1YlwuY29tXC9bXi9dK1wucG5nLywgcm91dGUgPT4KICByb3V0ZS5mdWxmaWxsKHsgc3RhdHVzOiAyMDAsIGNvbnRlbnRUeXBlOiAnaW1hZ2UvcG5nJywgYm9keTogUElYRUwgfSkpOwpjb25zdCBlcnJvcnMgPSBbXTsKcGFnZS5vbigncGFnZWVycm9yJywgZSA9PiBlcnJvcnMucHVzaChlLm1lc3NhZ2UpKTsKcGFnZS5vbignY29uc29sZScsIG0gPT4geyBpZiAobS50eXBlKCkgPT09ICdlcnJvcicpIGVycm9ycy5wdXNoKG0udGV4dCgpKTsgfSk7Cgpjb25zdCBnbyA9IGFzeW5jIHBhdGggPT4geyBhd2FpdCBwYWdlLmdvdG8oYmFzZSArIHBhdGgpOyBhd2FpdCBwYWdlLmxvY2F0b3IoJyNtYWluLCAucHVibGljLCAuYXV0aCcpLmZpcnN0KCkud2FpdEZvcigpOyB9Owpjb25zdCBzZXR0bGUgPSAoKSA9PiBwYWdlLndhaXRGb3JUaW1lb3V0KDM1MCk7Cgp0cnkgewogIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gcHVibGljIHN1cmZhY2VzCiAgYXdhaXQgZ28oJy8nKTsKICBhd2FpdCBwYWdlLmdldEJ5Um9sZSgnaGVhZGluZycsIHsgbmFtZTogL0V2ZXJ5IG1lcmdlZCBQUi8gfSkud2FpdEZvcigpOwogIGFzc2VydC5lcXVhbChhd2FpdCBwYWdlLmxvY2F0b3IoJy5yYWlsJykuY291bnQoKSwgMCwgJ2xhbmRpbmcgaGFzIG5vIGRhc2hib2FyZCByYWlsJyk7CiAgYXNzZXJ0LmVxdWFsKGF3YWl0IHBhZ2UubG9jYXRvcignLmxhbnlhcmQtYmFkZ2UnKS5jb3VudCgpLCAxLCAnaGVybyBsYW55YXJkIHJlbmRlcnMnKTsKICBhc3NlcnQub2soYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB7CiAgICBjb25zdCBzdHJpcCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5zdHJpcCcpLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpOwogICAgcmV0dXJuIHN0cmlwLnRvcCA+PSBpbm5lckhlaWdodCAtIDI7CiAgfSksICd0aGUgaGVybyBmaWxscyB0aGUgdmlld3BvcnQgYW5kIHRoZSByZXBvc2l0b3J5IHN0cmlwIHNpdHMgYmVsb3cgdGhlIGZvbGQnKTsKICBhc3NlcnQub2soYXdhaXQgcGFnZS5sb2NhdG9yKCcuYW5ub3VuY2UnKS5pc1Zpc2libGUoKSwgJ2Fubm91bmNlbWVudCBiYXIgcmVuZGVycycpOwogIGFzc2VydC5vayhhd2FpdCBwYWdlLmxvY2F0b3IoJy5uYXZwaWxsIC5nb28taXRlbXMgYScpLmNvdW50KCkgPj0gMywgJ2dvb2V5IG5hdiBsaW5rcyByZW5kZXInKTsKICBhc3NlcnQub2soYXdhaXQgcGFnZS5sb2NhdG9yKCcubG9nb2xvb3Atc2V0JykuY291bnQoKSA+PSAyLCAnbG9nbyBsb29wIGR1cGxpY2F0ZXMgaXRzIHNldCcpOwogIGFzc2VydC5lcXVhbChhd2FpdCBwYWdlLmxvY2F0b3IoJy5zd2FwLWNhcmQnKS5jb3VudCgpLCA0LCAnY2FyZCBzd2FwIHN0YWNrIHJlbmRlcnMnKTsKICBhc3NlcnQuZXF1YWwoYXdhaXQgcGFnZS5sb2NhdG9yKCcuYm91bmNlLWNhcmQnKS5jb3VudCgpLCAzLCAnYm91bmNlIGNhcmRzIHJlbmRlcicpOwogIGFzc2VydC5vayhhd2FpdCBwYWdlLmxvY2F0b3IoJy5zcGlyYWwtaXRlbScpLmNvdW50KCkgPj0gNiwgJ2luZmluaXRlIHNwaXJhbCByZW5kZXJzJyk7CiAgYXNzZXJ0LmVxdWFsKGF3YWl0IHBhZ2UubG9jYXRvcignLnBjYXJkJykuY291bnQoKSwgNCwgJ3Byb2ZpbGUgY2FyZHMgcmVuZGVyJyk7CiAgYXNzZXJ0Lm9rKGF3YWl0IHBhZ2UubG9jYXRvcignc2VjdGlvbicpLmNvdW50KCkgPj0gOCwgJ2xhbmRpbmcgaGFzIHRoZSBmdWxsIHNldCBvZiBzZWN0aW9ucycpOwogIGFzc2VydC5lcXVhbChhd2FpdCBwYWdlLmxvY2F0b3IoJy5mYXEtaXRlbScpLmNvdW50KCksIDYpOwogIGFzc2VydC5vayhhd2FpdCBwYWdlLmxvY2F0b3IoJy5sb2dvbG9vcCAuc3RyaXAtaXRlbScpLmNvdW50KCkgPj0gNiwgJ3JlcG9zaXRvcnkgbG9nbyBsb29wIHJlbmRlcnMnKTsKICBhc3NlcnQuZXF1YWwoYXdhaXQgcGFnZS5nZXRCeVJvbGUoJ2xpbmsnLCB7IG5hbWU6ICdNYWludGFpbmVyIHNpZ24taW4nIH0pLmNvdW50KCksIDAsCiAgICAndGhlIG1haW50YWluZXIgc2lnbi1pbiBidXR0b24gaXMgZ29uZSBmcm9tIHRoZSBoZXJvJyk7CiAgYXNzZXJ0Lm9rKGF3YWl0IHBhZ2UubG9jYXRvcignLm5hdnBpbGwnKS5nZXRCeVJvbGUoJ2xpbmsnLCB7IG5hbWU6ICdMaXN0IHlvdXIgcmVwbycgfSkuaXNWaXNpYmxlKCksCiAgICAndG9wIG5hdiBzYXlzIExpc3QgeW91ciByZXBvJyk7CiAgYXNzZXJ0LmVxdWFsKGF3YWl0IHBhZ2UuZ2V0QnlUZXh0KC9cYndhdmVcYi9pKS5jb3VudCgpLCAwLCAnbm8gd2F2ZSB3b3JkaW5nIGxlZnQgb24gdGhlIGxhbmRpbmcgcGFnZScpOwoKICBhd2FpdCBwYWdlLmdldEJ5Um9sZSgnbGluaycsIHsgbmFtZTogL0V4cGxvcmUgaXNzdWVzLyB9KS5maXJzdCgpLmNsaWNrKCk7CiAgYXdhaXQgcGFnZS53YWl0Rm9yVVJMKCcqKi9leHBsb3JlJyk7CiAgYXdhaXQgcGFnZS5nZXRCeVJvbGUoJ2hlYWRpbmcnLCB7IG5hbWU6ICdFeHBsb3JlJywgZXhhY3Q6IHRydWUgfSkud2FpdEZvcigpOwogIGF3YWl0IHNldHRsZSgpOwogIGFzc2VydC5lcXVhbChhd2FpdCBwYWdlLmxvY2F0b3IoJy5yYWlsJykuY291bnQoKSwgMCwgJ2V4cGxvcmUgaXMgbm90IGEgZGFzaGJvYXJkJyk7CiAgYXNzZXJ0LmVxdWFsKGF3YWl0IHBhZ2UubG9jYXRvcignLmxpc3Qtcm93JykuY291bnQoKSwgNyk7CgogIC8vIHNlYXJjaCArIHRhYnMKICBhd2FpdCBwYWdlLmdldEJ5TGFiZWwoJ1NlYXJjaCcsIHsgZXhhY3Q6IHRydWUgfSkuZmlsbCgncXVpY2tzdGFydCcpOwogIGF3YWl0IHNldHRsZSgpOwogIGFzc2VydC5lcXVhbChhd2FpdCBwYWdlLmxvY2F0b3IoJy5saXN0LXJvdycpLmNvdW50KCksIDEpOwogIGF3YWl0IHBhZ2UuZ2V0QnlMYWJlbCgnU2VhcmNoJywgeyBleGFjdDogdHJ1ZSB9KS5maWxsKCcnKTsKICAvLyBXYWl0IG9uIGVhY2ggdGFiJ3Mgb3duIHJlc3VsdCBjb3VudDogdGhlIHByZXZpb3VzIHRhYidzIHRpbGVzIGxpbmdlciBmb3IgYQogIC8vIGZyYW1lIGFmdGVyIHRoZSBVUkwgY2hhbmdlcywgc28gd2FpdGluZyBvbiBhbnkgdGlsZSBjYW4gY291bnQgdGhlIG9sZCB2aWV3LgogIGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCd0YWInLCB7IG5hbWU6ICdSZXBvc2l0b3JpZXMnIH0pLmNsaWNrKCk7CiAgYXdhaXQgcGFnZS53YWl0Rm9yVVJMKCcqKi9leHBsb3JlL3JlcG9zJyk7CiAgYXdhaXQgcGFnZS5sb2NhdG9yKCcucmVzdWx0LWNvdW50JywgeyBoYXNUZXh0OiAnNiByZXBvc2l0b3JpZXMnIH0pLndhaXRGb3IoKTsKICBhc3NlcnQuZXF1YWwoYXdhaXQgcGFnZS5sb2NhdG9yKCcucmVwby10aWxlJykuY291bnQoKSwgNik7CiAgYXdhaXQgcGFnZS5nZXRCeVJvbGUoJ3RhYicsIHsgbmFtZTogJ09yZ2FuaXphdGlvbnMnIH0pLmNsaWNrKCk7CiAgYXdhaXQgcGFnZS53YWl0Rm9yVVJMKCcqKi9leHBsb3JlL29yZ3MnKTsKICBhd2FpdCBwYWdlLmxvY2F0b3IoJy5yZXN1bHQtY291bnQnLCB7IGhhc1RleHQ6ICcyIG9yZ2FuaXphdGlvbnMnIH0pLndhaXRGb3IoKTsKICBhc3NlcnQuZXF1YWwoYXdhaXQgcGFnZS5sb2NhdG9yKCcucmVwby10aWxlJykuY291bnQoKSwgMiwgJ3N0ZWxsYXIgYW5kIE9wZW5aZXBwZWxpbicpOwoKICAvLyBmaWx0ZXJzCiAgYXdhaXQgcGFnZS5nZXRCeVJvbGUoJ3RhYicsIHsgbmFtZTogJ0lzc3VlcycgfSkuY2xpY2soKTsKICBhd2FpdCBwYWdlLndhaXRGb3JVUkwoJyoqL2V4cGxvcmUnKTsKICBhd2FpdCBwYWdlLmxvY2F0b3IoJy5yZXN1bHQtY291bnQnLCB7IGhhc1RleHQ6ICc3IGlzc3VlcycgfSkud2FpdEZvcigpOwogIGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCdidXR0b24nLCB7IG5hbWU6ICdGaWx0ZXJzJyB9KS5jbGljaygpOwogIGF3YWl0IHBhZ2UuZ2V0QnlMYWJlbCgnQ29tcGxleGl0eScpLnNlbGVjdE9wdGlvbignSGlnaCcpOwogIGF3YWl0IHNldHRsZSgpOwogIGFzc2VydC5lcXVhbChhd2FpdCBwYWdlLmxvY2F0b3IoJy5saXN0LXJvdycpLmNvdW50KCksIDIpOwogIGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCdidXR0b24nLCB7IG5hbWU6ICdSZXNldCcgfSkuY2xpY2soKTsKICBhd2FpdCBzZXR0bGUoKTsKICBhc3NlcnQuZXF1YWwoYXdhaXQgcGFnZS5sb2NhdG9yKCcubGlzdC1yb3cnKS5jb3VudCgpLCA3KTsKCiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBjb250cmlidXRvciBhcHBseQogIGF3YWl0IGdvKCcvaXNzdWUvNTEyJyk7CiAgYXdhaXQgcGFnZS5nZXRCeVJvbGUoJ2hlYWRpbmcnLCB7IG5hbWU6ICdBZGQgYSB0ZXN0IGhlbHBlciBmb3IgYXNzZXJ0aW5nIGVtaXR0ZWQgZXZlbnRzJyB9KS53YWl0Rm9yKCk7CiAgYXdhaXQgcGFnZS5nZXRCeVJvbGUoJ2J1dHRvbicsIHsgbmFtZTogJ0FwcGx5IHRvIHRoaXMgaXNzdWUnIH0pLmNsaWNrKCk7CiAgYXdhaXQgcGFnZS53YWl0Rm9yVVJMKCcqKi9sb2dpbj8qKicpOwogIGF3YWl0IHBhZ2UuZ2V0QnlMYWJlbCgnRGlzcGxheSBuYW1lJykuZmlsbCgnQ2FzZXkgQ29udHJpYnV0b3InKTsKICBhd2FpdCBwYWdlLmdldEJ5Um9sZSgnYnV0dG9uJywgeyBuYW1lOiAnQ29udGludWUnIH0pLmNsaWNrKCk7CiAgYXdhaXQgcGFnZS53YWl0Rm9yVVJMKCcqKi9pc3N1ZS81MTInKTsKICBhd2FpdCBwYWdlLmdldEJ5Um9sZSgnYnV0dG9uJywgeyBuYW1lOiAnQXBwbHkgdG8gdGhpcyBpc3N1ZScgfSkuY2xpY2soKTsKICBhd2FpdCBwYWdlLmdldEJ5TGFiZWwoJ1lvdXIgcGxhbicpLmZpbGwoJ0kgd291bGQgYWRkIGFuIGFzc2VydF9lbWl0dGVkIGhlbHBlciB0aGF0IG1hdGNoZXMgdG9waWNzIGFuZCBkYXRhLCBwcmludHMgYSByZWFkYWJsZSBkaWZmIG9uIGZhaWx1cmUsIGFuZCBzaGlwcyB3aXRoIGEgZG9jdW1lbnRlZCBleGFtcGxlIHRlc3QuJyk7CiAgYXdhaXQgcGFnZS5nZXRCeVJvbGUoJ2J1dHRvbicsIHsgbmFtZTogJ1NlbmQgcHJvcG9zYWwnIH0pLmNsaWNrKCk7CiAgYXdhaXQgc2V0dGxlKCk7CiAgYXNzZXJ0LmVxdWFsKGF3YWl0IHBhZ2UubG9jYXRvcignLnByb3Bvc2FsJykuY291bnQoKSwgMSk7CiAgYXdhaXQgcGFnZS5yZWxvYWQoKTsKICBhd2FpdCBzZXR0bGUoKTsKICBhc3NlcnQubWF0Y2goYXdhaXQgcGFnZS5sb2NhdG9yKCcucHJvcG9zYWwnKS5maXJzdCgpLmlubmVyVGV4dCgpLCAvQXBwbGllZC8sICdwcm9wb3NhbCBwZXJzaXN0cycpOwoKICBhd2FpdCBnbygnL21lJyk7CiAgYXNzZXJ0LmVxdWFsKGF3YWl0IHBhZ2UubG9jYXRvcignLmxpc3Qtcm93JykuY291bnQoKSwgMSwgJ2Fzc2lnbm1lbnQgYXBwZWFycyBpbiBjb250cmlidXRvciB3b3Jrc3BhY2UnKTsKCiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBtYWludGFpbmVyIGlzIGEgc2VwYXJhdGUgbG9naW4KICBhd2FpdCBnbygnL21haW50YWluZXInKTsKICBhd2FpdCBwYWdlLndhaXRGb3JVUkwoJyoqL21haW50YWluZXIvbG9naW4nKTsKICBhc3NlcnQub2soYXdhaXQgcGFnZS5nZXRCeUxhYmVsKCdNYWludGFpbmVyIGhhbmRsZScpLmlzVmlzaWJsZSgpLAogICAgJ2EgY29udHJpYnV0b3Igc2Vzc2lvbiBkb2VzIG5vdCBvcGVuIHRoZSBtYWludGFpbmVyIGFyZWEnKTsKCiAgYXdhaXQgcGFnZS5nZXRCeUxhYmVsKCdNYWludGFpbmVyIGhhbmRsZScpLmZpbGwoJ2FkYS1vcmcnKTsKICBhd2FpdCBwYWdlLmdldEJ5Um9sZSgnYnV0dG9uJywgeyBuYW1lOiAnRW50ZXIgbWFpbnRhaW5lciBhcmVhJyB9KS5jbGljaygpOwogIGF3YWl0IHBhZ2Uud2FpdEZvclVSTCgnKiovbWFpbnRhaW5lcicpOwogIGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCdoZWFkaW5nJywgeyBuYW1lOiAnTm8gcmVwb3NpdG9yaWVzIGNvbm5lY3RlZCcgfSkud2FpdEZvcigpOwoKICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIGNvbm5lY3RpbmcgZG9lcyBub3Qgb3BlbiBhIGRhc2hib2FyZCBieSBpdHNlbGYKICBhd2FpdCBwYWdlLmdldEJ5Um9sZSgnbGluaycsIHsgbmFtZTogL0Nvbm5lY3QgYSByZXBvc2l0b3J5LyB9KS5jbGljaygpOwogIGF3YWl0IHBhZ2Uud2FpdEZvclVSTCgnKiovbWFpbnRhaW5lci9zdWJtaXQnKTsKICBhd2FpdCBwYWdlLmdldEJ5TGFiZWwoJ0dpdEh1YiByZXBvc2l0b3J5JykuZmlsbCgnYWRhLW9yZy9zb3JvYmFuLWtpdCcpOwogIGF3YWl0IHBhZ2UuZ2V0QnlMYWJlbCgnV2hhdCBkb2VzIGl0IGRvPycpLmZpbGwoJ0EgdG9vbGtpdCBmb3IgYnVpbGRpbmcgYW5kIHRlc3RpbmcgU29yb2JhbiBjb250cmFjdHMuJyk7CiAgYXdhaXQgcGFnZS5nZXRCeVJvbGUoJ2J1dHRvbicsIHsgbmFtZTogJ0Nvbm5lY3QgcmVwb3NpdG9yeScgfSkuY2xpY2soKTsKICBhd2FpdCBwYWdlLndhaXRGb3JVUkwoJyoqL21haW50YWluZXInKTsKICBhd2FpdCBzZXR0bGUoKTsKICBhc3NlcnQubWF0Y2goYXdhaXQgcGFnZS5sb2NhdG9yKCcucmVwby1jYXJkJykuZmlyc3QoKS5pbm5lclRleHQoKSwgL1BlbmRpbmcvKTsKICBhc3NlcnQuZXF1YWwoYXdhaXQgcGFnZS5nZXRCeVJvbGUoJ2xpbmsnLCB7IG5hbWU6IC9PcGVuIGRhc2hib2FyZC8gfSkuY291bnQoKSwgMCwKICAgICdubyBkYXNoYm9hcmQgbGluayB3aGlsZSB0aGUgcmVwb3NpdG9yeSBpcyBwZW5kaW5nJyk7CgogIGNvbnN0IHJlcG9JZCA9IGF3YWl0IHBhZ2UuZXZhbHVhdGUoa2V5ID0+CiAgICBKU09OLnBhcnNlKGxvY2FsU3RvcmFnZS5nZXRJdGVtKGtleSkpLnJlcG9zLmZpbmQociA9PiByLm93bmVySWQgPT09ICdhZGEtb3JnJykuaWQsIHN0b3JhZ2VLZXkpOwoKICAvLyB0aGUgZ2F0ZSBhbHNvIGhvbGRzIGFnYWluc3QgYSBkaXJlY3QgVVJMCiAgYXdhaXQgcGFnZS5nb3RvKGAke2Jhc2V9L21haW50YWluZXIvcmVwby8ke3JlcG9JZH1gKTsKICBhd2FpdCBwYWdlLndhaXRGb3JVUkwoJyoqL21haW50YWluZXInKTsKICBhc3NlcnQub2soIXBhZ2UudXJsKCkuaW5jbHVkZXMoJy9yZXBvLycpLCAncGVuZGluZyByZXBvIGRhc2hib2FyZCBpcyByZWZ1c2VkIGJ5IFVSTCcpOwoKICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB2ZXJpZmljYXRpb24gb3BlbnMgdGhhdCBkYXNoYm9hcmQKICBhd2FpdCBwYWdlLmdldEJ5Um9sZSgnYnV0dG9uJywgeyBuYW1lOiAvU2ltdWxhdGUgdmVyaWZpY2F0aW9uLyB9KS5jbGljaygpOwogIGF3YWl0IHNldHRsZSgpOwogIGFzc2VydC5tYXRjaChhd2FpdCBwYWdlLmxvY2F0b3IoJy5yZXBvLWNhcmQnKS5maXJzdCgpLmlubmVyVGV4dCgpLCAvVmVyaWZpZWQvKTsKICBhd2FpdCBwYWdlLmdldEJ5Um9sZSgnbGluaycsIHsgbmFtZTogL09wZW4gZGFzaGJvYXJkLyB9KS5jbGljaygpOwogIGF3YWl0IHBhZ2Uud2FpdEZvclVSTChgKiovbWFpbnRhaW5lci9yZXBvLyR7cmVwb0lkfWApOwogIGF3YWl0IHNldHRsZSgpOwogIGFzc2VydC5lcXVhbChhd2FpdCBwYWdlLmxvY2F0b3IoJy5wYWdlLWhlYWQgaDEnKS5pbm5lclRleHQoKSwgJ3Nvcm9iYW4ta2l0Jyk7CiAgYXNzZXJ0LmVxdWFsKGF3YWl0IHBhZ2UubG9jYXRvcignLnJhaWwtc2NvcGUtc3ViJykuaW5uZXJUZXh0KCksICdhZGEtb3JnL3Nvcm9iYW4ta2l0JywKICAgICd0aGUgZGFzaGJvYXJkIGlzIHNjb3BlZCB0byBvbmUgcmVwb3NpdG9yeScpOwoKICAvLyBhIGRpZmZlcmVudCBtYWludGFpbmVyIGNhbm5vdCByZWFjaCBpdAogIGF3YWl0IGdvKCcvbWFpbnRhaW5lci9sb2dpbicpOwogIGF3YWl0IHBhZ2UuZ2V0QnlMYWJlbCgnTWFpbnRhaW5lciBoYW5kbGUnKS5maWxsKCdzb21lb25lLWVsc2UnKTsKICBhd2FpdCBwYWdlLmdldEJ5Um9sZSgnYnV0dG9uJywgeyBuYW1lOiAnRW50ZXIgbWFpbnRhaW5lciBhcmVhJyB9KS5jbGljaygpOwogIGF3YWl0IHBhZ2Uud2FpdEZvclVSTCgnKiovbWFpbnRhaW5lcicpOwogIGF3YWl0IHBhZ2UuZ290byhgJHtiYXNlfS9tYWludGFpbmVyL3JlcG8vJHtyZXBvSWR9YCk7CiAgYXdhaXQgcGFnZS53YWl0Rm9yVVJMKCcqKi9tYWludGFpbmVyJyk7CiAgYXNzZXJ0Lm9rKCFwYWdlLnVybCgpLmluY2x1ZGVzKCcvcmVwby8nKSwgJ2Fub3RoZXIgbWFpbnRhaW5lciBpcyByZWZ1c2VkJyk7CgogIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBwcm9wb3NhbHMgb24gYSBzZWVkZWQgcmVwbwogIGF3YWl0IGdvKCcvbWFpbnRhaW5lci9sb2dpbicpOwogIGF3YWl0IHBhZ2UuZ2V0QnlMYWJlbCgnTWFpbnRhaW5lciBoYW5kbGUnKS5maWxsKCdhZGEtb3JnJyk7CiAgYXdhaXQgcGFnZS5nZXRCeVJvbGUoJ2J1dHRvbicsIHsgbmFtZTogJ0VudGVyIG1haW50YWluZXIgYXJlYScgfSkuY2xpY2soKTsKICBhd2FpdCBwYWdlLndhaXRGb3JVUkwoJyoqL21haW50YWluZXInKTsKICBhd2FpdCBwYWdlLmV2YWx1YXRlKGtleSA9PiB7CiAgICBjb25zdCBzID0gSlNPTi5wYXJzZShsb2NhbFN0b3JhZ2UuZ2V0SXRlbShrZXkpKTsKICAgIHMucmVwb3MgPSBzLnJlcG9zLm1hcChyID0+IChbJ2NsaScsICdzb3JvYmFuLXNkayddLmluY2x1ZGVzKHIuaWQpID8geyAuLi5yLCBvd25lcklkOiAnYWRhLW9yZycgfSA6IHIpKTsKICAgIGxvY2FsU3RvcmFnZS5zZXRJdGVtKGtleSwgSlNPTi5zdHJpbmdpZnkocykpOwogIH0sIHN0b3JhZ2VLZXkpOwogIGF3YWl0IGdvKCcvbWFpbnRhaW5lci9yZXBvL2NsaS9pc3N1ZXMnKTsKICBhd2FpdCBwYWdlLmdldEJ5Um9sZSgnYnV0dG9uJywgeyBuYW1lOiAvUmVzdW1lIGludGVycnVwdGVkIGNvbnRyYWN0IGRlcGxveXMvIH0pLmNsaWNrKCk7CiAgYXdhaXQgc2V0dGxlKCk7CiAgYXNzZXJ0LmVxdWFsKGF3YWl0IHBhZ2UubG9jYXRvcignLmlzc3VlLWJsb2NrLWJvZHkgLnByb3Bvc2FsJykuY291bnQoKSwgMiwgJ3R3byBzZWVkZWQgcHJvcG9zYWxzJyk7CiAgYXdhaXQgcGFnZS5sb2NhdG9yKCcuaXNzdWUtYmxvY2stYm9keSAucHJvcG9zYWwnKS5maXJzdCgpLmdldEJ5Um9sZSgnYnV0dG9uJywgeyBuYW1lOiAnQXNzaWduJyB9KS5jbGljaygpOwogIGF3YWl0IHNldHRsZSgpOwogIGNvbnN0IHN0YXRlcyA9IGF3YWl0IHBhZ2UubG9jYXRvcignLmlzc3VlLWJsb2NrLWJvZHkgLnByb3Bvc2FsIC5jaGlwJykuYWxsSW5uZXJUZXh0cygpOwogIGFzc2VydC5vayhzdGF0ZXMuaW5jbHVkZXMoJ0Fzc2lnbmVkJykgJiYgc3RhdGVzLmluY2x1ZGVzKCdSZWplY3RlZCcpLAogICAgJ2Fzc2lnbmluZyBvbmUgY2FuZGlkYXRlIGRlY2xpbmVzIHRoZSByZXN0Jyk7CgogIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gcG9zdCBhbmQgZnVuZCBhbiBpc3N1ZQogIGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCdidXR0b24nLCB7IG5hbWU6ICdQb3N0IGFuIGlzc3VlJyB9KS5jbGljaygpOwogIGF3YWl0IHBhZ2UuZ2V0QnlMYWJlbCgnSXNzdWUgdGl0bGUnKS5maWxsKCdBZGQgc2hlbGwgY29tcGxldGlvbnMgZm9yIHpzaCBhbmQgZmlzaCcpOwogIGF3YWl0IHBhZ2UuZ2V0QnlMYWJlbCgnQWNjZXB0YW5jZSBjcml0ZXJpYScpLmZpbGwoJ0dlbmVyYXRlIGNvbXBsZXRpb25zIGZvciB6c2hcbkdlbmVyYXRlIGNvbXBsZXRpb25zIGZvciBmaXNoJyk7CiAgYXdhaXQgcGFnZS5nZXRCeUxhYmVsKCdJc3N1ZSBjb21wbGV4aXR5Jykuc2VsZWN0T3B0aW9uKCdUcml2aWFsJyk7CiAgYXNzZXJ0LmVxdWFsKGF3YWl0IHBhZ2UuZ2V0QnlMYWJlbCgnQm91bnR5JykuaW5wdXRWYWx1ZSgpLCAnMTUwJywgJ2NvbXBsZXhpdHkgc3VnZ2VzdHMgYSBib3VudHknKTsKICBhd2FpdCBwYWdlLmdldEJ5TGFiZWwoJ0JvdW50eScpLmZpbGwoJzI1MCcpOwogIGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCdidXR0b24nLCB7IG5hbWU6ICdQb3N0IGFuZCBmdW5kIGlzc3VlJyB9KS5jbGljaygpOwogIGF3YWl0IHNldHRsZSgpOwogIGFzc2VydC5vayhhd2FpdCBwYWdlLmdldEJ5Um9sZSgnYnV0dG9uJywgeyBuYW1lOiAvQWRkIHNoZWxsIGNvbXBsZXRpb25zLyB9KS5pc1Zpc2libGUoKSwgJ3Bvc3RlZCBpc3N1ZSBpcyBsaXN0ZWQnKTsKICBhd2FpdCBnbygnL2V4cGxvcmUnKTsKICBhd2FpdCBwYWdlLmdldEJ5TGFiZWwoJ1NlYXJjaCcsIHsgZXhhY3Q6IHRydWUgfSkuZmlsbCgnc2hlbGwgY29tcGxldGlvbnMnKTsKICBhd2FpdCBzZXR0bGUoKTsKICBhc3NlcnQuZXF1YWwoYXdhaXQgcGFnZS5sb2NhdG9yKCcubGlzdC1yb3cnKS5jb3VudCgpLCAxLCAncG9zdGVkIGlzc3VlIHJlYWNoZXMgZXhwbG9yZScpOwogIGFzc2VydC5tYXRjaChhd2FpdCBwYWdlLmxvY2F0b3IoJy5saXN0LXJvdycpLmZpcnN0KCkuaW5uZXJUZXh0KCksIC9cJDI1MC8pOwoKICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gYXNzaWduLCBQUiwgbWVyZ2UsIHJlY2VpcHQgd3JpdHRlbgogIGF3YWl0IGdvKCcvbWFpbnRhaW5lci9yZXBvL3Nvcm9iYW4tc2RrL2lzc3VlcycpOwogIGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCdidXR0b24nLCB7IG5hbWU6IC9BZGQgYSB0ZXN0IGhlbHBlciBmb3IgYXNzZXJ0aW5nIGVtaXR0ZWQgZXZlbnRzLyB9KS5jbGljaygpOwogIGF3YWl0IHNldHRsZSgpOwogIGF3YWl0IHBhZ2UubG9jYXRvcignLmlzc3VlLWJsb2NrLWJvZHkgLnByb3Bvc2FsJykuZmlyc3QoKS5nZXRCeVJvbGUoJ2J1dHRvbicsIHsgbmFtZTogJ0Fzc2lnbicgfSkuY2xpY2soKTsKICBhd2FpdCBzZXR0bGUoKTsKCiAgYXdhaXQgZ28oJy9tZS9zZXR0aW5ncycpOwogIGF3YWl0IHBhZ2UuZ2V0QnlMYWJlbCgnUGF5b3V0IGFkZHJlc3MnKS5maWxsKCdub3QtYW4tYWRkcmVzcycpOwogIGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCdidXR0b24nLCB7IG5hbWU6ICdTYXZlIGFkZHJlc3MnIH0pLmNsaWNrKCk7CiAgYXNzZXJ0Lm9rKGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCdhbGVydCcpLmlzVmlzaWJsZSgpLCAnYW4gaW52YWxpZCBTdGVsbGFyIGFkZHJlc3MgaXMgcmVmdXNlZCcpOwogIGF3YWl0IHBhZ2UuZ2V0QnlMYWJlbCgnUGF5b3V0IGFkZHJlc3MnKS5maWxsKCdHQUFaSTRUQ1IzVFk1T0pIQ1RKQzJBNFFTWTZDSldKSDVJQUpUR0tJTjJFUjdMQk5WS09DQ1dONycpOwogIGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCdidXR0b24nLCB7IG5hbWU6ICdTYXZlIGFkZHJlc3MnIH0pLmNsaWNrKCk7CiAgYXdhaXQgc2V0dGxlKCk7CgogIGF3YWl0IGdvKCcvbWUnKTsKICBhd2FpdCBwYWdlLmdldEJ5Um9sZSgnYnV0dG9uJywgeyBuYW1lOiAnU3VibWl0IFBSJyB9KS5jbGljaygpOwogIGF3YWl0IHBhZ2UuZ2V0QnlMYWJlbCgnUHVsbCByZXF1ZXN0IFVSTCcpLmZpbGwoJ2h0dHBzOi8vZ2l0aHViLmNvbS9zdGVsbGFyL3JzLXNvcm9iYW4tc2RrL3B1bGwvMTIzNCcpOwogIGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCdidXR0b24nLCB7IG5hbWU6ICdTdWJtaXQgZm9yIHJldmlldycgfSkuY2xpY2soKTsKICBhd2FpdCBzZXR0bGUoKTsKCiAgYXdhaXQgZ28oJy9tYWludGFpbmVyL3JlcG8vc29yb2Jhbi1zZGsvaXNzdWVzJyk7CiAgYXdhaXQgcGFnZS5nZXRCeVJvbGUoJ2J1dHRvbicsIHsgbmFtZTogL0FkZCBhIHRlc3QgaGVscGVyIGZvciBhc3NlcnRpbmcgZW1pdHRlZCBldmVudHMvIH0pLmNsaWNrKCk7CiAgYXdhaXQgc2V0dGxlKCk7CiAgYXdhaXQgcGFnZS5nZXRCeVJvbGUoJ2J1dHRvbicsIHsgbmFtZTogL01lcmdlICYgcmVsZWFzZS8gfSkuY2xpY2soKTsKICBhd2FpdCBzZXR0bGUoKTsKCiAgYXdhaXQgZ28oJy9tZS9yZWNlaXB0cycpOwogIGFzc2VydC5lcXVhbChhd2FpdCBwYWdlLmxvY2F0b3IoJy5saXN0LXJvdycpLmNvdW50KCksIDEsICdtZXJnaW5nIHdyaXRlcyBhIHJlY2VpcHQnKTsKICBhc3NlcnQubWF0Y2goYXdhaXQgcGFnZS5sb2NhdG9yKCcubGlzdC1yb3cnKS5maXJzdCgpLmlubmVyVGV4dCgpLCAvXCtcJDQ1MC8pOwogIGFzc2VydC5tYXRjaChhd2FpdCBwYWdlLmxvY2F0b3IoJy5zdGF0JykuZmlyc3QoKS5pbm5lclRleHQoKSwgL1wkNDUwLywgJ2Vhcm5lZCB0b3RhbCByZWZsZWN0cyB0aGUgcGF5b3V0Jyk7CgogIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB0aGVtZSBwZXJzaXN0ZW5jZQogIC8vIERhcmsgaXMgdGhlIGRlZmF1bHQsIHNvIHRvZ2dsZSB0byBsaWdodCwgdmVyaWZ5IGl0IHN0aWNrcywgdGhlbiByZXR1cm4uCiAgYXdhaXQgZ28oJy9leHBsb3JlJyk7CiAgYXNzZXJ0LmVxdWFsKGF3YWl0IHBhZ2UubG9jYXRvcignaHRtbCcpLmdldEF0dHJpYnV0ZSgnZGF0YS10aGVtZScpLCAnZGFyaycpOwogIGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCdidXR0b24nLCB7IG5hbWU6IC9Td2l0Y2ggdG8gbGlnaHQgdGhlbWUvIH0pLmNsaWNrKCk7CiAgYXdhaXQgc2V0dGxlKCk7CiAgYXdhaXQgcGFnZS5yZWxvYWQoKTsKICBhc3NlcnQuZXF1YWwoYXdhaXQgcGFnZS5sb2NhdG9yKCdodG1sJykuZ2V0QXR0cmlidXRlKCdkYXRhLXRoZW1lJyksICdsaWdodCcpOwogIGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCdidXR0b24nLCB7IG5hbWU6IC9Td2l0Y2ggdG8gZGFyayB0aGVtZS8gfSkuY2xpY2soKTsKICBhd2FpdCBzZXR0bGUoKTsKICBhd2FpdCBwYWdlLnJlbG9hZCgpOwogIGFzc2VydC5lcXVhbChhd2FpdCBwYWdlLmxvY2F0b3IoJ2h0bWwnKS5nZXRBdHRyaWJ1dGUoJ2RhdGEtdGhlbWUnKSwgJ2RhcmsnKTsKCiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSByZXNwb25zaXZlCiAgY29uc3Qgcm91dGVzID0gWwogICAgJy8nLCAnL2V4cGxvcmUnLCAnL2V4cGxvcmUvcmVwb3MnLCAnL2V4cGxvcmUvb3JncycsICcvaXNzdWUvODQyJywKICAgICcvbG9naW4nLCAnL21haW50YWluZXIvbG9naW4nLCAnL21haW50YWluZXInLCAnL21haW50YWluZXIvc3VibWl0JywKICAgICcvbWFpbnRhaW5lci9yZXBvL2NsaScsICcvbWFpbnRhaW5lci9yZXBvL2NsaS9pc3N1ZXMnLAogICAgJy9tYWludGFpbmVyL3JlcG8vY2xpL3NldHRpbmdzJywgJy9tZScsICcvbWUvcmVjZWlwdHMnLCAnL21lL3NldHRpbmdzJywKICBdOwogIGZvciAoY29uc3Qgd2lkdGggb2YgWzE0NDAsIDEwMjQsIDgyMCwgNjQwLCAzOTBdKSB7CiAgICBhd2FpdCBwYWdlLnNldFZpZXdwb3J0U2l6ZSh7IHdpZHRoLCBoZWlnaHQ6IDkwMCB9KTsKICAgIGZvciAoY29uc3Qgcm91dGUgb2Ygcm91dGVzKSB7CiAgICAgIGF3YWl0IGdvKHJvdXRlKTsKICAgICAgYXNzZXJ0LmVxdWFsKAogICAgICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnNjcm9sbFdpZHRoID4gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmNsaWVudFdpZHRoICsgMSksCiAgICAgICAgZmFsc2UsCiAgICAgICAgYCR7cm91dGV9IG92ZXJmbG93cyBob3Jpem9udGFsbHkgYXQgJHt3aWR0aH1weGAsCiAgICAgICk7CiAgICB9CiAgfQoKICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIG1vYmlsZSBkcmF3ZXIKICBhd2FpdCBwYWdlLnNldFZpZXdwb3J0U2l6ZSh7IHdpZHRoOiAzOTAsIGhlaWdodDogOTAwIH0pOwogIGF3YWl0IGdvKCcvbWFpbnRhaW5lcicpOwogIGF3YWl0IHBhZ2UuZ2V0QnlSb2xlKCdidXR0b24nLCB7IG5hbWU6ICdPcGVuIG1lbnUnIH0pLmNsaWNrKCk7CiAgYXdhaXQgc2V0dGxlKCk7CiAgYXdhaXQgcGFnZS5sb2NhdG9yKCcucmFpbC5vcGVuJykuZ2V0QnlSb2xlKCdsaW5rJywgeyBuYW1lOiAnQ29ubmVjdCBhIHJlcG8nIH0pLmNsaWNrKCk7CiAgYXdhaXQgcGFnZS53YWl0Rm9yVVJMKCcqKi9tYWludGFpbmVyL3N1Ym1pdCcpOwoKICBhc3NlcnQuZGVlcEVxdWFsKGVycm9ycywgW10pOwogIGNvbnNvbGUubG9nKCdQYXNzZWQ6IHB1YmxpYyBleHBsb3JlIChubyBkYXNoYm9hcmQgY2hyb21lKSwgc2VhcmNoL3RhYnMvZmlsdGVycywgY29udHJpYnV0b3IgYXBwbHkgYW5kIHBlcnNpc3RlbmNlLCBzZXBhcmF0ZSBtYWludGFpbmVyIHNpZ24taW4sIGNvbm5lY3QtdGhlbi12ZXJpZnkgZ2F0ZSwgcGVyLXJlcG8gZGFzaGJvYXJkcyBzY29wZWQgYnkgb3duZXIgYW5kIHZlcmlmaWNhdGlvbiwgcHJvcG9zYWwgYXNzaWdubWVudCwgcG9zdGluZyBhIGZ1bmRlZCBpc3N1ZSwgcGF5b3V0IGFkZHJlc3MgdmFsaWRhdGlvbiwgbWVyZ2UtYW5kLXJlbGVhc2Ugd3JpdGluZyBhIHJlY2VpcHQsIHRoZW1lIHBlcnNpc3RlbmNlLCAxNSByb3V0ZXMgYXQgNSB3aWR0aHMgd2l0aCBubyBvdmVyZmxvdywgYW5kIHRoZSBtb2JpbGUgZHJhd2VyLicpOwp9IGNhdGNoIChlcnJvcikgewogIGNvbnNvbGUubG9nKGF3YWl0IHBhZ2UubG9jYXRvcignYm9keScpLmlubmVyVGV4dCgpKTsKICBta2RpclN5bmMoJ3Rlc3QtcmVzdWx0cycsIHsgcmVjdXJzaXZlOiB0cnVlIH0pOwogIGF3YWl0IHBhZ2Uuc2NyZWVuc2hvdCh7IHBhdGg6ICd0ZXN0LXJlc3VsdHMvc21va2UtZmFpbHVyZS5wbmcnLCBmdWxsUGFnZTogdHJ1ZSB9KTsKICB0aHJvdyBlcnJvcjsKfSBmaW5hbGx5IHsKICBhd2FpdCBicm93c2VyLmNsb3NlKCk7Cn0K \ No newline at end of file diff --git a/src/pages/explore.tsx b/src/pages/explore.tsx index 484866e..32ef1b4 100644 --- a/src/pages/explore.tsx +++ b/src/pages/explore.tsx @@ -1,249 +1 @@ -import { useEffect, useMemo, useRef, useState } from 'react'; -import { Link, useNavigate, useSearchParams } from 'react-router-dom'; -import { AnimatePresence, motion } from 'motion/react'; -import { ArrowUpRight, GitFork, Search, SlidersHorizontal, Star, X } from 'lucide-react'; -import { formatCount, formatMoney, relativeDate, repoName, type Complexity } from '../lib/model'; -import { useApp } from '../lib/store'; -import { PLATFORM } from '../lib/platform'; -import { Avatar, Chip, EASE, Empty, Item, LangDot, Page, Segmented, Stagger } from '../components/ui'; - -type Tab = 'issues' | 'repos' | 'orgs'; - -const TABS = [ - { value: 'issues' as const, label: 'Issues' }, - { value: 'repos' as const, label: 'Repositories' }, - { value: 'orgs' as const, label: 'Organizations' }, -]; - -const SORTS = ['Newest', 'Bounty', 'Title'] as const; -const LEVELS: (Complexity | 'Any')[] = ['Any', 'Trivial', 'Medium', 'High']; - -export function Explore({ tab }: { tab: Tab }) { - const { state } = useApp(); - const navigate = useNavigate(); - const [params, setParams] = useSearchParams(); - const [q, setQ] = useState(params.get('q') ?? ''); - const [level, setLevel] = useState('Any'); - const [lang, setLang] = useState('Any'); - const [sort, setSort] = useState<(typeof SORTS)[number]>('Newest'); - const [filtersOpen, setFiltersOpen] = useState(false); - const searchRef = useRef(null); - - useEffect(() => { - if (params.get('focus')) { - searchRef.current?.focus(); - const next = new URLSearchParams(params); - next.delete('focus'); - setParams(next, { replace: true }); - } - }, [params, setParams]); - - const accepted = useMemo(() => state.repos.filter(r => r.status === 'Verified'), [state.repos]); - const languages = useMemo( - () => ['Any', ...new Set(accepted.flatMap(r => r.languages))], - [accepted], - ); - const needle = q.trim().toLowerCase(); - - const issues = useMemo(() => { - const list = state.issues.filter(issue => { - const repo = accepted.find(r => r.id === issue.repoId); - if (!repo) return false; - if (level !== 'Any' && issue.complexity !== level) return false; - if (lang !== 'Any' && !repo.languages.includes(lang)) return false; - return `${issue.title} ${issue.id} ${repoName(repo)}`.toLowerCase().includes(needle); - }); - return list.sort((a, b) => - sort === 'Bounty' ? b.bounty - a.bounty - : sort === 'Title' ? a.title.localeCompare(b.title) - : b.created.localeCompare(a.created)); - }, [state.issues, accepted, level, lang, needle, sort]); - - const repos = useMemo( - () => accepted.filter(r => - (lang === 'Any' || r.languages.includes(lang)) - && `${repoName(r)} ${r.description}`.toLowerCase().includes(needle)), - [accepted, lang, needle], - ); - - const orgs = useMemo(() => { - const names = [...new Set(accepted.map(r => r.org))]; - return names - .filter(o => o.toLowerCase().includes(needle)) - .map(o => ({ org: o, repos: accepted.filter(r => r.org === o) })); - }, [accepted, needle]); - - const activeFilters = (level !== 'Any' ? 1 : 0) + (lang !== 'Any' ? 1 : 0); - const count = tab === 'issues' ? issues.length : tab === 'repos' ? repos.length : orgs.length; - - return ( - -
-
-
-

Explore

-

Funded issues across every verified repository. Each bounty is escrowed in {PLATFORM.asset} and paid on merge.

-
-
- -
- navigate(v === 'issues' ? '/explore' : `/explore/${v}`)} - /> - - {tab !== 'orgs' && ( - - )} -
- - - {filtersOpen && tab !== 'orgs' && ( - -
- {tab === 'issues' && ( - - )} - - {tab === 'issues' && ( - - )} - -
-
- )} -
- -

{count} {tab === 'issues' ? 'issues' : tab === 'repos' ? 'repositories' : 'organizations'}

- - {tab === 'issues' && (issues.length ? ( - - {issues.map(issue => { - const repo = accepted.find(r => r.id === issue.repoId)!; - return ( - - - #{issue.id} - - {issue.title} - {repoName(repo)} - - {issue.complexity} - ${formatMoney(issue.bounty)} - - - - ); - })} - - ) : Try a different search or clear the filters.)} - - {tab === 'repos' && (repos.length ? ( - - {repos.map(r => ( - -
- - - {r.name} - {r.org} - -
-

{r.description}

- {r.topics && ( -
- {r.topics.map(t => {t})} -
- )} -
- {r.languages[0] && {r.languages[0]}} - {formatCount(r.stars)} - {formatCount(r.forks)} - {r.license && {r.license}} -
-
- - {r.updated ? `Updated ${relativeDate(r.updated)}` : ''} - - - - {(n => `${n} ${n === 1 ? 'issue' : 'issues'}`)(state.issues.filter(i => i.repoId === r.id).length)} - -
-
- ))} -
- ) : Try a different search term.)} - - {tab === 'orgs' && (orgs.length ? ( - - {orgs.map(({ org, repos: list }) => ( - -
- - - {org} - {list.length} {list.length === 1 ? 'repository' : 'repositories'} - -
-

{list[0].description}

- - GitHub - -
- ))} -
- ) : Try a different search term.)} -
-
- ); -} +aW1wb3J0IHsgdXNlRWZmZWN0LCB1c2VNZW1vLCB1c2VSZWYsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnOwppbXBvcnQgeyBMaW5rLCB1c2VOYXZpZ2F0ZSwgdXNlU2VhcmNoUGFyYW1zIH0gZnJvbSAncmVhY3Qtcm91dGVyLWRvbSc7CmltcG9ydCB7IEFuaW1hdGVQcmVzZW5jZSwgbW90aW9uIH0gZnJvbSAnbW90aW9uL3JlYWN0JzsKaW1wb3J0IHsgQXJyb3dVcFJpZ2h0LCBHaXRGb3JrLCBTZWFyY2gsIFNsaWRlcnNIb3Jpem9udGFsLCBTdGFyLCBYIH0gZnJvbSAnbHVjaWRlLXJlYWN0JzsKaW1wb3J0IHsgZXNjcm93ZWRUb3RhbCwgZm9ybWF0Q291bnQsIGZvcm1hdE1vbmV5LCByZWxhdGl2ZURhdGUsIHJlcG9OYW1lLCB0eXBlIENvbXBsZXhpdHkgfSBmcm9tICcuLi9saWIvbW9kZWwnOwppbXBvcnQgeyB1c2VBcHAgfSBmcm9tICcuLi9saWIvc3RvcmUnOwppbXBvcnQgeyBQTEFURk9STSB9IGZyb20gJy4uL2xpYi9wbGF0Zm9ybSc7CmltcG9ydCB7IEF2YXRhciwgQ2hpcCwgRUFTRSwgRW1wdHksIEl0ZW0sIExhbmdEb3QsIFBhZ2UsIFNlZ21lbnRlZCwgU3RhZ2dlciB9IGZyb20gJy4uL2NvbXBvbmVudHMvdWknOwoKdHlwZSBUYWIgPSAnaXNzdWVzJyB8ICdyZXBvcycgfCAnb3Jncyc7Cgpjb25zdCBUQUJTID0gWwogIHsgdmFsdWU6ICdpc3N1ZXMnIGFzIGNvbnN0LCBsYWJlbDogJ0lzc3VlcycgfSwKICB7IHZhbHVlOiAncmVwb3MnIGFzIGNvbnN0LCBsYWJlbDogJ1JlcG9zaXRvcmllcycgfSwKICB7IHZhbHVlOiAnb3JncycgYXMgY29uc3QsIGxhYmVsOiAnT3JnYW5pemF0aW9ucycgfSwKXTsKCmNvbnN0IFNPUlRTID0gWydOZXdlc3QnLCAnQm91bnR5JywgJ1RpdGxlJ10gYXMgY29uc3Q7CmNvbnN0IFJFUE9fU09SVFMgPSBbJ1RvdGFsIGVzY3JvdycsICdTdGFycycsICdPcGVuIGlzc3VlcyddIGFzIGNvbnN0Owpjb25zdCBMRVZFTFM6IChDb21wbGV4aXR5IHwgJ0FueScpW10gPSBbJ0FueScsICdUcml2aWFsJywgJ01lZGl1bScsICdIaWdoJ107CgpleHBvcnQgZnVuY3Rpb24gRXhwbG9yZSh7IHRhYiB9OiB7IHRhYjogVGFiIH0pIHsKICBjb25zdCB7IHN0YXRlIH0gPSB1c2VBcHAoKTsKICBjb25zdCBuYXZpZ2F0ZSA9IHVzZU5hdmlnYXRlKCk7CiAgY29uc3QgW3BhcmFtcywgc2V0UGFyYW1zXSA9IHVzZVNlYXJjaFBhcmFtcygpOwogIGNvbnN0IFtxLCBzZXRRXSA9IHVzZVN0YXRlKHBhcmFtcy5nZXQoJ3EnKSA/PyAnJyk7CiAgY29uc3QgW2xldmVsLCBzZXRMZXZlbF0gPSB1c2VTdGF0ZTxDb21wbGV4aXR5IHwgJ0FueSc+KCdBbnknKTsKICBjb25zdCBbbGFuZywgc2V0TGFuZ10gPSB1c2VTdGF0ZSgnQW55Jyk7CiAgY29uc3QgW3NvcnQsIHNldFNvcnRdID0gdXNlU3RhdGU8KHR5cGVvZiBTT1JUUylbbnVtYmVyXT4oJ05ld2VzdCcpOwogIGNvbnN0IFtyZXBvU29ydCwgc2V0UmVwb1NvcnRdID0gdXNlU3RhdGU8KHR5cGVvZiBSRVBPX1NPUlRTKVtudW1iZXJdPignVG90YWwgZXNjcm93Jyk7CiAgY29uc3QgW2ZpbHRlcnNPcGVuLCBzZXRGaWx0ZXJzT3Blbl0gPSB1c2VTdGF0ZShmYWxzZSk7CiAgY29uc3Qgc2VhcmNoUmVmID0gdXNlUmVmPEhUTUxJbnB1dEVsZW1lbnQ+KG51bGwpOwoKICB1c2VFZmZlY3QoKCkgPT4gewogICAgaWYgKHBhcmFtcy5nZXQoJ2ZvY3VzJykpIHsKICAgICAgc2VhcmNoUmVmLmN1cnJlbnQ/LmZvY3VzKCk7CiAgICAgIGNvbnN0IG5leHQgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKHBhcmFtcyk7CiAgICAgIG5leHQuZGVsZXRlKCdmb2N1cycpOwogICAgICBzZXRQYXJhbXMobmV4dCwgeyByZXBsYWNlOiB0cnVlIH0pOwogICAgfQogIH0sIFtwYXJhbXMsIHNldFBhcmFtc10pOwoKICBjb25zdCBhY2NlcHRlZCA9IHVzZU1lbW8oKCkgPT4gc3RhdGUucmVwb3MuZmlsdGVyKHIgPT4gci5zdGF0dXMgPT09ICdWZXJpZmllZCcpLCBbc3RhdGUucmVwb3NdKTsKICBjb25zdCBsYW5ndWFnZXMgPSB1c2VNZW1vKAogICAgKCkgPT4gWydBbnknLCAuLi5uZXcgU2V0KGFjY2VwdGVkLmZsYXRNYXAociA9PiByLmxhbmd1YWdlcykpXSwKICAgIFthY2NlcHRlZF0sCiAgKTsKICBjb25zdCBuZWVkbGUgPSBxLnRyaW0oKS50b0xvd2VyQ2FzZSgpOwoKICBjb25zdCBpc3N1ZXMgPSB1c2VNZW1vKCgpID0+IHsKICAgIGNvbnN0IGxpc3QgPSBzdGF0ZS5pc3N1ZXMuZmlsdGVyKGlzc3VlID0+IHsKICAgICAgY29uc3QgcmVwbyA9IGFjY2VwdGVkLmZpbmQociA9PiByLmlkID09PSBpc3N1ZS5yZXBvSWQpOwogICAgICBpZiAoIXJlcG8pIHJldHVybiBmYWxzZTsKICAgICAgaWYgKGxldmVsICE9PSAnQW55JyAmJiBpc3N1ZS5jb21wbGV4aXR5ICE9PSBsZXZlbCkgcmV0dXJuIGZhbHNlOwogICAgICBpZiAobGFuZyAhPT0gJ0FueScgJiYgIXJlcG8ubGFuZ3VhZ2VzLmluY2x1ZGVzKGxhbmcpKSByZXR1cm4gZmFsc2U7CiAgICAgIHJldHVybiBgJHtpc3N1ZS50aXRsZX0gJHtpc3N1ZS5pZH0gJHtyZXBvTmFtZShyZXBvKX1gLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMobmVlZGxlKTsKICAgIH0pOwogICAgcmV0dXJuIGxpc3Quc29ydCgoYSwgYikgPT4KICAgICAgc29ydCA9PT0gJ0JvdW50eScgPyBiLmJvdW50eSAtIGEuYm91bnR5CiAgICAgICAgOiBzb3J0ID09PSAnVGl0bGUnID8gYS50aXRsZS5sb2NhbGVDb21wYXJlKGIudGl0bGUpCiAgICAgICAgICA6IGIuY3JlYXRlZC5sb2NhbGVDb21wYXJlKGEuY3JlYXRlZCkpOwogIH0sIFtzdGF0ZS5pc3N1ZXMsIGFjY2VwdGVkLCBsZXZlbCwgbGFuZywgbmVlZGxlLCBzb3J0XSk7CgogIGNvbnN0IHJlcG9zID0gdXNlTWVtbygoKSA9PiB7CiAgICBjb25zdCBsaXN0ID0gYWNjZXB0ZWQuZmlsdGVyKHIgPT4KICAgICAgKGxhbmcgPT09ICdBbnknIHx8IHIubGFuZ3VhZ2VzLmluY2x1ZGVzKGxhbmcpKQogICAgICAmJiBgJHtyZXBvTmFtZShyKX0gJHtyLmRlc2NyaXB0aW9ufWAudG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhuZWVkbGUpKTsKICAgIGNvbnN0IGlzc3Vlc0ZvciA9IChpZDogc3RyaW5nKSA9PiBzdGF0ZS5pc3N1ZXMuZmlsdGVyKGkgPT4gaS5yZXBvSWQgPT09IGlkKTsKICAgIHJldHVybiBsaXN0LnNvcnQoKGEsIGIpID0+IHsKICAgICAgaWYgKHJlcG9Tb3J0ID09PSAnU3RhcnMnKSByZXR1cm4gYi5zdGFycyAtIGEuc3RhcnM7CiAgICAgIGlmIChyZXBvU29ydCA9PT0gJ09wZW4gaXNzdWVzJykgcmV0dXJuIGlzc3Vlc0ZvcihiLmlkKS5sZW5ndGggLSBpc3N1ZXNGb3IoYS5pZCkubGVuZ3RoOwogICAgICByZXR1cm4gZXNjcm93ZWRUb3RhbChpc3N1ZXNGb3IoYi5pZCksIFtdKSAtIGVzY3Jvd2VkVG90YWwoaXNzdWVzRm9yKGEuaWQpLCBbXSk7CiAgICB9KTsKICB9LCBbYWNjZXB0ZWQsIGxhbmcsIG5lZWRsZSwgcmVwb1NvcnQsIHN0YXRlLmlzc3Vlc10pOwoKICBjb25zdCBvcmdzID0gdXNlTWVtbygoKSA9PiB7CiAgICBjb25zdCBuYW1lcyA9IFsuLi5uZXcgU2V0KGFjY2VwdGVkLm1hcChyID0+IHIub3JnKSldOwogICAgcmV0dXJuIG5hbWVzCiAgICAgIC5maWx0ZXIobyA9PiBvLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMobmVlZGxlKSkKICAgICAgLm1hcChvID0+ICh7IG9yZzogbywgcmVwb3M6IGFjY2VwdGVkLmZpbHRlcihyID0+IHIub3JnID09PSBvKSB9KSk7CiAgfSwgW2FjY2VwdGVkLCBuZWVkbGVdKTsKCiAgY29uc3QgYWN0aXZlRmlsdGVycyA9IChsZXZlbCAhPT0gJ0FueScgPyAxIDogMCkgKyAobGFuZyAhPT0gJ0FueScgPyAxIDogMCk7CiAgY29uc3QgY291bnQgPSB0YWIgPT09ICdpc3N1ZXMnID8gaXNzdWVzLmxlbmd0aCA6IHRhYiA9PT0gJ3JlcG9zJyA/IHJlcG9zLmxlbmd0aCA6IG9yZ3MubGVuZ3RoOwoKICByZXR1cm4gKAogICAgPFBhZ2UgY2xhc3NOYW1lPSJleHBsb3JlIj4KICAgICAgPGRpdiBjbGFzc05hbWU9ImV4cGxvcmUtaW5uZXIiPgogICAgICAgIDxoZWFkZXIgY2xhc3NOYW1lPSJleHBsb3JlLWhlYWQiPgogICAgICAgICAgPGRpdj4KICAgICAgICAgICAgPGgxPkV4cGxvcmU8L2gxPgogICAgICAgICAgICA8cCBjbGFzc05hbWU9Im11dGVkIj5GdW5kZWQgaXNzdWVzIGFjcm9zcyBldmVyeSB2ZXJpZmllZCByZXBvc2l0b3J5LiBFYWNoIGJvdW50eSBpcyBlc2Nyb3dlZCBpbiB7UExBVEZPUk0uYXNzZXR9IGFuZCBwYWlkIG9uIG1lcmdlLjwvcD4KICAgICAgICAgIDwvZGl2PgogICAgICAgIDwvaGVhZGVyPgoKICAgICAgICA8ZGl2IGNsYXNzTmFtZT0iZXhwbG9yZS1iYXIiPgogICAgICAgICAgPFNlZ21lbnRlZAogICAgICAgICAgICBpZFByZWZpeD0iZXhwbG9yZSIKICAgICAgICAgICAgdmFsdWU9e3RhYn0KICAgICAgICAgICAgb3B0aW9ucz17VEFCU30KICAgICAgICAgICAgb25DaGFuZ2U9e3YgPT4gbmF2aWdhdGUodiA9PT0gJ2lzc3VlcycgPyAnL2V4cGxvcmUnIDogYC9leHBsb3JlLyR7dn1gKX0KICAgICAgICAgIC8+CiAgICAgICAgICA8bGFiZWwgY2xhc3NOYW1lPSJzZWFyY2ggZXhwbG9yZS1zZWFyY2giPgogICAgICAgICAgICA8U2VhcmNoIHNpemU9ezE0fSAvPgogICAgICAgICAgICA8aW5wdXQKICAgICAgICAgICAgICByZWY9e3NlYXJjaFJlZn0KICAgICAgICAgICAgICBhcmlhLWxhYmVsPSJTZWFyY2giCiAgICAgICAgICAgICAgcGxhY2Vob2xkZXI9e3RhYiA9PT0gJ29yZ3MnID8gJ1NlYXJjaCBvcmdhbml6YXRpb25z4oCmJyA6IHRhYiA9PT0gJ3JlcG9zJyA/ICdTZWFyY2ggcmVwb3NpdG9yaWVz4oCmJyA6ICdTZWFyY2ggaXNzdWVz4oCmJ30KICAgICAgICAgICAgICB2YWx1ZT17cX0KICAgICAgICAgICAgICBvbkNoYW5nZT17ZSA9PiBzZXRRKGUudGFyZ2V0LnZhbHVlKX0KICAgICAgICAgICAgLz4KICAgICAgICAgICAge3EgJiYgKAogICAgICAgICAgICAgIDxidXR0b24gY2xhc3NOYW1lPSJidG4gZ2hvc3QgaWNvbiB4cyIgYXJpYS1sYWJlbD0iQ2xlYXIgc2VhcmNoIiBvbkNsaWNrPXsoKSA9PiBzZXRRKCcnKX0+CiAgICAgICAgICAgICAgICA8WCBzaXplPXsxMn0gLz4KICAgICAgICAgICAgICA8L2J1dHRvbj4KICAgICAgICAgICAgKX0KICAgICAgICAgIDwvbGFiZWw+CiAgICAgICAgICB7dGFiICE9PSAnb3JncycgJiYgKAogICAgICAgICAgICA8YnV0dG9uCiAgICAgICAgICAgICAgY2xhc3NOYW1lPXthY3RpdmVGaWx0ZXJzID8gJ2J0biBzbSBvbicgOiAnYnRuIHNtJ30KICAgICAgICAgICAgICBhcmlhLWV4cGFuZGVkPXtmaWx0ZXJzT3Blbn0KICAgICAgICAgICAgICBvbkNsaWNrPXsoKSA9PiBzZXRGaWx0ZXJzT3BlbihvID0+ICFvKX0KICAgICAgICAgICAgPgogICAgICAgICAgICAgIDxTbGlkZXJzSG9yaXpvbnRhbCBzaXplPXsxM30gLz4KICAgICAgICAgICAgICBGaWx0ZXJzCiAgICAgICAgICAgICAge2FjdGl2ZUZpbHRlcnMgPiAwICYmIDxzcGFuIGNsYXNzTmFtZT0icGlwY291bnQgbnVtIj57YWN0aXZlRmlsdGVyc308L3NwYW4+fQogICAgICAgICAgICA8L2J1dHRvbj4KICAgICAgICAgICl9CiAgICAgICAgPC9kaXY+CgogICAgICAgIDxBbmltYXRlUHJlc2VuY2UgaW5pdGlhbD17ZmFsc2V9PgogICAgICAgICAge2ZpbHRlcnNPcGVuICYmIHRhYiAhPT0gJ29yZ3MnICYmICgKICAgICAgICAgICAgPG1vdGlvbi5kaXYKICAgICAgICAgICAgICBjbGFzc05hbWU9ImZpbHRlcnMiCiAgICAgICAgICAgICAgaW5pdGlhbD17eyBoZWlnaHQ6IDAsIG9wYWNpdHk6IDAgfX0KICAgICAgICAgICAgICBhbmltYXRlPXt7IGhlaWdodDogJ2F1dG8nLCBvcGFjaXR5OiAxIH19CiAgICAgICAgICAgICAgZXhpdD17eyBoZWlnaHQ6IDAsIG9wYWNpdHk6IDAgfX0KICAgICAgICAgICAgICB0cmFuc2l0aW9uPXt7IGR1cmF0aW9uOiAwLjIyLCBlYXNlOiBFQVNFIH19CiAgICAgICAgICAgID4KICAgICAgICAgICAgICA8ZGl2IGNsYXNzTmFtZT0iZmlsdGVycy1pbm5lciI+CiAgICAgICAgICAgICAgICB7dGFiID09PSAnaXNzdWVzJyAmJiAoCiAgICAgICAgICAgICAgICAgIDxsYWJlbCBjbGFzc05hbWU9ImZpZWxkIGlubGluZSI+CiAgICAgICAgICAgICAgICAgICAgPHNwYW4+Q29tcGxleGl0eTwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICA8c2VsZWN0IGNsYXNzTmFtZT0ic2VsZWN0IiBhcmlhLWxhYmVsPSJDb21wbGV4aXR5IiB2YWx1ZT17bGV2ZWx9CiAgICAgICAgICAgICAgICAgICAgICBvbkNoYW5nZT17ZSA9PiBzZXRMZXZlbChlLnRhcmdldC52YWx1ZSBhcyBDb21wbGV4aXR5IHwgJ0FueScpfT4KICAgICAgICAgICAgICAgICAgICAgIHtMRVZFTFMubWFwKGwgPT4gPG9wdGlvbiBrZXk9e2x9PntsfTwvb3B0aW9uPil9CiAgICAgICAgICAgICAgICAgICAgPC9zZWxlY3Q+CiAgICAgICAgICAgICAgICAgIDwvbGFiZWw+CiAgICAgICAgICAgICAgICApfQogICAgICAgICAgICAgICAgPGxhYmVsIGNsYXNzTmFtZT0iZmllbGQgaW5saW5lIj4KICAgICAgICAgICAgICAgICAgPHNwYW4+TGFuZ3VhZ2U8L3NwYW4+CiAgICAgICAgICAgICAgICAgIDxzZWxlY3QgY2xhc3NOYW1lPSJzZWxlY3QiIGFyaWEtbGFiZWw9Ikxhbmd1YWdlIiB2YWx1ZT17bGFuZ30gb25DaGFuZ2U9e2UgPT4gc2V0TGFuZyhlLnRhcmdldC52YWx1ZSl9PgogICAgICAgICAgICAgICAgICAgIHtsYW5ndWFnZXMubWFwKGwgPT4gPG9wdGlvbiBrZXk9e2x9PntsfTwvb3B0aW9uPil9CiAgICAgICAgICAgICAgICAgIDwvc2VsZWN0PgogICAgICAgICAgICAgICAgPC9sYWJlbD4KICAgICAgICAgICAgICAgIHt0YWIgPT09ICdpc3N1ZXMnICYmICgKICAgICAgICAgICAgICAgICAgPGxhYmVsIGNsYXNzTmFtZT0iZmllbGQgaW5saW5lIj4KICAgICAgICAgICAgICAgICAgICA8c3Bhbj5Tb3J0PC9zcGFuPgogICAgICAgICAgICAgICAgICAgIDxzZWxlY3QgY2xhc3NOYW1lPSJzZWxlY3QiIGFyaWEtbGFiZWw9IlNvcnQiIHZhbHVlPXtzb3J0fQogICAgICAgICAgICAgICAgICAgICAgb25DaGFuZ2U9e2UgPT4gc2V0U29ydChlLnRhcmdldC52YWx1ZSBhcyAodHlwZW9mIFNPUlRTKVtudW1iZXJdKX0+CiAgICAgICAgICAgICAgICAgICAgICB7U09SVFMubWFwKHMgPT4gPG9wdGlvbiBrZXk9e3N9PntzfTwvb3B0aW9uPil9CiAgICAgICAgICAgICAgICAgICAgPC9zZWxlY3Q+CiAgICAgICAgICAgICAgICAgIDwvbGFiZWw+CiAgICAgICAgICAgICAgICApfQogICAgICAgICAgICAgICAge3RhYiA9PT0gJ3JlcG9zJyAmJiAoCiAgICAgICAgICAgICAgICAgIDxsYWJlbCBjbGFzc05hbWU9ImZpZWxkIGlubGluZSI+CiAgICAgICAgICAgICAgICAgICAgPHNwYW4+U29ydDwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICA8c2VsZWN0IGNsYXNzTmFtZT0ic2VsZWN0IiBhcmlhLWxhYmVsPSJTb3J0IHJlcG9zaXRvcmllcyIgdmFsdWU9e3JlcG9Tb3J0fQogICAgICAgICAgICAgICAgICAgICAgb25DaGFuZ2U9e2UgPT4gc2V0UmVwb1NvcnQoZS50YXJnZXQudmFsdWUgYXMgKHR5cGVvZiBSRVBPX1NPUlRTKVtudW1iZXJdKX0+CiAgICAgICAgICAgICAgICAgICAgICB7UkVQT19TT1JUUy5tYXAocyA9PiA8b3B0aW9uIGtleT17c30+e3N9PC9vcHRpb24+KX0KICAgICAgICAgICAgICAgICAgICA8L3NlbGVjdD4KICAgICAgICAgICAgICAgICAgPC9sYWJlbD4KICAgICAgICAgICAgICAgICl9CiAgICAgICAgICAgICAgICA8YnV0dG9uIGNsYXNzTmFtZT0iYnRuIHNtIGdob3N0IiBvbkNsaWNrPXsoKSA9PiB7IHNldExldmVsKCdBbnknKTsgc2V0TGFuZygnQW55Jyk7IHNldFNvcnQoJ05ld2VzdCcpOyBzZXRSZXBvU29ydCgnVG90YWwgZXNjcm93Jyk7IH19PgogICAgICAgICAgICAgICAgICBSZXNldAogICAgICAgICAgICAgICAgPC9idXR0b24+CiAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDwvbW90aW9uLmRpdj4KICAgICAgICAgICl9CiAgICAgICAgPC9BbmltYXRlUHJlc2VuY2U+CgogICAgICAgIDxwIGNsYXNzTmFtZT0icmVzdWx0LWNvdW50IG51bSI+e2NvdW50fSB7dGFiID09PSAnaXNzdWVzJyA/ICdpc3N1ZXMnIDogdGFiID09PSAncmVwb3MnID8gJ3JlcG9zaXRvcmllcycgOiAnb3JnYW5pemF0aW9ucyd9PC9wPgoKICAgICAgICB7dGFiID09PSAnaXNzdWVzJyAmJiAoaXNzdWVzLmxlbmd0aCA/ICgKICAgICAgICAgIDxTdGFnZ2VyIGNsYXNzTmFtZT0ibGlzdCI+CiAgICAgICAgICAgIHtpc3N1ZXMubWFwKGlzc3VlID0+IHsKICAgICAgICAgICAgICBjb25zdCByZXBvID0gYWNjZXB0ZWQuZmluZChyID0+IHIuaWQgPT09IGlzc3VlLnJlcG9JZCkhOwogICAgICAgICAgICAgIHJldHVybiAoCiAgICAgICAgICAgICAgICA8SXRlbSBrZXk9e2lzc3VlLmlkfT4KICAgICAgICAgICAgICAgICAgPExpbmsgY2xhc3NOYW1lPSJsaXN0LXJvdyIgdG89e2AvaXNzdWUvJHtpc3N1ZS5pZH1gfT4KICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9Im1vbm8gZGltIGlzc3VlLW51bSI+I3tpc3N1ZS5pZH08L3NwYW4+CiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJjb2wiIHN0eWxlPXt7IGdhcDogMiwgbWluV2lkdGg6IDAsIGZsZXg6IDEgfX0+CiAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9InJvdy10aXRsZSI+e2lzc3VlLnRpdGxlfTwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT0icm93LXN1YiI+e3JlcG9OYW1lKHJlcG8pfTwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICA8L3NwYW4+CiAgICAgICAgICAgICAgICAgICAgPENoaXAgY2xhc3NOYW1lPSJoaWRlLXNtIj57aXNzdWUuY29tcGxleGl0eX08L0NoaXA+CiAgICAgICAgICAgICAgICAgICAgPENoaXAgY2xhc3NOYW1lPSJzb2xpZCBudW0iPiR7Zm9ybWF0TW9uZXkoaXNzdWUuYm91bnR5KX08L0NoaXA+CiAgICAgICAgICAgICAgICAgICAgPEFycm93VXBSaWdodCBzaXplPXsxNH0gY2xhc3NOYW1lPSJkaW0gaGlkZS1zbSIgLz4KICAgICAgICAgICAgICAgICAgPC9MaW5rPgogICAgICAgICAgICAgICAgPC9JdGVtPgogICAgICAgICAgICAgICk7CiAgICAgICAgICAgIH0pfQogICAgICAgICAgPC9TdGFnZ2VyPgogICAgICAgICkgOiA8RW1wdHkgdGl0bGU9Ik5vIGlzc3VlcyBtYXRjaCI+VHJ5IGEgZGlmZmVyZW50IHNlYXJjaCBvciBjbGVhciB0aGUgZmlsdGVycy48L0VtcHR5Pil9CgogICAgICAgIHt0YWIgPT09ICdyZXBvcycgJiYgKHJlcG9zLmxlbmd0aCA/ICgKICAgICAgICAgIDxTdGFnZ2VyIGNsYXNzTmFtZT0iZ3JpZCBjMyI+CiAgICAgICAgICAgIHtyZXBvcy5tYXAociA9PiAoCiAgICAgICAgICAgICAgPEl0ZW0ga2V5PXtyLmlkfSBjbGFzc05hbWU9ImNhcmQgcmVwby10aWxlIj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3NOYW1lPSJyb3ciIHN0eWxlPXt7IGdhcDogMTAgfX0+CiAgICAgICAgICAgICAgICAgIDxBdmF0YXIgbmFtZT17ci5vcmd9IG9yZz17ci5vcmd9IHNxdWFyZSBzaXplPSJsZyIgLz4KICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJjb2wiIHN0eWxlPXt7IGdhcDogMSwgbWluV2lkdGg6IDAgfX0+CiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJyb3ctdGl0bGUiPntyLm5hbWV9PC9zcGFuPgogICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT0icm93LXN1YiI+e3Iub3JnfTwvc3Bhbj4KICAgICAgICAgICAgICAgICAgPC9zcGFuPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8cCBjbGFzc05hbWU9Im11dGVkIHJlcG8tZGVzYyI+e3IuZGVzY3JpcHRpb259PC9wPgogICAgICAgICAgICAgICAge3IudG9waWNzICYmICgKICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9InJvdyB3cmFwIiBzdHlsZT17eyBnYXA6IDUgfX0+CiAgICAgICAgICAgICAgICAgICAge3IudG9waWNzLm1hcCh0ID0+IDxDaGlwIGtleT17dH0+e3R9PC9DaGlwPil9CiAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgKX0KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3NOYW1lPSJyZXBvLW1ldGEiPgogICAgICAgICAgICAgICAgICB7ci5sYW5ndWFnZXNbMF0gJiYgPHNwYW4gY2xhc3NOYW1lPSJyb3ciIHN0eWxlPXt7IGdhcDogNSB9fT48TGFuZ0RvdCBsYW5nPXtyLmxhbmd1YWdlc1swXX0gLz57ci5sYW5ndWFnZXNbMF19PC9zcGFuPn0KICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJyb3ciIHN0eWxlPXt7IGdhcDogNCB9fT48U3RhciBzaXplPXsxMX0gLz57Zm9ybWF0Q291bnQoci5zdGFycyl9PC9zcGFuPgogICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9InJvdyIgc3R5bGU9e3sgZ2FwOiA0IH19PjxHaXRGb3JrIHNpemU9ezExfSAvPntmb3JtYXRDb3VudChyLmZvcmtzKX08L3NwYW4+CiAgICAgICAgICAgICAgICAgIHtyLmxpY2Vuc2UgJiYgPHNwYW4+e3IubGljZW5zZX08L3NwYW4+fQogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzTmFtZT0icm93IHJlcG8tZm9vdCI+CiAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT0iZGltIiBzdHlsZT17eyBmb250U2l6ZTogJ3ZhcigtLXQyKScgfX0+CiAgICAgICAgICAgICAgICAgICAge3IudXBkYXRlZCA/IGBVcGRhdGVkICR7cmVsYXRpdmVEYXRlKHIudXBkYXRlZCl9YCA6ICcnfQogICAgICAgICAgICAgICAgICA8L3NwYW4+CiAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT0ic3BhY2VyIiAvPgogICAgICAgICAgICAgICAgICA8TGluayBjbGFzc05hbWU9ImJ0biB4cyIgdG89e2AvZXhwbG9yZT9xPSR7ZW5jb2RlVVJJQ29tcG9uZW50KHIubmFtZSl9YH0+CiAgICAgICAgICAgICAgICAgICAgeyhuID0+IGAke259ICR7biA9PT0gMSA/ICdpc3N1ZScgOiAnaXNzdWVzJ31gKShzdGF0ZS5pc3N1ZXMuZmlsdGVyKGkgPT4gaS5yZXBvSWQgPT09IHIuaWQpLmxlbmd0aCl9CiAgICAgICAgICAgICAgICAgIDwvTGluaz4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgIDwvSXRlbT4KICAgICAgICAgICAgKSl9CiAgICAgICAgICA8L1N0YWdnZXI+CiAgICAgICAgKSA6IDxFbXB0eSB0aXRsZT0iTm8gcmVwb3NpdG9yaWVzIG1hdGNoIj5UcnkgYSBkaWZmZXJlbnQgc2VhcmNoIHRlcm0uPC9FbXB0eT4pfQoKICAgICAgICB7dGFiID09PSAnb3JncycgJiYgKG9yZ3MubGVuZ3RoID8gKAogICAgICAgICAgPFN0YWdnZXIgY2xhc3NOYW1lPSJncmlkIGMzIj4KICAgICAgICAgICAge29yZ3MubWFwKCh7IG9yZywgcmVwb3M6IGxpc3QgfSkgPT4gKAogICAgICAgICAgICAgIDxJdGVtIGtleT17b3JnfSBjbGFzc05hbWU9ImNhcmQgcmVwby10aWxlIj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3NOYW1lPSJyb3ciIHN0eWxlPXt7IGdhcDogMTAgfX0+CiAgICAgICAgICAgICAgICAgIDxBdmF0YXIgbmFtZT17b3JnfSBvcmc9e29yZ30gc3F1YXJlIHNpemU9ImxnIiAvPgogICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9ImNvbCIgc3R5bGU9e3sgZ2FwOiAxLCBtaW5XaWR0aDogMCB9fT4KICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9InJvdy10aXRsZSI+e29yZ308L3NwYW4+CiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJyb3ctc3ViIj57bGlzdC5sZW5ndGh9IHtsaXN0Lmxlbmd0aCA9PT0gMSA/ICdyZXBvc2l0b3J5JyA6ICdyZXBvc2l0b3JpZXMnfTwvc3Bhbj4KICAgICAgICAgICAgICAgICAgPC9zcGFuPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8cCBjbGFzc05hbWU9Im11dGVkIHJlcG8tZGVzYyI+e2xpc3RbMF0uZGVzY3JpcHRpb259PC9wPgogICAgICAgICAgICAgICAgPGEgY2xhc3NOYW1lPSJidG4geHMiIGhyZWY9e2BodHRwczovL2dpdGh1Yi5jb20vJHtvcmd9YH0gdGFyZ2V0PSJfYmxhbmsiIHJlbD0ibm9yZWZlcnJlciI+CiAgICAgICAgICAgICAgICAgIEdpdEh1YjxBcnJvd1VwUmlnaHQgc2l6ZT17MTF9IC8+CiAgICAgICAgICAgICAgICA8L2E+CiAgICAgICAgICAgICAgPC9JdGVtPgogICAgICAgICAgICApKX0KICAgICAgICAgIDwvU3RhZ2dlcj4KICAgICAgICApIDogPEVtcHR5IHRpdGxlPSJObyBvcmdhbml6YXRpb25zIG1hdGNoIj5UcnkgYSBkaWZmZXJlbnQgc2VhcmNoIHRlcm0uPC9FbXB0eT4pfQogICAgICA8L2Rpdj4KICAgIDwvUGFnZT4KICApOwp9Cg== \ No newline at end of file From c65009e3c6618f91769df4968066173df064318d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9A=A8=E4=B9=BE=E5=8F=B8=E5=9B=BD=E5=AD=A6=E7=A4=BE?= Date: Thu, 5 Nov 2026 09:00:19 +0800 Subject: [PATCH 2/2] fix: use state.applications in escrowedTotal, add smoke test sort coverage --- src/pages/explore.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/explore.tsx b/src/pages/explore.tsx index 32ef1b4..0ef73be 100644 --- a/src/pages/explore.tsx +++ b/src/pages/explore.tsx @@ -1 +1 @@ -aW1wb3J0IHsgdXNlRWZmZWN0LCB1c2VNZW1vLCB1c2VSZWYsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnOwppbXBvcnQgeyBMaW5rLCB1c2VOYXZpZ2F0ZSwgdXNlU2VhcmNoUGFyYW1zIH0gZnJvbSAncmVhY3Qtcm91dGVyLWRvbSc7CmltcG9ydCB7IEFuaW1hdGVQcmVzZW5jZSwgbW90aW9uIH0gZnJvbSAnbW90aW9uL3JlYWN0JzsKaW1wb3J0IHsgQXJyb3dVcFJpZ2h0LCBHaXRGb3JrLCBTZWFyY2gsIFNsaWRlcnNIb3Jpem9udGFsLCBTdGFyLCBYIH0gZnJvbSAnbHVjaWRlLXJlYWN0JzsKaW1wb3J0IHsgZXNjcm93ZWRUb3RhbCwgZm9ybWF0Q291bnQsIGZvcm1hdE1vbmV5LCByZWxhdGl2ZURhdGUsIHJlcG9OYW1lLCB0eXBlIENvbXBsZXhpdHkgfSBmcm9tICcuLi9saWIvbW9kZWwnOwppbXBvcnQgeyB1c2VBcHAgfSBmcm9tICcuLi9saWIvc3RvcmUnOwppbXBvcnQgeyBQTEFURk9STSB9IGZyb20gJy4uL2xpYi9wbGF0Zm9ybSc7CmltcG9ydCB7IEF2YXRhciwgQ2hpcCwgRUFTRSwgRW1wdHksIEl0ZW0sIExhbmdEb3QsIFBhZ2UsIFNlZ21lbnRlZCwgU3RhZ2dlciB9IGZyb20gJy4uL2NvbXBvbmVudHMvdWknOwoKdHlwZSBUYWIgPSAnaXNzdWVzJyB8ICdyZXBvcycgfCAnb3Jncyc7Cgpjb25zdCBUQUJTID0gWwogIHsgdmFsdWU6ICdpc3N1ZXMnIGFzIGNvbnN0LCBsYWJlbDogJ0lzc3VlcycgfSwKICB7IHZhbHVlOiAncmVwb3MnIGFzIGNvbnN0LCBsYWJlbDogJ1JlcG9zaXRvcmllcycgfSwKICB7IHZhbHVlOiAnb3JncycgYXMgY29uc3QsIGxhYmVsOiAnT3JnYW5pemF0aW9ucycgfSwKXTsKCmNvbnN0IFNPUlRTID0gWydOZXdlc3QnLCAnQm91bnR5JywgJ1RpdGxlJ10gYXMgY29uc3Q7CmNvbnN0IFJFUE9fU09SVFMgPSBbJ1RvdGFsIGVzY3JvdycsICdTdGFycycsICdPcGVuIGlzc3VlcyddIGFzIGNvbnN0Owpjb25zdCBMRVZFTFM6IChDb21wbGV4aXR5IHwgJ0FueScpW10gPSBbJ0FueScsICdUcml2aWFsJywgJ01lZGl1bScsICdIaWdoJ107CgpleHBvcnQgZnVuY3Rpb24gRXhwbG9yZSh7IHRhYiB9OiB7IHRhYjogVGFiIH0pIHsKICBjb25zdCB7IHN0YXRlIH0gPSB1c2VBcHAoKTsKICBjb25zdCBuYXZpZ2F0ZSA9IHVzZU5hdmlnYXRlKCk7CiAgY29uc3QgW3BhcmFtcywgc2V0UGFyYW1zXSA9IHVzZVNlYXJjaFBhcmFtcygpOwogIGNvbnN0IFtxLCBzZXRRXSA9IHVzZVN0YXRlKHBhcmFtcy5nZXQoJ3EnKSA/PyAnJyk7CiAgY29uc3QgW2xldmVsLCBzZXRMZXZlbF0gPSB1c2VTdGF0ZTxDb21wbGV4aXR5IHwgJ0FueSc+KCdBbnknKTsKICBjb25zdCBbbGFuZywgc2V0TGFuZ10gPSB1c2VTdGF0ZSgnQW55Jyk7CiAgY29uc3QgW3NvcnQsIHNldFNvcnRdID0gdXNlU3RhdGU8KHR5cGVvZiBTT1JUUylbbnVtYmVyXT4oJ05ld2VzdCcpOwogIGNvbnN0IFtyZXBvU29ydCwgc2V0UmVwb1NvcnRdID0gdXNlU3RhdGU8KHR5cGVvZiBSRVBPX1NPUlRTKVtudW1iZXJdPignVG90YWwgZXNjcm93Jyk7CiAgY29uc3QgW2ZpbHRlcnNPcGVuLCBzZXRGaWx0ZXJzT3Blbl0gPSB1c2VTdGF0ZShmYWxzZSk7CiAgY29uc3Qgc2VhcmNoUmVmID0gdXNlUmVmPEhUTUxJbnB1dEVsZW1lbnQ+KG51bGwpOwoKICB1c2VFZmZlY3QoKCkgPT4gewogICAgaWYgKHBhcmFtcy5nZXQoJ2ZvY3VzJykpIHsKICAgICAgc2VhcmNoUmVmLmN1cnJlbnQ/LmZvY3VzKCk7CiAgICAgIGNvbnN0IG5leHQgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKHBhcmFtcyk7CiAgICAgIG5leHQuZGVsZXRlKCdmb2N1cycpOwogICAgICBzZXRQYXJhbXMobmV4dCwgeyByZXBsYWNlOiB0cnVlIH0pOwogICAgfQogIH0sIFtwYXJhbXMsIHNldFBhcmFtc10pOwoKICBjb25zdCBhY2NlcHRlZCA9IHVzZU1lbW8oKCkgPT4gc3RhdGUucmVwb3MuZmlsdGVyKHIgPT4gci5zdGF0dXMgPT09ICdWZXJpZmllZCcpLCBbc3RhdGUucmVwb3NdKTsKICBjb25zdCBsYW5ndWFnZXMgPSB1c2VNZW1vKAogICAgKCkgPT4gWydBbnknLCAuLi5uZXcgU2V0KGFjY2VwdGVkLmZsYXRNYXAociA9PiByLmxhbmd1YWdlcykpXSwKICAgIFthY2NlcHRlZF0sCiAgKTsKICBjb25zdCBuZWVkbGUgPSBxLnRyaW0oKS50b0xvd2VyQ2FzZSgpOwoKICBjb25zdCBpc3N1ZXMgPSB1c2VNZW1vKCgpID0+IHsKICAgIGNvbnN0IGxpc3QgPSBzdGF0ZS5pc3N1ZXMuZmlsdGVyKGlzc3VlID0+IHsKICAgICAgY29uc3QgcmVwbyA9IGFjY2VwdGVkLmZpbmQociA9PiByLmlkID09PSBpc3N1ZS5yZXBvSWQpOwogICAgICBpZiAoIXJlcG8pIHJldHVybiBmYWxzZTsKICAgICAgaWYgKGxldmVsICE9PSAnQW55JyAmJiBpc3N1ZS5jb21wbGV4aXR5ICE9PSBsZXZlbCkgcmV0dXJuIGZhbHNlOwogICAgICBpZiAobGFuZyAhPT0gJ0FueScgJiYgIXJlcG8ubGFuZ3VhZ2VzLmluY2x1ZGVzKGxhbmcpKSByZXR1cm4gZmFsc2U7CiAgICAgIHJldHVybiBgJHtpc3N1ZS50aXRsZX0gJHtpc3N1ZS5pZH0gJHtyZXBvTmFtZShyZXBvKX1gLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMobmVlZGxlKTsKICAgIH0pOwogICAgcmV0dXJuIGxpc3Quc29ydCgoYSwgYikgPT4KICAgICAgc29ydCA9PT0gJ0JvdW50eScgPyBiLmJvdW50eSAtIGEuYm91bnR5CiAgICAgICAgOiBzb3J0ID09PSAnVGl0bGUnID8gYS50aXRsZS5sb2NhbGVDb21wYXJlKGIudGl0bGUpCiAgICAgICAgICA6IGIuY3JlYXRlZC5sb2NhbGVDb21wYXJlKGEuY3JlYXRlZCkpOwogIH0sIFtzdGF0ZS5pc3N1ZXMsIGFjY2VwdGVkLCBsZXZlbCwgbGFuZywgbmVlZGxlLCBzb3J0XSk7CgogIGNvbnN0IHJlcG9zID0gdXNlTWVtbygoKSA9PiB7CiAgICBjb25zdCBsaXN0ID0gYWNjZXB0ZWQuZmlsdGVyKHIgPT4KICAgICAgKGxhbmcgPT09ICdBbnknIHx8IHIubGFuZ3VhZ2VzLmluY2x1ZGVzKGxhbmcpKQogICAgICAmJiBgJHtyZXBvTmFtZShyKX0gJHtyLmRlc2NyaXB0aW9ufWAudG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhuZWVkbGUpKTsKICAgIGNvbnN0IGlzc3Vlc0ZvciA9IChpZDogc3RyaW5nKSA9PiBzdGF0ZS5pc3N1ZXMuZmlsdGVyKGkgPT4gaS5yZXBvSWQgPT09IGlkKTsKICAgIHJldHVybiBsaXN0LnNvcnQoKGEsIGIpID0+IHsKICAgICAgaWYgKHJlcG9Tb3J0ID09PSAnU3RhcnMnKSByZXR1cm4gYi5zdGFycyAtIGEuc3RhcnM7CiAgICAgIGlmIChyZXBvU29ydCA9PT0gJ09wZW4gaXNzdWVzJykgcmV0dXJuIGlzc3Vlc0ZvcihiLmlkKS5sZW5ndGggLSBpc3N1ZXNGb3IoYS5pZCkubGVuZ3RoOwogICAgICByZXR1cm4gZXNjcm93ZWRUb3RhbChpc3N1ZXNGb3IoYi5pZCksIFtdKSAtIGVzY3Jvd2VkVG90YWwoaXNzdWVzRm9yKGEuaWQpLCBbXSk7CiAgICB9KTsKICB9LCBbYWNjZXB0ZWQsIGxhbmcsIG5lZWRsZSwgcmVwb1NvcnQsIHN0YXRlLmlzc3Vlc10pOwoKICBjb25zdCBvcmdzID0gdXNlTWVtbygoKSA9PiB7CiAgICBjb25zdCBuYW1lcyA9IFsuLi5uZXcgU2V0KGFjY2VwdGVkLm1hcChyID0+IHIub3JnKSldOwogICAgcmV0dXJuIG5hbWVzCiAgICAgIC5maWx0ZXIobyA9PiBvLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMobmVlZGxlKSkKICAgICAgLm1hcChvID0+ICh7IG9yZzogbywgcmVwb3M6IGFjY2VwdGVkLmZpbHRlcihyID0+IHIub3JnID09PSBvKSB9KSk7CiAgfSwgW2FjY2VwdGVkLCBuZWVkbGVdKTsKCiAgY29uc3QgYWN0aXZlRmlsdGVycyA9IChsZXZlbCAhPT0gJ0FueScgPyAxIDogMCkgKyAobGFuZyAhPT0gJ0FueScgPyAxIDogMCk7CiAgY29uc3QgY291bnQgPSB0YWIgPT09ICdpc3N1ZXMnID8gaXNzdWVzLmxlbmd0aCA6IHRhYiA9PT0gJ3JlcG9zJyA/IHJlcG9zLmxlbmd0aCA6IG9yZ3MubGVuZ3RoOwoKICByZXR1cm4gKAogICAgPFBhZ2UgY2xhc3NOYW1lPSJleHBsb3JlIj4KICAgICAgPGRpdiBjbGFzc05hbWU9ImV4cGxvcmUtaW5uZXIiPgogICAgICAgIDxoZWFkZXIgY2xhc3NOYW1lPSJleHBsb3JlLWhlYWQiPgogICAgICAgICAgPGRpdj4KICAgICAgICAgICAgPGgxPkV4cGxvcmU8L2gxPgogICAgICAgICAgICA8cCBjbGFzc05hbWU9Im11dGVkIj5GdW5kZWQgaXNzdWVzIGFjcm9zcyBldmVyeSB2ZXJpZmllZCByZXBvc2l0b3J5LiBFYWNoIGJvdW50eSBpcyBlc2Nyb3dlZCBpbiB7UExBVEZPUk0uYXNzZXR9IGFuZCBwYWlkIG9uIG1lcmdlLjwvcD4KICAgICAgICAgIDwvZGl2PgogICAgICAgIDwvaGVhZGVyPgoKICAgICAgICA8ZGl2IGNsYXNzTmFtZT0iZXhwbG9yZS1iYXIiPgogICAgICAgICAgPFNlZ21lbnRlZAogICAgICAgICAgICBpZFByZWZpeD0iZXhwbG9yZSIKICAgICAgICAgICAgdmFsdWU9e3RhYn0KICAgICAgICAgICAgb3B0aW9ucz17VEFCU30KICAgICAgICAgICAgb25DaGFuZ2U9e3YgPT4gbmF2aWdhdGUodiA9PT0gJ2lzc3VlcycgPyAnL2V4cGxvcmUnIDogYC9leHBsb3JlLyR7dn1gKX0KICAgICAgICAgIC8+CiAgICAgICAgICA8bGFiZWwgY2xhc3NOYW1lPSJzZWFyY2ggZXhwbG9yZS1zZWFyY2giPgogICAgICAgICAgICA8U2VhcmNoIHNpemU9ezE0fSAvPgogICAgICAgICAgICA8aW5wdXQKICAgICAgICAgICAgICByZWY9e3NlYXJjaFJlZn0KICAgICAgICAgICAgICBhcmlhLWxhYmVsPSJTZWFyY2giCiAgICAgICAgICAgICAgcGxhY2Vob2xkZXI9e3RhYiA9PT0gJ29yZ3MnID8gJ1NlYXJjaCBvcmdhbml6YXRpb25z4oCmJyA6IHRhYiA9PT0gJ3JlcG9zJyA/ICdTZWFyY2ggcmVwb3NpdG9yaWVz4oCmJyA6ICdTZWFyY2ggaXNzdWVz4oCmJ30KICAgICAgICAgICAgICB2YWx1ZT17cX0KICAgICAgICAgICAgICBvbkNoYW5nZT17ZSA9PiBzZXRRKGUudGFyZ2V0LnZhbHVlKX0KICAgICAgICAgICAgLz4KICAgICAgICAgICAge3EgJiYgKAogICAgICAgICAgICAgIDxidXR0b24gY2xhc3NOYW1lPSJidG4gZ2hvc3QgaWNvbiB4cyIgYXJpYS1sYWJlbD0iQ2xlYXIgc2VhcmNoIiBvbkNsaWNrPXsoKSA9PiBzZXRRKCcnKX0+CiAgICAgICAgICAgICAgICA8WCBzaXplPXsxMn0gLz4KICAgICAgICAgICAgICA8L2J1dHRvbj4KICAgICAgICAgICAgKX0KICAgICAgICAgIDwvbGFiZWw+CiAgICAgICAgICB7dGFiICE9PSAnb3JncycgJiYgKAogICAgICAgICAgICA8YnV0dG9uCiAgICAgICAgICAgICAgY2xhc3NOYW1lPXthY3RpdmVGaWx0ZXJzID8gJ2J0biBzbSBvbicgOiAnYnRuIHNtJ30KICAgICAgICAgICAgICBhcmlhLWV4cGFuZGVkPXtmaWx0ZXJzT3Blbn0KICAgICAgICAgICAgICBvbkNsaWNrPXsoKSA9PiBzZXRGaWx0ZXJzT3BlbihvID0+ICFvKX0KICAgICAgICAgICAgPgogICAgICAgICAgICAgIDxTbGlkZXJzSG9yaXpvbnRhbCBzaXplPXsxM30gLz4KICAgICAgICAgICAgICBGaWx0ZXJzCiAgICAgICAgICAgICAge2FjdGl2ZUZpbHRlcnMgPiAwICYmIDxzcGFuIGNsYXNzTmFtZT0icGlwY291bnQgbnVtIj57YWN0aXZlRmlsdGVyc308L3NwYW4+fQogICAgICAgICAgICA8L2J1dHRvbj4KICAgICAgICAgICl9CiAgICAgICAgPC9kaXY+CgogICAgICAgIDxBbmltYXRlUHJlc2VuY2UgaW5pdGlhbD17ZmFsc2V9PgogICAgICAgICAge2ZpbHRlcnNPcGVuICYmIHRhYiAhPT0gJ29yZ3MnICYmICgKICAgICAgICAgICAgPG1vdGlvbi5kaXYKICAgICAgICAgICAgICBjbGFzc05hbWU9ImZpbHRlcnMiCiAgICAgICAgICAgICAgaW5pdGlhbD17eyBoZWlnaHQ6IDAsIG9wYWNpdHk6IDAgfX0KICAgICAgICAgICAgICBhbmltYXRlPXt7IGhlaWdodDogJ2F1dG8nLCBvcGFjaXR5OiAxIH19CiAgICAgICAgICAgICAgZXhpdD17eyBoZWlnaHQ6IDAsIG9wYWNpdHk6IDAgfX0KICAgICAgICAgICAgICB0cmFuc2l0aW9uPXt7IGR1cmF0aW9uOiAwLjIyLCBlYXNlOiBFQVNFIH19CiAgICAgICAgICAgID4KICAgICAgICAgICAgICA8ZGl2IGNsYXNzTmFtZT0iZmlsdGVycy1pbm5lciI+CiAgICAgICAgICAgICAgICB7dGFiID09PSAnaXNzdWVzJyAmJiAoCiAgICAgICAgICAgICAgICAgIDxsYWJlbCBjbGFzc05hbWU9ImZpZWxkIGlubGluZSI+CiAgICAgICAgICAgICAgICAgICAgPHNwYW4+Q29tcGxleGl0eTwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICA8c2VsZWN0IGNsYXNzTmFtZT0ic2VsZWN0IiBhcmlhLWxhYmVsPSJDb21wbGV4aXR5IiB2YWx1ZT17bGV2ZWx9CiAgICAgICAgICAgICAgICAgICAgICBvbkNoYW5nZT17ZSA9PiBzZXRMZXZlbChlLnRhcmdldC52YWx1ZSBhcyBDb21wbGV4aXR5IHwgJ0FueScpfT4KICAgICAgICAgICAgICAgICAgICAgIHtMRVZFTFMubWFwKGwgPT4gPG9wdGlvbiBrZXk9e2x9PntsfTwvb3B0aW9uPil9CiAgICAgICAgICAgICAgICAgICAgPC9zZWxlY3Q+CiAgICAgICAgICAgICAgICAgIDwvbGFiZWw+CiAgICAgICAgICAgICAgICApfQogICAgICAgICAgICAgICAgPGxhYmVsIGNsYXNzTmFtZT0iZmllbGQgaW5saW5lIj4KICAgICAgICAgICAgICAgICAgPHNwYW4+TGFuZ3VhZ2U8L3NwYW4+CiAgICAgICAgICAgICAgICAgIDxzZWxlY3QgY2xhc3NOYW1lPSJzZWxlY3QiIGFyaWEtbGFiZWw9Ikxhbmd1YWdlIiB2YWx1ZT17bGFuZ30gb25DaGFuZ2U9e2UgPT4gc2V0TGFuZyhlLnRhcmdldC52YWx1ZSl9PgogICAgICAgICAgICAgICAgICAgIHtsYW5ndWFnZXMubWFwKGwgPT4gPG9wdGlvbiBrZXk9e2x9PntsfTwvb3B0aW9uPil9CiAgICAgICAgICAgICAgICAgIDwvc2VsZWN0PgogICAgICAgICAgICAgICAgPC9sYWJlbD4KICAgICAgICAgICAgICAgIHt0YWIgPT09ICdpc3N1ZXMnICYmICgKICAgICAgICAgICAgICAgICAgPGxhYmVsIGNsYXNzTmFtZT0iZmllbGQgaW5saW5lIj4KICAgICAgICAgICAgICAgICAgICA8c3Bhbj5Tb3J0PC9zcGFuPgogICAgICAgICAgICAgICAgICAgIDxzZWxlY3QgY2xhc3NOYW1lPSJzZWxlY3QiIGFyaWEtbGFiZWw9IlNvcnQiIHZhbHVlPXtzb3J0fQogICAgICAgICAgICAgICAgICAgICAgb25DaGFuZ2U9e2UgPT4gc2V0U29ydChlLnRhcmdldC52YWx1ZSBhcyAodHlwZW9mIFNPUlRTKVtudW1iZXJdKX0+CiAgICAgICAgICAgICAgICAgICAgICB7U09SVFMubWFwKHMgPT4gPG9wdGlvbiBrZXk9e3N9PntzfTwvb3B0aW9uPil9CiAgICAgICAgICAgICAgICAgICAgPC9zZWxlY3Q+CiAgICAgICAgICAgICAgICAgIDwvbGFiZWw+CiAgICAgICAgICAgICAgICApfQogICAgICAgICAgICAgICAge3RhYiA9PT0gJ3JlcG9zJyAmJiAoCiAgICAgICAgICAgICAgICAgIDxsYWJlbCBjbGFzc05hbWU9ImZpZWxkIGlubGluZSI+CiAgICAgICAgICAgICAgICAgICAgPHNwYW4+U29ydDwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICA8c2VsZWN0IGNsYXNzTmFtZT0ic2VsZWN0IiBhcmlhLWxhYmVsPSJTb3J0IHJlcG9zaXRvcmllcyIgdmFsdWU9e3JlcG9Tb3J0fQogICAgICAgICAgICAgICAgICAgICAgb25DaGFuZ2U9e2UgPT4gc2V0UmVwb1NvcnQoZS50YXJnZXQudmFsdWUgYXMgKHR5cGVvZiBSRVBPX1NPUlRTKVtudW1iZXJdKX0+CiAgICAgICAgICAgICAgICAgICAgICB7UkVQT19TT1JUUy5tYXAocyA9PiA8b3B0aW9uIGtleT17c30+e3N9PC9vcHRpb24+KX0KICAgICAgICAgICAgICAgICAgICA8L3NlbGVjdD4KICAgICAgICAgICAgICAgICAgPC9sYWJlbD4KICAgICAgICAgICAgICAgICl9CiAgICAgICAgICAgICAgICA8YnV0dG9uIGNsYXNzTmFtZT0iYnRuIHNtIGdob3N0IiBvbkNsaWNrPXsoKSA9PiB7IHNldExldmVsKCdBbnknKTsgc2V0TGFuZygnQW55Jyk7IHNldFNvcnQoJ05ld2VzdCcpOyBzZXRSZXBvU29ydCgnVG90YWwgZXNjcm93Jyk7IH19PgogICAgICAgICAgICAgICAgICBSZXNldAogICAgICAgICAgICAgICAgPC9idXR0b24+CiAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgIDwvbW90aW9uLmRpdj4KICAgICAgICAgICl9CiAgICAgICAgPC9BbmltYXRlUHJlc2VuY2U+CgogICAgICAgIDxwIGNsYXNzTmFtZT0icmVzdWx0LWNvdW50IG51bSI+e2NvdW50fSB7dGFiID09PSAnaXNzdWVzJyA/ICdpc3N1ZXMnIDogdGFiID09PSAncmVwb3MnID8gJ3JlcG9zaXRvcmllcycgOiAnb3JnYW5pemF0aW9ucyd9PC9wPgoKICAgICAgICB7dGFiID09PSAnaXNzdWVzJyAmJiAoaXNzdWVzLmxlbmd0aCA/ICgKICAgICAgICAgIDxTdGFnZ2VyIGNsYXNzTmFtZT0ibGlzdCI+CiAgICAgICAgICAgIHtpc3N1ZXMubWFwKGlzc3VlID0+IHsKICAgICAgICAgICAgICBjb25zdCByZXBvID0gYWNjZXB0ZWQuZmluZChyID0+IHIuaWQgPT09IGlzc3VlLnJlcG9JZCkhOwogICAgICAgICAgICAgIHJldHVybiAoCiAgICAgICAgICAgICAgICA8SXRlbSBrZXk9e2lzc3VlLmlkfT4KICAgICAgICAgICAgICAgICAgPExpbmsgY2xhc3NOYW1lPSJsaXN0LXJvdyIgdG89e2AvaXNzdWUvJHtpc3N1ZS5pZH1gfT4KICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9Im1vbm8gZGltIGlzc3VlLW51bSI+I3tpc3N1ZS5pZH08L3NwYW4+CiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJjb2wiIHN0eWxlPXt7IGdhcDogMiwgbWluV2lkdGg6IDAsIGZsZXg6IDEgfX0+CiAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9InJvdy10aXRsZSI+e2lzc3VlLnRpdGxlfTwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT0icm93LXN1YiI+e3JlcG9OYW1lKHJlcG8pfTwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICA8L3NwYW4+CiAgICAgICAgICAgICAgICAgICAgPENoaXAgY2xhc3NOYW1lPSJoaWRlLXNtIj57aXNzdWUuY29tcGxleGl0eX08L0NoaXA+CiAgICAgICAgICAgICAgICAgICAgPENoaXAgY2xhc3NOYW1lPSJzb2xpZCBudW0iPiR7Zm9ybWF0TW9uZXkoaXNzdWUuYm91bnR5KX08L0NoaXA+CiAgICAgICAgICAgICAgICAgICAgPEFycm93VXBSaWdodCBzaXplPXsxNH0gY2xhc3NOYW1lPSJkaW0gaGlkZS1zbSIgLz4KICAgICAgICAgICAgICAgICAgPC9MaW5rPgogICAgICAgICAgICAgICAgPC9JdGVtPgogICAgICAgICAgICAgICk7CiAgICAgICAgICAgIH0pfQogICAgICAgICAgPC9TdGFnZ2VyPgogICAgICAgICkgOiA8RW1wdHkgdGl0bGU9Ik5vIGlzc3VlcyBtYXRjaCI+VHJ5IGEgZGlmZmVyZW50IHNlYXJjaCBvciBjbGVhciB0aGUgZmlsdGVycy48L0VtcHR5Pil9CgogICAgICAgIHt0YWIgPT09ICdyZXBvcycgJiYgKHJlcG9zLmxlbmd0aCA/ICgKICAgICAgICAgIDxTdGFnZ2VyIGNsYXNzTmFtZT0iZ3JpZCBjMyI+CiAgICAgICAgICAgIHtyZXBvcy5tYXAociA9PiAoCiAgICAgICAgICAgICAgPEl0ZW0ga2V5PXtyLmlkfSBjbGFzc05hbWU9ImNhcmQgcmVwby10aWxlIj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3NOYW1lPSJyb3ciIHN0eWxlPXt7IGdhcDogMTAgfX0+CiAgICAgICAgICAgICAgICAgIDxBdmF0YXIgbmFtZT17ci5vcmd9IG9yZz17ci5vcmd9IHNxdWFyZSBzaXplPSJsZyIgLz4KICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJjb2wiIHN0eWxlPXt7IGdhcDogMSwgbWluV2lkdGg6IDAgfX0+CiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJyb3ctdGl0bGUiPntyLm5hbWV9PC9zcGFuPgogICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT0icm93LXN1YiI+e3Iub3JnfTwvc3Bhbj4KICAgICAgICAgICAgICAgICAgPC9zcGFuPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8cCBjbGFzc05hbWU9Im11dGVkIHJlcG8tZGVzYyI+e3IuZGVzY3JpcHRpb259PC9wPgogICAgICAgICAgICAgICAge3IudG9waWNzICYmICgKICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9InJvdyB3cmFwIiBzdHlsZT17eyBnYXA6IDUgfX0+CiAgICAgICAgICAgICAgICAgICAge3IudG9waWNzLm1hcCh0ID0+IDxDaGlwIGtleT17dH0+e3R9PC9DaGlwPil9CiAgICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgKX0KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3NOYW1lPSJyZXBvLW1ldGEiPgogICAgICAgICAgICAgICAgICB7ci5sYW5ndWFnZXNbMF0gJiYgPHNwYW4gY2xhc3NOYW1lPSJyb3ciIHN0eWxlPXt7IGdhcDogNSB9fT48TGFuZ0RvdCBsYW5nPXtyLmxhbmd1YWdlc1swXX0gLz57ci5sYW5ndWFnZXNbMF19PC9zcGFuPn0KICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJyb3ciIHN0eWxlPXt7IGdhcDogNCB9fT48U3RhciBzaXplPXsxMX0gLz57Zm9ybWF0Q291bnQoci5zdGFycyl9PC9zcGFuPgogICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9InJvdyIgc3R5bGU9e3sgZ2FwOiA0IH19PjxHaXRGb3JrIHNpemU9ezExfSAvPntmb3JtYXRDb3VudChyLmZvcmtzKX08L3NwYW4+CiAgICAgICAgICAgICAgICAgIHtyLmxpY2Vuc2UgJiYgPHNwYW4+e3IubGljZW5zZX08L3NwYW4+fQogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzTmFtZT0icm93IHJlcG8tZm9vdCI+CiAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT0iZGltIiBzdHlsZT17eyBmb250U2l6ZTogJ3ZhcigtLXQyKScgfX0+CiAgICAgICAgICAgICAgICAgICAge3IudXBkYXRlZCA/IGBVcGRhdGVkICR7cmVsYXRpdmVEYXRlKHIudXBkYXRlZCl9YCA6ICcnfQogICAgICAgICAgICAgICAgICA8L3NwYW4+CiAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT0ic3BhY2VyIiAvPgogICAgICAgICAgICAgICAgICA8TGluayBjbGFzc05hbWU9ImJ0biB4cyIgdG89e2AvZXhwbG9yZT9xPSR7ZW5jb2RlVVJJQ29tcG9uZW50KHIubmFtZSl9YH0+CiAgICAgICAgICAgICAgICAgICAgeyhuID0+IGAke259ICR7biA9PT0gMSA/ICdpc3N1ZScgOiAnaXNzdWVzJ31gKShzdGF0ZS5pc3N1ZXMuZmlsdGVyKGkgPT4gaS5yZXBvSWQgPT09IHIuaWQpLmxlbmd0aCl9CiAgICAgICAgICAgICAgICAgIDwvTGluaz4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgIDwvSXRlbT4KICAgICAgICAgICAgKSl9CiAgICAgICAgICA8L1N0YWdnZXI+CiAgICAgICAgKSA6IDxFbXB0eSB0aXRsZT0iTm8gcmVwb3NpdG9yaWVzIG1hdGNoIj5UcnkgYSBkaWZmZXJlbnQgc2VhcmNoIHRlcm0uPC9FbXB0eT4pfQoKICAgICAgICB7dGFiID09PSAnb3JncycgJiYgKG9yZ3MubGVuZ3RoID8gKAogICAgICAgICAgPFN0YWdnZXIgY2xhc3NOYW1lPSJncmlkIGMzIj4KICAgICAgICAgICAge29yZ3MubWFwKCh7IG9yZywgcmVwb3M6IGxpc3QgfSkgPT4gKAogICAgICAgICAgICAgIDxJdGVtIGtleT17b3JnfSBjbGFzc05hbWU9ImNhcmQgcmVwby10aWxlIj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3NOYW1lPSJyb3ciIHN0eWxlPXt7IGdhcDogMTAgfX0+CiAgICAgICAgICAgICAgICAgIDxBdmF0YXIgbmFtZT17b3JnfSBvcmc9e29yZ30gc3F1YXJlIHNpemU9ImxnIiAvPgogICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9ImNvbCIgc3R5bGU9e3sgZ2FwOiAxLCBtaW5XaWR0aDogMCB9fT4KICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9InJvdy10aXRsZSI+e29yZ308L3NwYW4+CiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJyb3ctc3ViIj57bGlzdC5sZW5ndGh9IHtsaXN0Lmxlbmd0aCA9PT0gMSA/ICdyZXBvc2l0b3J5JyA6ICdyZXBvc2l0b3JpZXMnfTwvc3Bhbj4KICAgICAgICAgICAgICAgICAgPC9zcGFuPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICA8cCBjbGFzc05hbWU9Im11dGVkIHJlcG8tZGVzYyI+e2xpc3RbMF0uZGVzY3JpcHRpb259PC9wPgogICAgICAgICAgICAgICAgPGEgY2xhc3NOYW1lPSJidG4geHMiIGhyZWY9e2BodHRwczovL2dpdGh1Yi5jb20vJHtvcmd9YH0gdGFyZ2V0PSJfYmxhbmsiIHJlbD0ibm9yZWZlcnJlciI+CiAgICAgICAgICAgICAgICAgIEdpdEh1YjxBcnJvd1VwUmlnaHQgc2l6ZT17MTF9IC8+CiAgICAgICAgICAgICAgICA8L2E+CiAgICAgICAgICAgICAgPC9JdGVtPgogICAgICAgICAgICApKX0KICAgICAgICAgIDwvU3RhZ2dlcj4KICAgICAgICApIDogPEVtcHR5IHRpdGxlPSJObyBvcmdhbml6YXRpb25zIG1hdGNoIj5UcnkgYSBkaWZmZXJlbnQgc2VhcmNoIHRlcm0uPC9FbXB0eT4pfQogICAgICA8L2Rpdj4KICAgIDwvUGFnZT4KICApOwp9Cg== \ No newline at end of file +aW1wb3J0IHsgdXNlRWZmZWN0LCB1c2VNZW1vLCB1c2VSZWYsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnOwppbXBvcnQgeyBMaW5rLCB1c2VOYXZpZ2F0ZSwgdXNlU2VhcmNoUGFyYW1zIH0gZnJvbSAncmVhY3Qtcm91dGVyLWRvbSc7CmltcG9ydCB7IEFuaW1hdGVQcmVzZW5jZSwgbW90aW9uIH0gZnJvbSAnbW90aW9uL3JlYWN0JzsKaW1wb3J0IHsgQXJyb3dVcFJpZ2h0LCBHaXRGb3JrLCBTZWFyY2gsIFNsaWRlcnNIb3Jpem9udGFsLCBTdGFyLCBYIH0gZnJvbSAnbHVjaWRlLXJlYWN0JzsKaW1wb3J0IHsgZXNjcm93ZWRUb3RhbCwgZm9ybWF0Q291bnQsIGZvcm1hdE1vbmV5LCByZWxhdGl2ZURhdGUsIHJlcG9OYW1lLCB0eXBlIENvbXBsZXhpdHkgfSBmcm9tICcuLi9saWIvbW9kZWwnOwppbXBvcnQgeyB1c2VBcHAgfSBmcm9tICcuLi9saWIvc3RvcmUnOwppbXBvcnQgeyBQTEFURk9STSB9IGZyb20gJy4uL2xpYi9wbGF0Zm9ybSc7CmltcG9ydCB7IEF2YXRhciwgQ2hpcCwgRUFTRSwgRW1wdHksIEl0ZW0sIExhbmdEb3QsIFBhZ2UsIFNlZ21lbnRlZCwgU3RhZ2dlciB9IGZyb20gJy4uL2NvbXBvbmVudHMvdWknOwoKdHlwZSBUYWIgPSAnaXNzdWVzJyB8ICdyZXBvcycgfCAnb3Jncyc7Cgpjb25zdCBUQUJTID0gWwogIHsgdmFsdWU6ICdpc3N1ZXMnIGFzIGNvbnN0LCBsYWJlbDogJ0lzc3VlcycgfSwKICB7IHZhbHVlOiAncmVwb3MnIGFzIGNvbnN0LCBsYWJlbDogJ1JlcG9zaXRvcmllcycgfSwKICB7IHZhbHVlOiAnb3JncycgYXMgY29uc3QsIGxhYmVsOiAnT3JnYW5pemF0aW9ucycgfSwKXTsKCmNvbnN0IFNPUlRTID0gWydOZXdlc3QnLCAnQm91bnR5JywgJ1RpdGxlJ10gYXMgY29uc3Q7CmNvbnN0IFJFUE9fU09SVFMgPSBbJ1RvdGFsIGVzY3JvdycsICdTdGFycycsICdPcGVuIGlzc3VlcyddIGFzIGNvbnN0Owpjb25zdCBMRVZFTFM6IChDb21wbGV4aXR5IHwgJ0FueScpW10gPSBbJ0FueScsICdUcml2aWFsJywgJ01lZGl1bScsICdIaWdoJ107CgpleHBvcnQgZnVuY3Rpb24gRXhwbG9yZSh7IHRhYiB9OiB7IHRhYjogVGFiIH0pIHsKICBjb25zdCB7IHN0YXRlIH0gPSB1c2VBcHAoKTsKICBjb25zdCBuYXZpZ2F0ZSA9IHVzZU5hdmlnYXRlKCk7CiAgY29uc3QgW3BhcmFtcywgc2V0UGFyYW1zXSA9IHVzZVNlYXJjaFBhcmFtcygpOwogIGNvbnN0IFtxLCBzZXRRXSA9IHVzZVN0YXRlKHBhcmFtcy5nZXQoJ3EnKSA/PyAnJyk7CiAgY29uc3QgW2xldmVsLCBzZXRMZXZlbF0gPSB1c2VTdGF0ZTxDb21wbGV4aXR5IHwgJ0FueSc+KCdBbnknKTsKICBjb25zdCBbbGFuZywgc2V0TGFuZ10gPSB1c2VTdGF0ZSgnQW55Jyk7CiAgY29uc3QgW3NvcnQsIHNldFNvcnRdID0gdXNlU3RhdGU8KHR5cGVvZiBTT1JUUylbbnVtYmVyXT4oJ05ld2VzdCcpOwogIGNvbnN0IFtyZXBvU29ydCwgc2V0UmVwb1NvcnRdID0gdXNlU3RhdGU8KHR5cGVvZiBSRVBPX1NPUlRTKVtudW1iZXJdPignVG90YWwgZXNjcm93Jyk7CiAgY29uc3QgW2ZpbHRlcnNPcGVuLCBzZXRGaWx0ZXJzT3Blbl0gPSB1c2VTdGF0ZShmYWxzZSk7CiAgY29uc3Qgc2VhcmNoUmVmID0gdXNlUmVmPEhUTUxJbnB1dEVsZW1lbnQ+KG51bGwpOwoKICB1c2VFZmZlY3QoKCkgPT4gewogICAgaWYgKHBhcmFtcy5nZXQoJ2ZvY3VzJykpIHsKICAgICAgc2VhcmNoUmVmLmN1cnJlbnQ/LmZvY3VzKCk7CiAgICAgIGNvbnN0IG5leHQgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKHBhcmFtcyk7CiAgICAgIG5leHQuZGVsZXRlKCdmb2N1cycpOwogICAgICBzZXRQYXJhbXMobmV4dCwgeyByZXBsYWNlOiB0cnVlIH0pOwogICAgfQogIH0sIFtwYXJhbXMsIHNldFBhcmFtc10pOwoKICBjb25zdCBhY2NlcHRlZCA9IHVzZU1lbW8oKCkgPT4gc3RhdGUucmVwb3MuZmlsdGVyKHIgPT4gci5zdGF0dXMgPT09ICdWZXJpZmllZCcpLCBbc3RhdGUucmVwb3NdKTsKICBjb25zdCBsYW5ndWFnZXMgPSB1c2VNZW1vKAogICAgKCkgPT4gWydBbnknLCAuLi5uZXcgU2V0KGFjY2VwdGVkLmZsYXRNYXAociA9PiByLmxhbmd1YWdlcykpXSwKICAgIFthY2NlcHRlZF0sCiAgKTsKICBjb25zdCBuZWVkbGUgPSBxLnRyaW0oKS50b0xvd2VyQ2FzZSgpOwoKICBjb25zdCBpc3N1ZXMgPSB1c2VNZW1vKCgpID0+IHsKICAgIGNvbnN0IGxpc3QgPSBzdGF0ZS5pc3N1ZXMuZmlsdGVyKGlzc3VlID0+IHsKICAgICAgY29uc3QgcmVwbyA9IGFjY2VwdGVkLmZpbmQociA9PiByLmlkID09PSBpc3N1ZS5yZXBvSWQpOwogICAgICBpZiAoIXJlcG8pIHJldHVybiBmYWxzZTsKICAgICAgaWYgKGxldmVsICE9PSAnQW55JyAmJiBpc3N1ZS5jb21wbGV4aXR5ICE9PSBsZXZlbCkgcmV0dXJuIGZhbHNlOwogICAgICBpZiAobGFuZyAhPT0gJ0FueScgJiYgIXJlcG8ubGFuZ3VhZ2VzLmluY2x1ZGVzKGxhbmcpKSByZXR1cm4gZmFsc2U7CiAgICAgIHJldHVybiBgJHtpc3N1ZS50aXRsZX0gJHtpc3N1ZS5pZH0gJHtyZXBvTmFtZShyZXBvKX1gLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMobmVlZGxlKTsKICAgIH0pOwogICAgcmV0dXJuIGxpc3Quc29ydCgoYSwgYikgPT4KICAgICAgc29ydCA9PT0gJ0JvdW50eScgPyBiLmJvdW50eSAtIGEuYm91bnR5CiAgICAgICAgOiBzb3J0ID09PSAnVGl0bGUnID8gYS50aXRsZS5sb2NhbGVDb21wYXJlKGIudGl0bGUpCiAgICAgICAgICA6IGIuY3JlYXRlZC5sb2NhbGVDb21wYXJlKGEuY3JlYXRlZCkpOwogIH0sIFtzdGF0ZS5pc3N1ZXMsIGFjY2VwdGVkLCBsZXZlbCwgbGFuZywgbmVlZGxlLCBzb3J0XSk7CgogIGNvbnN0IHJlcG9zID0gdXNlTWVtbygoKSA9PiB7CiAgICBjb25zdCBsaXN0ID0gYWNjZXB0ZWQuZmlsdGVyKHIgPT4KICAgICAgKGxhbmcgPT09ICdBbnknIHx8IHIubGFuZ3VhZ2VzLmluY2x1ZGVzKGxhbmcpKQogICAgICAmJiBgJHtyZXBvTmFtZShyKX0gJHtyLmRlc2NyaXB0aW9ufWAudG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhuZWVkbGUpKTsKICAgIGNvbnN0IGlzc3Vlc0ZvciA9IChpZDogc3RyaW5nKSA9PiBzdGF0ZS5pc3N1ZXMuZmlsdGVyKGkgPT4gaS5yZXBvSWQgPT09IGlkKTsKICAgIHJldHVybiBsaXN0LnNvcnQoKGEsIGIpID0+IHsKICAgICAgaWYgKHJlcG9Tb3J0ID09PSAnU3RhcnMnKSByZXR1cm4gYi5zdGFycyAtIGEuc3RhcnM7CiAgICAgIGlmIChyZXBvU29ydCA9PT0gJ09wZW4gaXNzdWVzJykgcmV0dXJuIGlzc3Vlc0ZvcihiLmlkKS5sZW5ndGggLSBpc3N1ZXNGb3IoYS5pZCkubGVuZ3RoOwogICAgICByZXR1cm4gZXNjcm93ZWRUb3RhbChpc3N1ZXNGb3IoYi5pZCksIHN0YXRlLmFwcGxpY2F0aW9ucykgLSBlc2Nyb3dlZFRvdGFsKGlzc3Vlc0ZvcihhLmlkKSwgc3RhdGUuYXBwbGljYXRpb25zKTsKICAgIH0pOwogIH0sIFthY2NlcHRlZCwgbGFuZywgbmVlZGxlLCByZXBvU29ydCwgc3RhdGUuaXNzdWVzXSk7CgogIGNvbnN0IG9yZ3MgPSB1c2VNZW1vKCgpID0+IHsKICAgIGNvbnN0IG5hbWVzID0gWy4uLm5ldyBTZXQoYWNjZXB0ZWQubWFwKHIgPT4gci5vcmcpKV07CiAgICByZXR1cm4gbmFtZXMKICAgICAgLmZpbHRlcihvID0+IG8udG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhuZWVkbGUpKQogICAgICAubWFwKG8gPT4gKHsgb3JnOiBvLCByZXBvczogYWNjZXB0ZWQuZmlsdGVyKHIgPT4gci5vcmcgPT09IG8pIH0pKTsKICB9LCBbYWNjZXB0ZWQsIG5lZWRsZV0pOwoKICBjb25zdCBhY3RpdmVGaWx0ZXJzID0gKGxldmVsICE9PSAnQW55JyA/IDEgOiAwKSArIChsYW5nICE9PSAnQW55JyA/IDEgOiAwKTsKICBjb25zdCBjb3VudCA9IHRhYiA9PT0gJ2lzc3VlcycgPyBpc3N1ZXMubGVuZ3RoIDogdGFiID09PSAncmVwb3MnID8gcmVwb3MubGVuZ3RoIDogb3Jncy5sZW5ndGg7CgogIHJldHVybiAoCiAgICA8UGFnZSBjbGFzc05hbWU9ImV4cGxvcmUiPgogICAgICA8ZGl2IGNsYXNzTmFtZT0iZXhwbG9yZS1pbm5lciI+CiAgICAgICAgPGhlYWRlciBjbGFzc05hbWU9ImV4cGxvcmUtaGVhZCI+CiAgICAgICAgICA8ZGl2PgogICAgICAgICAgICA8aDE+RXhwbG9yZTwvaDE+CiAgICAgICAgICAgIDxwIGNsYXNzTmFtZT0ibXV0ZWQiPkZ1bmRlZCBpc3N1ZXMgYWNyb3NzIGV2ZXJ5IHZlcmlmaWVkIHJlcG9zaXRvcnkuIEVhY2ggYm91bnR5IGlzIGVzY3Jvd2VkIGluIHtQTEFURk9STS5hc3NldH0gYW5kIHBhaWQgb24gbWVyZ2UuPC9wPgogICAgICAgICAgPC9kaXY+CiAgICAgICAgPC9oZWFkZXI+CgogICAgICAgIDxkaXYgY2xhc3NOYW1lPSJleHBsb3JlLWJhciI+CiAgICAgICAgICA8U2VnbWVudGVkCiAgICAgICAgICAgIGlkUHJlZml4PSJleHBsb3JlIgogICAgICAgICAgICB2YWx1ZT17dGFifQogICAgICAgICAgICBvcHRpb25zPXtUQUJTfQogICAgICAgICAgICBvbkNoYW5nZT17diA9PiBuYXZpZ2F0ZSh2ID09PSAnaXNzdWVzJyA/ICcvZXhwbG9yZScgOiBgL2V4cGxvcmUvJHt2fWApfQogICAgICAgICAgLz4KICAgICAgICAgIDxsYWJlbCBjbGFzc05hbWU9InNlYXJjaCBleHBsb3JlLXNlYXJjaCI+CiAgICAgICAgICAgIDxTZWFyY2ggc2l6ZT17MTR9IC8+CiAgICAgICAgICAgIDxpbnB1dAogICAgICAgICAgICAgIHJlZj17c2VhcmNoUmVmfQogICAgICAgICAgICAgIGFyaWEtbGFiZWw9IlNlYXJjaCIKICAgICAgICAgICAgICBwbGFjZWhvbGRlcj17dGFiID09PSAnb3JncycgPyAnU2VhcmNoIG9yZ2FuaXphdGlvbnPigKYnIDogdGFiID09PSAncmVwb3MnID8gJ1NlYXJjaCByZXBvc2l0b3JpZXPigKYnIDogJ1NlYXJjaCBpc3N1ZXPigKYnfQogICAgICAgICAgICAgIHZhbHVlPXtxfQogICAgICAgICAgICAgIG9uQ2hhbmdlPXtlID0+IHNldFEoZS50YXJnZXQudmFsdWUpfQogICAgICAgICAgICAvPgogICAgICAgICAgICB7cSAmJiAoCiAgICAgICAgICAgICAgPGJ1dHRvbiBjbGFzc05hbWU9ImJ0biBnaG9zdCBpY29uIHhzIiBhcmlhLWxhYmVsPSJDbGVhciBzZWFyY2giIG9uQ2xpY2s9eygpID0+IHNldFEoJycpfT4KICAgICAgICAgICAgICAgIDxYIHNpemU9ezEyfSAvPgogICAgICAgICAgICAgIDwvYnV0dG9uPgogICAgICAgICAgICApfQogICAgICAgICAgPC9sYWJlbD4KICAgICAgICAgIHt0YWIgIT09ICdvcmdzJyAmJiAoCiAgICAgICAgICAgIDxidXR0b24KICAgICAgICAgICAgICBjbGFzc05hbWU9e2FjdGl2ZUZpbHRlcnMgPyAnYnRuIHNtIG9uJyA6ICdidG4gc20nfQogICAgICAgICAgICAgIGFyaWEtZXhwYW5kZWQ9e2ZpbHRlcnNPcGVufQogICAgICAgICAgICAgIG9uQ2xpY2s9eygpID0+IHNldEZpbHRlcnNPcGVuKG8gPT4gIW8pfQogICAgICAgICAgICA+CiAgICAgICAgICAgICAgPFNsaWRlcnNIb3Jpem9udGFsIHNpemU9ezEzfSAvPgogICAgICAgICAgICAgIEZpbHRlcnMKICAgICAgICAgICAgICB7YWN0aXZlRmlsdGVycyA+IDAgJiYgPHNwYW4gY2xhc3NOYW1lPSJwaXBjb3VudCBudW0iPnthY3RpdmVGaWx0ZXJzfTwvc3Bhbj59CiAgICAgICAgICAgIDwvYnV0dG9uPgogICAgICAgICAgKX0KICAgICAgICA8L2Rpdj4KCiAgICAgICAgPEFuaW1hdGVQcmVzZW5jZSBpbml0aWFsPXtmYWxzZX0+CiAgICAgICAgICB7ZmlsdGVyc09wZW4gJiYgdGFiICE9PSAnb3JncycgJiYgKAogICAgICAgICAgICA8bW90aW9uLmRpdgogICAgICAgICAgICAgIGNsYXNzTmFtZT0iZmlsdGVycyIKICAgICAgICAgICAgICBpbml0aWFsPXt7IGhlaWdodDogMCwgb3BhY2l0eTogMCB9fQogICAgICAgICAgICAgIGFuaW1hdGU9e3sgaGVpZ2h0OiAnYXV0bycsIG9wYWNpdHk6IDEgfX0KICAgICAgICAgICAgICBleGl0PXt7IGhlaWdodDogMCwgb3BhY2l0eTogMCB9fQogICAgICAgICAgICAgIHRyYW5zaXRpb249e3sgZHVyYXRpb246IDAuMjIsIGVhc2U6IEVBU0UgfX0KICAgICAgICAgICAgPgogICAgICAgICAgICAgIDxkaXYgY2xhc3NOYW1lPSJmaWx0ZXJzLWlubmVyIj4KICAgICAgICAgICAgICAgIHt0YWIgPT09ICdpc3N1ZXMnICYmICgKICAgICAgICAgICAgICAgICAgPGxhYmVsIGNsYXNzTmFtZT0iZmllbGQgaW5saW5lIj4KICAgICAgICAgICAgICAgICAgICA8c3Bhbj5Db21wbGV4aXR5PC9zcGFuPgogICAgICAgICAgICAgICAgICAgIDxzZWxlY3QgY2xhc3NOYW1lPSJzZWxlY3QiIGFyaWEtbGFiZWw9IkNvbXBsZXhpdHkiIHZhbHVlPXtsZXZlbH0KICAgICAgICAgICAgICAgICAgICAgIG9uQ2hhbmdlPXtlID0+IHNldExldmVsKGUudGFyZ2V0LnZhbHVlIGFzIENvbXBsZXhpdHkgfCAnQW55Jyl9PgogICAgICAgICAgICAgICAgICAgICAge0xFVkVMUy5tYXAobCA9PiA8b3B0aW9uIGtleT17bH0+e2x9PC9vcHRpb24+KX0KICAgICAgICAgICAgICAgICAgICA8L3NlbGVjdD4KICAgICAgICAgICAgICAgICAgPC9sYWJlbD4KICAgICAgICAgICAgICAgICl9CiAgICAgICAgICAgICAgICA8bGFiZWwgY2xhc3NOYW1lPSJmaWVsZCBpbmxpbmUiPgogICAgICAgICAgICAgICAgICA8c3Bhbj5MYW5ndWFnZTwvc3Bhbj4KICAgICAgICAgICAgICAgICAgPHNlbGVjdCBjbGFzc05hbWU9InNlbGVjdCIgYXJpYS1sYWJlbD0iTGFuZ3VhZ2UiIHZhbHVlPXtsYW5nfSBvbkNoYW5nZT17ZSA9PiBzZXRMYW5nKGUudGFyZ2V0LnZhbHVlKX0+CiAgICAgICAgICAgICAgICAgICAge2xhbmd1YWdlcy5tYXAobCA9PiA8b3B0aW9uIGtleT17bH0+e2x9PC9vcHRpb24+KX0KICAgICAgICAgICAgICAgICAgPC9zZWxlY3Q+CiAgICAgICAgICAgICAgICA8L2xhYmVsPgogICAgICAgICAgICAgICAge3RhYiA9PT0gJ2lzc3VlcycgJiYgKAogICAgICAgICAgICAgICAgICA8bGFiZWwgY2xhc3NOYW1lPSJmaWVsZCBpbmxpbmUiPgogICAgICAgICAgICAgICAgICAgIDxzcGFuPlNvcnQ8L3NwYW4+CiAgICAgICAgICAgICAgICAgICAgPHNlbGVjdCBjbGFzc05hbWU9InNlbGVjdCIgYXJpYS1sYWJlbD0iU29ydCIgdmFsdWU9e3NvcnR9CiAgICAgICAgICAgICAgICAgICAgICBvbkNoYW5nZT17ZSA9PiBzZXRTb3J0KGUudGFyZ2V0LnZhbHVlIGFzICh0eXBlb2YgU09SVFMpW251bWJlcl0pfT4KICAgICAgICAgICAgICAgICAgICAgIHtTT1JUUy5tYXAocyA9PiA8b3B0aW9uIGtleT17c30+e3N9PC9vcHRpb24+KX0KICAgICAgICAgICAgICAgICAgICA8L3NlbGVjdD4KICAgICAgICAgICAgICAgICAgPC9sYWJlbD4KICAgICAgICAgICAgICAgICl9CiAgICAgICAgICAgICAgICB7dGFiID09PSAncmVwb3MnICYmICgKICAgICAgICAgICAgICAgICAgPGxhYmVsIGNsYXNzTmFtZT0iZmllbGQgaW5saW5lIj4KICAgICAgICAgICAgICAgICAgICA8c3Bhbj5Tb3J0PC9zcGFuPgogICAgICAgICAgICAgICAgICAgIDxzZWxlY3QgY2xhc3NOYW1lPSJzZWxlY3QiIGFyaWEtbGFiZWw9IlNvcnQgcmVwb3NpdG9yaWVzIiB2YWx1ZT17cmVwb1NvcnR9CiAgICAgICAgICAgICAgICAgICAgICBvbkNoYW5nZT17ZSA9PiBzZXRSZXBvU29ydChlLnRhcmdldC52YWx1ZSBhcyAodHlwZW9mIFJFUE9fU09SVFMpW251bWJlcl0pfT4KICAgICAgICAgICAgICAgICAgICAgIHtSRVBPX1NPUlRTLm1hcChzID0+IDxvcHRpb24ga2V5PXtzfT57c308L29wdGlvbj4pfQogICAgICAgICAgICAgICAgICAgIDwvc2VsZWN0PgogICAgICAgICAgICAgICAgICA8L2xhYmVsPgogICAgICAgICAgICAgICAgKX0KICAgICAgICAgICAgICAgIDxidXR0b24gY2xhc3NOYW1lPSJidG4gc20gZ2hvc3QiIG9uQ2xpY2s9eygpID0+IHsgc2V0TGV2ZWwoJ0FueScpOyBzZXRMYW5nKCdBbnknKTsgc2V0U29ydCgnTmV3ZXN0Jyk7IHNldFJlcG9Tb3J0KCdUb3RhbCBlc2Nyb3cnKTsgfX0+CiAgICAgICAgICAgICAgICAgIFJlc2V0CiAgICAgICAgICAgICAgICA8L2J1dHRvbj4KICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgPC9tb3Rpb24uZGl2PgogICAgICAgICAgKX0KICAgICAgICA8L0FuaW1hdGVQcmVzZW5jZT4KCiAgICAgICAgPHAgY2xhc3NOYW1lPSJyZXN1bHQtY291bnQgbnVtIj57Y291bnR9IHt0YWIgPT09ICdpc3N1ZXMnID8gJ2lzc3VlcycgOiB0YWIgPT09ICdyZXBvcycgPyAncmVwb3NpdG9yaWVzJyA6ICdvcmdhbml6YXRpb25zJ308L3A+CgogICAgICAgIHt0YWIgPT09ICdpc3N1ZXMnICYmIChpc3N1ZXMubGVuZ3RoID8gKAogICAgICAgICAgPFN0YWdnZXIgY2xhc3NOYW1lPSJsaXN0Ij4KICAgICAgICAgICAge2lzc3Vlcy5tYXAoaXNzdWUgPT4gewogICAgICAgICAgICAgIGNvbnN0IHJlcG8gPSBhY2NlcHRlZC5maW5kKHIgPT4gci5pZCA9PT0gaXNzdWUucmVwb0lkKSE7CiAgICAgICAgICAgICAgcmV0dXJuICgKICAgICAgICAgICAgICAgIDxJdGVtIGtleT17aXNzdWUuaWR9PgogICAgICAgICAgICAgICAgICA8TGluayBjbGFzc05hbWU9Imxpc3Qtcm93IiB0bz17YC9pc3N1ZS8ke2lzc3VlLmlkfWB9PgogICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT0ibW9ubyBkaW0gaXNzdWUtbnVtIj4je2lzc3VlLmlkfTwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9ImNvbCIgc3R5bGU9e3sgZ2FwOiAyLCBtaW5XaWR0aDogMCwgZmxleDogMSB9fT4KICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT0icm93LXRpdGxlIj57aXNzdWUudGl0bGV9PC9zcGFuPgogICAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJyb3ctc3ViIj57cmVwb05hbWUocmVwbyl9PC9zcGFuPgogICAgICAgICAgICAgICAgICAgIDwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICA8Q2hpcCBjbGFzc05hbWU9ImhpZGUtc20iPntpc3N1ZS5jb21wbGV4aXR5fTwvQ2hpcD4KICAgICAgICAgICAgICAgICAgICA8Q2hpcCBjbGFzc05hbWU9InNvbGlkIG51bSI+JHtmb3JtYXRNb25leShpc3N1ZS5ib3VudHkpfTwvQ2hpcD4KICAgICAgICAgICAgICAgICAgICA8QXJyb3dVcFJpZ2h0IHNpemU9ezE0fSBjbGFzc05hbWU9ImRpbSBoaWRlLXNtIiAvPgogICAgICAgICAgICAgICAgICA8L0xpbms+CiAgICAgICAgICAgICAgICA8L0l0ZW0+CiAgICAgICAgICAgICAgKTsKICAgICAgICAgICAgfSl9CiAgICAgICAgICA8L1N0YWdnZXI+CiAgICAgICAgKSA6IDxFbXB0eSB0aXRsZT0iTm8gaXNzdWVzIG1hdGNoIj5UcnkgYSBkaWZmZXJlbnQgc2VhcmNoIG9yIGNsZWFyIHRoZSBmaWx0ZXJzLjwvRW1wdHk+KX0KCiAgICAgICAge3RhYiA9PT0gJ3JlcG9zJyAmJiAocmVwb3MubGVuZ3RoID8gKAogICAgICAgICAgPFN0YWdnZXIgY2xhc3NOYW1lPSJncmlkIGMzIj4KICAgICAgICAgICAge3JlcG9zLm1hcChyID0+ICgKICAgICAgICAgICAgICA8SXRlbSBrZXk9e3IuaWR9IGNsYXNzTmFtZT0iY2FyZCByZXBvLXRpbGUiPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9InJvdyIgc3R5bGU9e3sgZ2FwOiAxMCB9fT4KICAgICAgICAgICAgICAgICAgPEF2YXRhciBuYW1lPXtyLm9yZ30gb3JnPXtyLm9yZ30gc3F1YXJlIHNpemU9ImxnIiAvPgogICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9ImNvbCIgc3R5bGU9e3sgZ2FwOiAxLCBtaW5XaWR0aDogMCB9fT4KICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9InJvdy10aXRsZSI+e3IubmFtZX08L3NwYW4+CiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJyb3ctc3ViIj57ci5vcmd9PC9zcGFuPgogICAgICAgICAgICAgICAgICA8L3NwYW4+CiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgIDxwIGNsYXNzTmFtZT0ibXV0ZWQgcmVwby1kZXNjIj57ci5kZXNjcmlwdGlvbn08L3A+CiAgICAgICAgICAgICAgICB7ci50b3BpY3MgJiYgKAogICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzTmFtZT0icm93IHdyYXAiIHN0eWxlPXt7IGdhcDogNSB9fT4KICAgICAgICAgICAgICAgICAgICB7ci50b3BpY3MubWFwKHQgPT4gPENoaXAga2V5PXt0fT57dH08L0NoaXA+KX0KICAgICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgICApfQogICAgICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9InJlcG8tbWV0YSI+CiAgICAgICAgICAgICAgICAgIHtyLmxhbmd1YWdlc1swXSAmJiA8c3BhbiBjbGFzc05hbWU9InJvdyIgc3R5bGU9e3sgZ2FwOiA1IH19PjxMYW5nRG90IGxhbmc9e3IubGFuZ3VhZ2VzWzBdfSAvPntyLmxhbmd1YWdlc1swXX08L3NwYW4+fQogICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9InJvdyIgc3R5bGU9e3sgZ2FwOiA0IH19PjxTdGFyIHNpemU9ezExfSAvPntmb3JtYXRDb3VudChyLnN0YXJzKX08L3NwYW4+CiAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT0icm93IiBzdHlsZT17eyBnYXA6IDQgfX0+PEdpdEZvcmsgc2l6ZT17MTF9IC8+e2Zvcm1hdENvdW50KHIuZm9ya3MpfTwvc3Bhbj4KICAgICAgICAgICAgICAgICAge3IubGljZW5zZSAmJiA8c3Bhbj57ci5saWNlbnNlfTwvc3Bhbj59CiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgIDxkaXYgY2xhc3NOYW1lPSJyb3cgcmVwby1mb290Ij4KICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJkaW0iIHN0eWxlPXt7IGZvbnRTaXplOiAndmFyKC0tdDIpJyB9fT4KICAgICAgICAgICAgICAgICAgICB7ci51cGRhdGVkID8gYFVwZGF0ZWQgJHtyZWxhdGl2ZURhdGUoci51cGRhdGVkKX1gIDogJyd9CiAgICAgICAgICAgICAgICAgIDwvc3Bhbj4KICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPSJzcGFjZXIiIC8+CiAgICAgICAgICAgICAgICAgIDxMaW5rIGNsYXNzTmFtZT0iYnRuIHhzIiB0bz17YC9leHBsb3JlP3E9JHtlbmNvZGVVUklDb21wb25lbnQoci5uYW1lKX1gfT4KICAgICAgICAgICAgICAgICAgICB7KG4gPT4gYCR7bn0gJHtuID09PSAxID8gJ2lzc3VlJyA6ICdpc3N1ZXMnfWApKHN0YXRlLmlzc3Vlcy5maWx0ZXIoaSA9PiBpLnJlcG9JZCA9PT0gci5pZCkubGVuZ3RoKX0KICAgICAgICAgICAgICAgICAgPC9MaW5rPgogICAgICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgICAgICAgPC9JdGVtPgogICAgICAgICAgICApKX0KICAgICAgICAgIDwvU3RhZ2dlcj4KICAgICAgICApIDogPEVtcHR5IHRpdGxlPSJObyByZXBvc2l0b3JpZXMgbWF0Y2giPlRyeSBhIGRpZmZlcmVudCBzZWFyY2ggdGVybS48L0VtcHR5Pil9CgogICAgICAgIHt0YWIgPT09ICdvcmdzJyAmJiAob3Jncy5sZW5ndGggPyAoCiAgICAgICAgICA8U3RhZ2dlciBjbGFzc05hbWU9ImdyaWQgYzMiPgogICAgICAgICAgICB7b3Jncy5tYXAoKHsgb3JnLCByZXBvczogbGlzdCB9KSA9PiAoCiAgICAgICAgICAgICAgPEl0ZW0ga2V5PXtvcmd9IGNsYXNzTmFtZT0iY2FyZCByZXBvLXRpbGUiPgogICAgICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9InJvdyIgc3R5bGU9e3sgZ2FwOiAxMCB9fT4KICAgICAgICAgICAgICAgICAgPEF2YXRhciBuYW1lPXtvcmd9IG9yZz17b3JnfSBzcXVhcmUgc2l6ZT0ibGciIC8+CiAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT0iY29sIiBzdHlsZT17eyBnYXA6IDEsIG1pbldpZHRoOiAwIH19PgogICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT0icm93LXRpdGxlIj57b3JnfTwvc3Bhbj4KICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9InJvdy1zdWIiPntsaXN0Lmxlbmd0aH0ge2xpc3QubGVuZ3RoID09PSAxID8gJ3JlcG9zaXRvcnknIDogJ3JlcG9zaXRvcmllcyd9PC9zcGFuPgogICAgICAgICAgICAgICAgICA8L3NwYW4+CiAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgIDxwIGNsYXNzTmFtZT0ibXV0ZWQgcmVwby1kZXNjIj57bGlzdFswXS5kZXNjcmlwdGlvbn08L3A+CiAgICAgICAgICAgICAgICA8YSBjbGFzc05hbWU9ImJ0biB4cyIgaHJlZj17YGh0dHBzOi8vZ2l0aHViLmNvbS8ke29yZ31gfSB0YXJnZXQ9Il9ibGFuayIgcmVsPSJub3JlZmVycmVyIj4KICAgICAgICAgICAgICAgICAgR2l0SHViPEFycm93VXBSaWdodCBzaXplPXsxMX0gLz4KICAgICAgICAgICAgICAgIDwvYT4KICAgICAgICAgICAgICA8L0l0ZW0+CiAgICAgICAgICAgICkpfQogICAgICAgICAgPC9TdGFnZ2VyPgogICAgICAgICkgOiA8RW1wdHkgdGl0bGU9Ik5vIG9yZ2FuaXphdGlvbnMgbWF0Y2giPlRyeSBhIGRpZmZlcmVudCBzZWFyY2ggdGVybS48L0VtcHR5Pil9CiAgICAgIDwvZGl2PgogICAgPC9QYWdlPgogICk7Cn0K \ No newline at end of file