From 0d1ac618b613416c8d9e20a73c701be61157e361 Mon Sep 17 00:00:00 2001 From: Dion Whitehead <260221+dionjwa@users.noreply.github.com> Date: Wed, 27 Aug 2025 11:27:54 -0700 Subject: [PATCH] Fix and add test --- package.json | 2 +- src/core/index.ts | 5 +-- test/backward_compatibility_test.ts | 50 ++++++++++++++++++++++------- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index d208f23..236cf72 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@metapages/hash-query", - "version": "0.9.4", + "version": "0.9.5", "repository": { "type": "git", "url": "git+https://github.com/metapages/hash-query.git" diff --git a/src/core/index.ts b/src/core/index.ts index cecbaca..0b1d76e 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -3,7 +3,7 @@ * Important note: the internal hash string does NOT have the leading # */ -import stringify from 'fast-json-stable-stringify'; +import stringify from "fast-json-stable-stringify"; export type SetHashParamOpts = { modifyHistory?: boolean; @@ -27,7 +27,8 @@ export const stringToBase64String = (value: string): string => { export const stringFromBase64String = (value: string): string => { try { return decodeURIComponent(atob(value)); - } catch(e) { + } catch (e: any) { + console.error(`Failure to decode base64 string (error: ${e}): ${value}`, e); return decodeURIComponent(atob(decodeURIComponent(value))); } }; diff --git a/test/backward_compatibility_test.ts b/test/backward_compatibility_test.ts index ca2468b..ce1ea0c 100644 --- a/test/backward_compatibility_test.ts +++ b/test/backward_compatibility_test.ts @@ -1,11 +1,12 @@ /// -import { assertEquals } from '@std/assert'; +import { assert, assertEquals } from "@std/assert"; import { blobFromBase64String, stringFromBase64String, - stringToBase64String -} from '../src/core/index.ts'; + stringToBase64String, +} from "../src/core/index.ts"; +import { decode } from "node:punycode"; // Deno.test({ // name: "Backward compatibility with old double-encoded JSON objects", @@ -50,14 +51,20 @@ Deno.test({ }; // Create old format base64 (double-encoded) - const oldFormatBase64 = btoa(encodeURIComponent(JSON.stringify(jsonObject))); - + const oldFormatBase64 = btoa( + encodeURIComponent(JSON.stringify(jsonObject)) + ); + // Simulate URL encoding that would happen in hash parameters const urlEncodedOldFormat = encodeURIComponent(oldFormatBase64); - + // Test that the URL-encoded old format can be decoded correctly const decodedFromUrlEncoded = blobFromBase64String(urlEncodedOldFormat); - assertEquals(decodedFromUrlEncoded, jsonObject, "URL-encoded old format should decode correctly"); + assertEquals( + decodedFromUrlEncoded, + jsonObject, + "URL-encoded old format should decode correctly" + ); }, }); @@ -65,15 +72,36 @@ Deno.test({ name: "String encoding/decoding backward compatibility", async fn() { const testString = "Hello, World!"; - + // Test old format (double-encoded) const oldFormat = btoa(encodeURIComponent(testString)); const decodedFromOld = stringFromBase64String(oldFormat); - assertEquals(decodedFromOld, testString, "Old format string should decode correctly"); - + assertEquals( + decodedFromOld, + testString, + "Old format string should decode correctly" + ); + // Test new format (single-encoded) const newFormat = stringToBase64String(testString); const decodedFromNew = stringFromBase64String(newFormat); - assertEquals(decodedFromNew, testString, "New format string should decode correctly"); + assertEquals( + decodedFromNew, + testString, + "New format string should decode correctly" + ); + }, +}); + +Deno.test({ + name: "String encoding/decoding backward compatibility code", + async fn() { + let decoded = atob(existingBadlyEncodedString); + assert(decoded.startsWith("// Keep track of current video source")); + decoded = atob(decodeURIComponent(existingBadlyEncodedString)); + assert(decoded.startsWith("// Keep track of current video source")); }, }); + +const existingBadlyEncodedString = + "Ly8gS2VlcCB0cmFjayBvZiBjdXJyZW50IHZpZGVvIHNvdXJjZSBmb3IgY2xlYW51cApsZXQgY3VycmVudFZpZGVvU291cmNlID0gbnVsbDsKbGV0IGxvZ0VsZW1lbnQgPSBudWxsOwpsZXQgZmlsZU5hbWVFbGVtZW50ID0gbnVsbDsKCi8vIEN1c3RvbSBsb2dnaW5nIGZ1bmN0aW9uCmNvbnN0IGxvZyA9IChtZXNzYWdlLCBpc0Vycm9yID0gZmFsc2UpID0+IHsKICAvLyBDcmVhdGUgbG9nIGVsZW1lbnQgaWYgaXQgZG9lc24ndCBleGlzdAogIGlmICghbG9nRWxlbWVudCkgewogICAgbG9nRWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgbG9nRWxlbWVudC5pZCA9ICd2aWRlby1sb2dzJzsKICAgIGxvZ0VsZW1lbnQuc3R5bGUuY3NzVGV4dCA9IGAKICAgICAgcG9zaXRpb246IGZpeGVkOwogICAgICBib3R0b206IDEwcHg7CiAgICAgIGxlZnQ6IDEwcHg7CiAgICAgIHJpZ2h0OiAxMHB4OwogICAgICBtYXgtaGVpZ2h0OiAzMDBweDsKICAgICAgb3ZlcmZsb3cteTogYXV0bzsKICAgICAgYmFja2dyb3VuZDogcmdiYSgwLCAwLCAwLCAwLjgpOwogICAgICBjb2xvcjogd2hpdGU7CiAgICAgIGZvbnQtZmFtaWx5OiBtb25vc3BhY2U7CiAgICAgIGZvbnQtc2l6ZTogMTJweDsKICAgICAgcGFkZGluZzogMTBweDsKICAgICAgYm9yZGVyLXJhZGl1czogNHB4OwogICAgICB6LWluZGV4OiAxMDAwOwogICAgICBkaXNwbGF5OiBub25lOwogICAgICB3aGl0ZS1zcGFjZTogcHJlLXdyYXA7CiAgICBgOwogICAgcm9vdC5hcHBlbmRDaGlsZChsb2dFbGVtZW50KTsKICB9CiAgCiAgLy8gQWRkIHRpbWVzdGFtcCBhbmQgbWVzc2FnZQogIGNvbnN0IHRpbWVzdGFtcCA9IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKTsKICBjb25zdCBsb2dNZXNzYWdlID0gYFske3RpbWVzdGFtcH1dICR7bWVzc2FnZX1cbmA7CiAgCiAgaWYgKGlzRXJyb3IpIHsKICAgIGxvZ0VsZW1lbnQuc3R5bGUuY29sb3IgPSAnI2ZmNmI2Yic7CiAgICBsb2dFbGVtZW50LnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snOyAvLyBTaG93IGxvZ3Mgb24gZXJyb3IKICB9CiAgCiAgbG9nRWxlbWVudC50ZXh0Q29udGVudCArPSBsb2dNZXNzYWdlOwogIGxvZ0VsZW1lbnQuc2Nyb2xsVG9wID0gbG9nRWxlbWVudC5zY3JvbGxIZWlnaHQ7CiAgCiAgLy8gQWxzbyBsb2cgdG8gY29uc29sZQogIGlmIChpc0Vycm9yKSB7CiAgICBjb25zb2xlLmVycm9yKG1lc3NhZ2UpOwogIH0gZWxzZSB7CiAgICBjb25zb2xlLmxvZyhtZXNzYWdlKTsKICB9Cn07Cgpjb25zdCBzaG93RmlsZU5hbWUgPSAoZmlsZW5hbWUsIHNob3cgPSB0cnVlKSA9PiB7CiAgaWYgKCFzaG93KSB7CiAgICBpZiAoZmlsZU5hbWVFbGVtZW50KSB7CiAgICAgIGZpbGVOYW1lRWxlbWVudC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnOwogICAgfQogICAgcmV0dXJuOwogIH0KICAKICBpZiAoIWZpbGVOYW1lRWxlbWVudCkgewogICAgY29uc29sZS5sb2coIkNSRUFUSU5HIEZJTEUgRUxFTUVOVCIpCiAgICBmaWxlTmFtZUVsZW1lbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTsKICAgIGZpbGVOYW1lRWxlbWVudC5pZCA9ICd2aWRlby1maWxlbmFtZSc7CiAgICBmaWxlTmFtZUVsZW1lbnQuc3R5bGUuY3NzVGV4dCA9IGAKICAgICAgcG9zaXRpb246IGFic29sdXRlOwogICAgICBib3R0b206IDEwcHg7CiAgICAgIGxlZnQ6IDEwcHg7CiAgICAgIHJpZ2h0OiAxMHB4OwogICAgICBiYWNrZ3JvdW5kOiByZ2JhKDAsIDAsIDAsIDAuNyk7CiAgICAgIGNvbG9yOiB3aGl0ZTsKICAgICAgZm9udC1mYW1pbHk6IEFyaWFsLCBzYW5zLXNlcmlmOwogICAgICBmb250LXNpemU6IDE0cHg7CiAgICAgIHBhZGRpbmc6IDhweCAxMnB4OwogICAgICBib3JkZXItcmFkaXVzOiA0cHg7CiAgICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgICAgei1pbmRleDogNTAwOwogICAgYDsKICAgIHJvb3QuYXBwZW5kQ2hpbGQoZmlsZU5hbWVFbGVtZW50KTsKICB9CiAgCiAgZmlsZU5hbWVFbGVtZW50LnRleHRDb250ZW50ID0gZmlsZW5hbWU7CiAgZmlsZU5hbWVFbGVtZW50LnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snOwp9OwoKLy8gRnVuY3Rpb24gdG8gZ2V0IHZpZGVvIFVSTCBmcm9tIGlucHV0IChoYW5kbGVzIFVSTCwgRmlsZSwgb3IgQmxvYikKY29uc3QgZ2V0VmlkZW9VcmwgPSBhc3luYyAoaW5wdXQpID0+IHsKICAvLyBDbGVhbiB1cCBhbnkgcHJldmlvdXMgdmlkZW8gc291cmNlCiAgaWYgKGN1cnJlbnRWaWRlb1NvdXJjZSkgewogICAgVVJMLnJldm9rZU9iamVjdFVSTChjdXJyZW50VmlkZW9Tb3VyY2UpOwogICAgY3VycmVudFZpZGVvU291cmNlID0gbnVsbDsKICAgIGxvZygnQ2xlYW5lZCB1cCBwcmV2aW91cyB2aWRlbyBzb3VyY2UnKTsKICB9CiAgCiAgLy8gSWYgaW5wdXQgaXMgYSBzdHJpbmcgKFVSTCksIHJldHVybiBhcy1pcwogIGlmICh0eXBlb2YgaW5wdXQgPT09ICdzdHJpbmcnKSB7CiAgICBsb2coYElucHV0IGlzIGEgVVJMOiAke2lucHV0fWApOwogICAgcmV0dXJuIGlucHV0OwogIH0KICAKICAvLyBWYWxpZGF0ZSBpbnB1dAogIGlmICghaW5wdXQpIHsKICAgIGxvZygnRXJyb3I6IE5vIGlucHV0IHByb3ZpZGVkJywgdHJ1ZSk7CiAgICByZXR1cm4gbnVsbDsKICB9CiAgCiAgLy8gTG9nIGlucHV0IGRldGFpbHMKICBpZiAoaW5wdXQgaW5zdGFuY2VvZiBGaWxlKSB7CiAgICBsb2coYElucHV0IGlzIGEgRmlsZTogJHtpbnB1dC5uYW1lfSwgc2l6ZTogJHtpbnB1dC5zaXplfSBieXRlcywgdHlwZTogJHtpbnB1dC50eXBlfWApOwogIH0gZWxzZSBpZiAoaW5wdXQgaW5zdGFuY2VvZiBCbG9iKSB7CiAgICBsb2coYElucHV0IGlzIGEgQmxvYjogc2l6ZTogJHtpbnB1dC5zaXplfSBieXRlcywgdHlwZTogJHtpbnB1dC50eXBlfWApOwogIH0gZWxzZSB7CiAgICBsb2coYElucHV0IGlzIG5vdCBhIEZpbGUsIEJsb2IsIG9yIFVSTC4gVHlwZTogJHt0eXBlb2YgaW5wdXR9YCwgdHJ1ZSk7CiAgICBpZiAodHlwZW9mIGlucHV0ID09PSAnb2JqZWN0JykgewogICAgICBsb2coYElucHV0IG9iamVjdCBrZXlzOiAke09iamVjdC5rZXlzKGlucHV0KS5qb2luKCcsICcpfWAsIHRydWUpOwogICAgfQogICAgcmV0dXJuIG51bGw7CiAgfQogIAogIC8vIENoZWNrIGlmIGJsb2IgaGFzIGNvbnRlbnQKICBpZiAoaW5wdXQuc2l6ZSA9PT0gMCkgewogICAgbG9nKCdFcnJvcjogSW5wdXQgYmxvYiBpcyBlbXB0eSAoMCBieXRlcyknLCB0cnVlKTsKICAgIHJldHVybiBudWxsOwogIH0KICAKICB0cnkgewogICAgLy8gSGFuZGxlIE1JTUUgdHlwZSAtIGJlIG1vcmUgY2FyZWZ1bCBoZXJlCiAgICBsZXQgcHJvY2Vzc2VkQmxvYiA9IGlucHV0OwogICAgaWYgKCFpbnB1dC50eXBlIHx8IGlucHV0LnR5cGUgPT09ICcnKSB7CiAgICAgIGxvZygnTm8gTUlNRSB0eXBlIGRldGVjdGVkLCBjcmVhdGluZyBuZXcgYmxvYiB3aXRoIHZpZGVvL21wNCB0eXBlJyk7CiAgICAgIHRyeSB7CiAgICAgICAgcHJvY2Vzc2VkQmxvYiA9IG5ldyBCbG9iKFtpbnB1dF0sIHsgdHlwZTogJ3ZpZGVvL21wNCcgfSk7CiAgICAgICAgbG9nKGBOZXcgYmxvYiBjcmVhdGVkIHdpdGggdHlwZTogJHtwcm9jZXNzZWRCbG9iLnR5cGV9LCBzaXplOiAke3Byb2Nlc3NlZEJsb2Iuc2l6ZX1gKTsKICAgICAgfSBjYXRjaCAoYmxvYkVycm9yKSB7CiAgICAgICAgbG9nKGBFcnJvciBjcmVhdGluZyBuZXcgYmxvYjogJHtibG9iRXJyb3IubWVzc2FnZX1gLCB0cnVlKTsKICAgICAgICBwcm9jZXNzZWRCbG9iID0gaW5wdXQ7IC8vIEZhbGwgYmFjayB0byBvcmlnaW5hbAogICAgICB9CiAgICB9CiAgICAKICAgIC8vIENyZWF0ZSBvYmplY3QgVVJMCiAgICBjb25zdCB2aWRlb1VybCA9IFVSTC5jcmVhdGVPYmplY3RVUkwocHJvY2Vzc2VkQmxvYik7CiAgICBjdXJyZW50VmlkZW9Tb3VyY2UgPSB2aWRlb1VybDsgLy8gU3RvcmUgZm9yIGNsZWFudXAKICAgIGxvZyhgQ3JlYXRlZCBvYmplY3QgVVJMOiAke3ZpZGVvVXJsfWApOwogICAgcmV0dXJuIHZpZGVvVXJsOwogICAgCiAgfSBjYXRjaCAoZXJyb3IpIHsKICAgIGxvZyhgRXJyb3IgY3JlYXRpbmcgdmlkZW8gVVJMOiAke2Vycm9yLm1lc3NhZ2V9YCwgdHJ1ZSk7CiAgICByZXR1cm4gbnVsbDsKICB9Cn07CgovLyBGdW5jdGlvbiB0byBjcmVhdGUgdmlkZW8gZWxlbWVudCBmcm9tIFVSTApjb25zdCBjcmVhdGVWaWRlb0Zyb21VcmwgPSAodmlkZW9VcmwpID0+IHsKICBpZiAoIXZpZGVvVXJsKSB7CiAgICBsb2coJ0Vycm9yOiBObyB2aWRlbyBVUkwgcHJvdmlkZWQnLCB0cnVlKTsKICAgIHJldHVybjsKICB9CiAgCiAgLy8gQ2xlYXIgdGhlIHJvb3QgZGl2IChleGNlcHQgbG9nIGVsZW1lbnQpCiAgQXJyYXkuZnJvbShyb290LmNoaWxkcmVuKS5mb3JFYWNoKGNoaWxkID0+IHsKICAgIGlmIChjaGlsZC5pZCAhPT0gJ3ZpZGVvLWxvZ3MnKSB7CiAgICAgIHJvb3QucmVtb3ZlQ2hpbGQoY2hpbGQpOwogICAgfQogIH0pOwogIAogIHRyeSB7CiAgICAvLyBDcmVhdGUgdmlkZW8gZWxlbWVudAogICAgY29uc3QgdmlkZW8gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd2aWRlbycpOwogICAgdmlkZW8uY29udHJvbHMgPSB0cnVlOwogICAgdmlkZW8uYXV0b3BsYXkgPSB0cnVlOwogICAgdmlkZW8ucHJlbG9hZCA9ICdtZXRhZGF0YSc7CiAgICAKICAgIC8vIFNldCBleHBsaWNpdCBzdHlsaW5nIHRvIGVuc3VyZSB2aXNpYmlsaXR5CiAgICB2aWRlby5zdHlsZS53aWR0aCA9ICcxMDAlJzsKICAgIHZpZGVvLnN0eWxlLmhlaWdodCA9ICdhdXRvJzsKICAgIHZpZGVvLnN0eWxlLm1heFdpZHRoID0gJzEwMCUnOwogICAgdmlkZW8uc3R5bGUubWF4SGVpZ2h0ID0gJzEwMCUnOwogICAgdmlkZW8uc3R5bGUuZGlzcGxheSA9ICdibG9jayc7CiAgICB2aWRlby5zdHlsZS5iYWNrZ3JvdW5kQ29sb3IgPSAnIzAwMCc7CiAgICAKICAgIC8vIFNldCBhdHRyaWJ1dGVzIGZvciBiZXR0ZXIgY29tcGF0aWJpbGl0eQogICAgdmlkZW8uc2V0QXR0cmlidXRlKCdwbGF5c2lubGluZScsICcnKTsgLy8gRm9yIG1vYmlsZSBTYWZhcmkKICAgIHZpZGVvLnNldEF0dHJpYnV0ZSgnd2Via2l0LXBsYXlzaW5saW5lJywgJycpOyAvLyBGb3Igb2xkZXIgaU9TCiAgICAKICAgIC8vIEFkZCB2aWRlbyB0byBET00gRklSU1QsIHRoZW4gc2V0IHNvdXJjZQogICAgbG9nKCdBZGRpbmcgdmlkZW8gZWxlbWVudCB0byBET00uLi4nKTsKICAgIHJvb3QuYXBwZW5kQ2hpbGQodmlkZW8pOwogICAgCiAgICAvLyBWZXJpZnkgdmlkZW8gaXMgaW4gRE9NCiAgICBsb2coJ1ZpZGVvIGFkZGVkIHRvIERPTSwgdmVyaWZ5aW5nLi4uJyk7CiAgICBsb2coYFZpZGVvIGVsZW1lbnQgcGFyZW50OiAke3ZpZGVvLnBhcmVudEVsZW1lbnQgPyAnZm91bmQnIDogJ25vdCBmb3VuZCd9YCk7CiAgICBsb2coYFJvb3QgY2hpbGRyZW4gY291bnQ6ICR7cm9vdC5jaGlsZHJlbi5sZW5ndGh9YCk7CiAgICBsb2coYFZpZGVvIGVsZW1lbnQgaW4gRE9NOiAke2RvY3VtZW50LmNvbnRhaW5zKHZpZGVvKX1gKTsKICAgIAogICAgLy8gU2V0IHVwIGV2ZW50IGxpc3RlbmVycyBCRUZPUkUgc2V0dGluZyBzcmMKICAgIHZpZGVvLmFkZEV2ZW50TGlzdGVuZXIoJ2xvYWRzdGFydCcsICgpID0+IHsKICAgICAgbG9nKCdWaWRlbzogbG9hZHN0YXJ0IGV2ZW50IC0gYnJvd3NlciBzdGFydGVkIGxvYWRpbmcnKTsKICAgICAgbG9nKGBWaWRlbyBzcmM6ICR7dmlkZW8uc3JjfWApOwogICAgICAvLyBIaWRlIGxvZyBlbGVtZW50IG9uY2UgdmlkZW8gc3RhcnRzIGxvYWRpbmcgKHVubGVzcyB0aGVyZSBhcmUgZXJyb3JzKQogICAgICBpZiAobG9nRWxlbWVudCAmJiAhbG9nRWxlbWVudC50ZXh0Q29udGVudC5pbmNsdWRlcygnRXJyb3I6JykpIHsKICAgICAgICBsb2dFbGVtZW50LnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7CiAgICAgIH0KICAgIH0pOwogICAgCiAgICB2aWRlby5hZGRFdmVudExpc3RlbmVyKCdsb2FkZWRtZXRhZGF0YScsICgpID0+IHsKICAgICAgbG9nKCdWaWRlbzogbG9hZGVkbWV0YWRhdGEgZXZlbnQnKTsKICAgICAgbG9nKGAgIER1cmF0aW9uOiAke3ZpZGVvLmR1cmF0aW9ufXNgKTsKICAgICAgbG9nKGAgIERpbWVuc2lvbnM6ICR7dmlkZW8udmlkZW9XaWR0aH14JHt2aWRlby52aWRlb0hlaWdodH1gKTsKICAgICAgbG9nKGAgIFJlYWR5IHN0YXRlOiAke3ZpZGVvLnJlYWR5U3RhdGV9YCk7CiAgICAgIAogICAgICAvLyBEZWJ1ZyB2aWRlbyBlbGVtZW50IHZpc2liaWxpdHkKICAgICAgY29uc3QgcmVjdCA9IHZpZGVvLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpOwogICAgICBsb2coYCAgVmlkZW8gZWxlbWVudCBib3VuZHM6ICR7cmVjdC53aWR0aH14JHtyZWN0LmhlaWdodH0gYXQgKCR7cmVjdC54fSwgJHtyZWN0Lnl9KWApOwogICAgICBsb2coYCAgVmlkZW8gc3R5bGUgd2lkdGg6ICR7dmlkZW8uc3R5bGUud2lkdGh9YCk7CiAgICAgIGxvZyhgICBWaWRlbyBzdHlsZSBoZWlnaHQ6ICR7dmlkZW8uc3R5bGUuaGVpZ2h0fWApOwogICAgICBsb2coYCAgVmlkZW8gY29tcHV0ZWQgc3R5bGU6ICR7d2luZG93LmdldENvbXB1dGVkU3R5bGUodmlkZW8pLndpZHRofSB4ICR7d2luZG93LmdldENvbXB1dGVkU3R5bGUodmlkZW8pLmhlaWdodH1gKTsKICAgICAgbG9nKGAgIFZpZGVvIG9mZnNldFdpZHRoL0hlaWdodDogJHt2aWRlby5vZmZzZXRXaWR0aH14JHt2aWRlby5vZmZzZXRIZWlnaHR9YCk7CiAgICAgIGxvZyhgICBWaWRlbyBjbGllbnRXaWR0aC9IZWlnaHQ6ICR7dmlkZW8uY2xpZW50V2lkdGh9eCR7dmlkZW8uY2xpZW50SGVpZ2h0fWApOwogICAgICBsb2coYCAgVmlkZW8gZGlzcGxheSBzdHlsZTogJHt3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZSh2aWRlbykuZGlzcGxheX1gKTsKICAgICAgbG9nKGAgIFZpZGVvIHZpc2liaWxpdHk6ICR7d2luZG93LmdldENvbXB1dGVkU3R5bGUodmlkZW8pLnZpc2liaWxpdHl9YCk7CiAgICAgIGxvZyhgICBSb290IGVsZW1lbnQgZGltZW5zaW9uczogJHtyb290Lm9mZnNldFdpZHRofXgke3Jvb3Qub2Zmc2V0SGVpZ2h0fWApOwogICAgfSk7CiAgICAKICAgIHZpZGVvLmFkZEV2ZW50TGlzdGVuZXIoJ2xvYWRlZGRhdGEnLCAoKSA9PiB7CiAgICAgIGxvZygnVmlkZW8gbG9hZGVkIHN1Y2Nlc3NmdWxseSAtIHJlYWR5IHRvIHBsYXknKTsKICAgICAgc2V0T3V0cHV0KCd2aWRlb1N0YXR1cycsIHsgCiAgICAgICAgbG9hZGVkOiB0cnVlLCAKICAgICAgICB0eXBlOiB0eXBlb2YgdmlkZW9VcmwgPT09ICdzdHJpbmcnICYmIHZpZGVvVXJsLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/ICd1cmwnIDogJ2Jsb2InLAogICAgICAgIHVybDogdmlkZW9VcmwsCiAgICAgICAgZHVyYXRpb246IHZpZGVvLmR1cmF0aW9uLAogICAgICAgIGRpbWVuc2lvbnM6IGAke3ZpZGVvLnZpZGVvV2lkdGh9eCR7dmlkZW8udmlkZW9IZWlnaHR9YAogICAgICB9KTsKICAgIH0pOwogICAgCiAgICB2aWRlby5hZGRFdmVudExpc3RlbmVyKCdjYW5wbGF5JywgKCkgPT4gewogICAgICBsb2coJ1ZpZGVvOiBjYW5wbGF5IGV2ZW50IC0gZW5vdWdoIGRhdGEgbG9hZGVkIHRvIHN0YXJ0IHBsYXlpbmcnKTsKICAgIH0pOwogICAgCiAgICB2aWRlby5hZGRFdmVudExpc3RlbmVyKCdjYW5wbGF5dGhyb3VnaCcsICgpID0+IHsKICAgICAgbG9nKCdWaWRlbzogY2FucGxheXRocm91Z2ggZXZlbnQgLSBjYW4gcGxheSB0aHJvdWdoIHdpdGhvdXQgc3RvcHBpbmcnKTsKICAgIH0pOwogICAgCiAgICB2aWRlby5hZGRFdmVudExpc3RlbmVyKCdlcnJvcicsIChlKSA9PiB7CiAgICAgIGNvbnN0IGVyciA9IHZpZGVvLmVycm9yOwogICAgICBsb2coYFZpZGVvIGVycm9yIG9jY3VycmVkOmAsIHRydWUpOwogICAgICBsb2coYCAgRXJyb3IgY29kZTogJHtlcnIuY29kZX1gLCB0cnVlKTsKICAgICAgbG9nKGAgIEVycm9yIG1lc3NhZ2U6ICR7ZXJyLm1lc3NhZ2UgfHwgJ05vIGRldGFpbGVkIG1lc3NhZ2UnfWAsIHRydWUpOwogICAgICBsb2coYCAgVmlkZW8gc3JjOiAke3ZpZGVvLnNyY31gLCB0cnVlKTsKICAgICAgbG9nKGAgIFZpZGVvIHJlYWR5IHN0YXRlOiAke3ZpZGVvLnJlYWR5U3RhdGV9YCwgdHJ1ZSk7CiAgICAgIAogICAgICBzd2l0Y2goZXJyLmNvZGUpIHsKICAgICAgICBjYXNlIE1lZGlhRXJyb3IuTUVESUFfRVJSX0FCT1JURUQ6CiAgICAgICAgICBsb2coJ0Vycm9yOiBQbGF5YmFjayBhYm9ydGVkIGJ5IHRoZSB1c2VyJywgdHJ1ZSk7CiAgICAgICAgICBicmVhazsKICAgICAgICBjYXNlIE1lZGlhRXJyb3IuTUVESUFfRVJSX05FVFdPUks6CiAgICAgICAgICBsb2coJ0Vycm9yOiBOZXR3b3JrIGVycm9yIC0gVVJMIG1pZ2h0IGJlIGludmFsaWQgb3IgdW5yZWFjaGFibGUnLCB0cnVlKTsKICAgICAgICAgIGJyZWFrOwogICAgICAgIGNhc2UgTWVkaWFFcnJvci5NRURJQV9FUlJfREVDT0RFOgogICAgICAgICAgbG9nKCdFcnJvcjogRGVjb2RpbmcgZXJyb3IgLSBjb3JydXB0ZWQgZmlsZSBvciB1bnN1cHBvcnRlZCBjb2RlYycsIHRydWUpOwogICAgICAgICAgYnJlYWs7CiAgICAgICAgY2FzZSBNZWRpYUVycm9yLk1FRElBX0VSUl9TUkNfTk9UX1NVUFBPUlRFRDoKICAgICAgICAgIGxvZygnRXJyb3I6IFZpZGVvIGZvcm1hdC9jb2RlYyBub3Qgc3VwcG9ydGVkIGJ5IGJyb3dzZXInLCB0cnVlKTsKICAgICAgICAgIGJyZWFrOwogICAgICAgIGRlZmF1bHQ6CiAgICAgICAgICBsb2coJ0Vycm9yOiBVbmtub3duIGVycm9yIG9jY3VycmVkJywgdHJ1ZSk7CiAgICAgIH0KICAgICAgCiAgICAgIC8vIFNlbmQgZXJyb3IgZGV0YWlscyB0byBvdXRwdXQKICAgICAgc2V0T3V0cHV0KCd2aWRlb0Vycm9yJywgewogICAgICAgIGNvZGU6IGVyci5jb2RlLAogICAgICAgIG1lc3NhZ2U6IGVyci5tZXNzYWdlLAogICAgICAgIHNyYzogdmlkZW8uc3JjCiAgICAgIH0pOwogICAgfSk7CiAgICAKICAgIHZpZGVvLmFkZEV2ZW50TGlzdGVuZXIoJ3N0YWxsZWQnLCAoKSA9PiB7CiAgICAgIGxvZygnVmlkZW86IHN0YWxsZWQgZXZlbnQgLSBkb3dubG9hZCBoYXMgc3RvcHBlZCcsIHRydWUpOwogICAgfSk7CiAgICAKICAgIHZpZGVvLmFkZEV2ZW50TGlzdGVuZXIoJ2Fib3J0JywgKCkgPT4gewogICAgICBsb2coJ1ZpZGVvOiBhYm9ydCBldmVudCAtIGxvYWRpbmcgd2FzIGFib3J0ZWQnLCB0cnVlKTsKICAgIH0pOwogICAgCiAgICAvLyBOb3cgc2V0IHRoZSB2aWRlbyBzb3VyY2UgLSB0aGlzIHNob3VsZCB0cmlnZ2VyIGxvYWRpbmcKICAgIGxvZygnU2V0dGluZyB2aWRlbyBzb3VyY2UuLi4nKTsKICAgIHZpZGVvLnNyYyA9IHZpZGVvVXJsOwogICAgbG9nKGBWaWRlbyBzcmMgc2V0IHRvOiAke3ZpZGVvLnNyY31gKTsKICAgIAogICAgLy8gTG9hZCB0aGUgdmlkZW8gZXhwbGljaXRseQogICAgdmlkZW8ubG9hZCgpOwogICAgbG9nKCdDYWxsZWQgdmlkZW8ubG9hZCgpJyk7CiAgICAKICAgIC8vIENoZWNrIGlmIHZpZGVvIGxvYWRzIHdpdGhpbiByZWFzb25hYmxlIHRpbWUKICAgIGNvbnN0IGxvYWRUaW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiB7CiAgICAgIGlmICh2aWRlby5yZWFkeVN0YXRlID09PSAwKSB7IC8vIEhBVkVfTk9USElORyBzdGF0ZQogICAgICAgIGxvZygnVmlkZW8gZmFpbGVkIHRvIGxvYWQgYWZ0ZXIgMTAgc2Vjb25kcycsIHRydWUpOwogICAgICAgIGxvZyhgVmlkZW8gcmVhZHkgc3RhdGU6ICR7dmlkZW8ucmVhZHlTdGF0ZX1gLCB0cnVlKTsKICAgICAgICBsb2coYFZpZGVvIG5ldHdvcmsgc3RhdGU6ICR7dmlkZW8ubmV0d29ya1N0YXRlfWAsIHRydWUpOwogICAgICAgIGxvZyhgVmlkZW8gc3JjIHN0aWxsOiAke3ZpZGVvLnNyY31gLCB0cnVlKTsKICAgICAgICAKICAgICAgICAvLyBDaGVjayBpZiBVUkwgaXMgc3RpbGwgdmFsaWQgYnkgdHJ5aW5nIHRvIGZldGNoIGl0CiAgICAgICAgaWYgKHZpZGVvVXJsLnN0YXJ0c1dpdGgoJ2Jsb2I6JykpIHsKICAgICAgICAgIGZldGNoKHZpZGVvVXJsKQogICAgICAgICAgICAudGhlbihyZXNwb25zZSA9PiB7CiAgICAgICAgICAgICAgbG9nKGBCbG9iIFVSTCBmZXRjaCBzdGF0dXM6ICR7cmVzcG9uc2Uuc3RhdHVzfWApOwogICAgICAgICAgICAgIGxvZyhgQmxvYiBVUkwgY29udGVudCB0eXBlOiAke3Jlc3BvbnNlLmhlYWRlcnMuZ2V0KCdjb250ZW50LXR5cGUnKX1gKTsKICAgICAgICAgICAgfSkKICAgICAgICAgICAgLmNhdGNoKGZldGNoRXJyb3IgPT4gewogICAgICAgICAgICAgIGxvZyhgQmxvYiBVUkwgZmV0Y2ggZmFpbGVkOiAke2ZldGNoRXJyb3IubWVzc2FnZX1gLCB0cnVlKTsKICAgICAgICAgICAgfSk7CiAgICAgICAgfQogICAgICB9CiAgICB9LCAxMDAwMCk7CiAgICAKICAgIC8vIENsZWFyIHRpbWVvdXQgaWYgdmlkZW8gbG9hZHMgc3VjY2Vzc2Z1bGx5CiAgICB2aWRlby5hZGRFdmVudExpc3RlbmVyKCdsb2Fkc3RhcnQnLCAoKSA9PiB7CiAgICAgIGNsZWFyVGltZW91dChsb2FkVGltZW91dCk7CiAgICB9LCB7IG9uY2U6IHRydWUgfSk7CiAgICAKICB9IGNhdGNoIChlcnJvcikgewogICAgbG9nKGBFeGNlcHRpb24gaW4gY3JlYXRlVmlkZW9Gcm9tVXJsOiAke2Vycm9yLm1lc3NhZ2V9YCwgdHJ1ZSk7CiAgICBsb2coJ0Z1bGwgZXJyb3IgZGV0YWlsczogJyArIGVycm9yLnN0YWNrLCB0cnVlKTsKICB9Cn07CgovLyBIYW5kbGUgaW5wdXRzCmV4cG9ydCBjb25zdCBvbklucHV0cyA9IGFzeW5jIChpbnB1dHMpID0+IHsKICBsb2coYFJlY2VpdmVkIGlucHV0cyB3aXRoIGtleXM6ICR7T2JqZWN0LmtleXMoaW5wdXRzKS5qb2luKCcsICcpfWApOwoKICBsZXQga2V5ID0gIm1wNCI7CiAgbGV0IHZhbHVlID0gaW5wdXRzW2tleV07CiAgaWYgKCF2YWx1ZSkgewogICAga2V5ID0gT2JqZWN0LmtleXMoaW5wdXRzKVswXTsKICAgIHZhbHVlID0gaW5wdXRzW2tleV07CiAgfQoKICBsb2coYGtleT0ke2tleX0gdmFsdWU9JHtKU09OLnN0cmluZ2lmeSh2YWx1ZSl9YCkKICAKICBpZiAodmFsdWUpIHsKICAgIAogICAgbG9nKCdTdGFydGluZyB0byBwcm9jZXNzIHZpZGVvIGlucHV0Jyk7CiAgICBjb25zdCBkYXRhcmVmVXJsID0gdmFsdWU/LnZhbHVlOwogICAgaWYgKGRhdGFyZWZVcmwpIHsKICAgICAgbG9nKCdHb3QgZGF0YXJlZiBVUkw6ICcgKyBkYXRhcmVmVXJsKTsKICAgICAgY3JlYXRlVmlkZW9Gcm9tVXJsKGRhdGFyZWZVcmwpOwogICAgICBpZiAoa2V5ICE9PSAibXA0KSIpIHsKICAgICAgICBzaG93RmlsZU5hbWUoa2V5KTsKICAgICAgfQogICAgICAKICAgIH0gZWxzZSB7CiAgICAgIGNvbnN0IHZpZGVvVXJsID0gYXdhaXQgZ2V0VmlkZW9VcmwodmFsdWUpOwogICAgICBpZiAodmlkZW9VcmwpIHsKICAgICAgICBjcmVhdGVWaWRlb0Zyb21VcmwodmlkZW9VcmwpOwogICAgICB9IGVsc2UgewogICAgICAgIGxvZygnRmFpbGVkIHRvIGdldCB2aWRlbyBVUkwnLCB0cnVlKTsKICAgICAgfQogICAgfQogIH0gZWxzZSB7CiAgICAvLyBDbGVhbiB1cCBhbnkgcHJldmlvdXMgdmlkZW8gc291cmNlIHdoZW4gbm8gaW5wdXQKICAgIGlmIChjdXJyZW50VmlkZW9Tb3VyY2UpIHsKICAgICAgVVJMLnJldm9rZU9iamVjdFVSTChjdXJyZW50VmlkZW9Tb3VyY2UpOwogICAgICBjdXJyZW50VmlkZW9Tb3VyY2UgPSBudWxsOwogICAgICBsb2coJ0NsZWFuZWQgdXAgdmlkZW8gc291cmNlIChubyBpbnB1dCknKTsKICAgIH0KICAgIGxvZygnV2FpdGluZyBmb3IgTVA0IGlucHV0Li4uJyk7CiAgICAKICAgIC8vIENsZWFyIHJvb3QgYW5kIHNob3cgd2FpdGluZyBtZXNzYWdlCiAgICByb290LmlubmVySFRNTCA9ICcnOwogICAgaWYgKGxvZ0VsZW1lbnQpIHsKICAgICAgcm9vdC5hcHBlbmRDaGlsZChsb2dFbGVtZW50KTsKICAgIH0KICAgIAogICAgY29uc3Qgd2FpdGluZ0RpdiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpOwogICAgd2FpdGluZ0Rpdi5zdHlsZS5jc3NUZXh0ID0gJ3BhZGRpbmc6IDIwcHg7IHRleHQtYWxpZ246IGNlbnRlcjsgY29sb3I6ICM2NjY7JzsKICAgIHdhaXRpbmdEaXYudGV4dENvbnRlbnQgPSAnV2FpdGluZyBmb3IgTVA0IGZpbGUgaW5wdXQuLi4nOwogICAgcm9vdC5hcHBlbmRDaGlsZCh3YWl0aW5nRGl2KTsKICAgIAogICAgLy8gU2hvdyBsb2cgZWxlbWVudCB3aGVuIHdhaXRpbmcKICAgIGlmIChsb2dFbGVtZW50KSB7CiAgICAgIGxvZ0VsZW1lbnQuc3R5bGUuZGlzcGxheSA9ICdibG9jayc7CiAgICB9CiAgfQp9OwoKLy8gSGFuZGxlIHJlc2l6ZSBldmVudHMKZXhwb3J0IGNvbnN0IG9uUmVzaXplID0gKHdpZHRoLCBoZWlnaHQpID0+IHsKICBjb25zdCB2aWRlbyA9IHJvb3QucXVlcnlTZWxlY3RvcigndmlkZW8nKTsKICBpZiAodmlkZW8pIHsKICAgIHZpZGVvLnN0eWxlLm1heFdpZHRoID0gYCR7d2lkdGh9cHhgOwogICAgdmlkZW8uc3R5bGUubWF4SGVpZ2h0ID0gYCR7aGVpZ2h0fXB4YDsKICB9Cn07CgovLyBDbGVhbnVwIGZ1bmN0aW9uIGZvciB3aGVuIGNvbXBvbmVudCB1bm1vdW50cyAgCmV4cG9ydCBjb25zdCBjbGVhbnVwID0gKCkgPT4gewogIGlmIChjdXJyZW50VmlkZW9Tb3VyY2UpIHsKICAgIFVSTC5yZXZva2VPYmplY3RVUkwoY3VycmVudFZpZGVvU291cmNlKTsKICAgIGN1cnJlbnRWaWRlb1NvdXJjZSA9IG51bGw7CiAgICBsb2coJ0NsZWFuZWQgdXAgdmlkZW8gc291cmNlIChjb21wb25lbnQgZGVzdHJveWVkKScpOwogIH0KICBzaG93RmlsZU5hbWUoJycsIGZhbHNlKTsKfTs";