Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
5 changes: 3 additions & 2 deletions src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)));
}
};
Expand Down
50 changes: 39 additions & 11 deletions test/backward_compatibility_test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/// <reference lib="deno.ns" />
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",
Expand Down Expand Up @@ -50,30 +51,57 @@ 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"
);
},
});

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";