@cartanova/qgrid-cli 2.5.3 → 2.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +7 -2
- package/README.md +7 -2
- package/bundle/dist/application/monit/monit.frame.js +3 -3
- package/bundle/dist/application/qgrid/oauth.js +1 -1
- package/bundle/dist/application/qgrid/qgrid.dispatcher.js +36 -14
- package/bundle/dist/application/qgrid/qgrid.frame.js +19 -5
- package/bundle/dist/application/qgrid/tool-emulation-schema.js +93 -0
- package/bundle/dist/application/qgrid/tool-emulation.js +116 -96
- package/bundle/dist/application/request-log/request-log.model.js +3 -3
- package/bundle/dist/application/request-log-step/request-log-step.model.js +2 -2
- package/bundle/dist/application/token/token.model.js +2 -2
- package/bundle/dist/server-error-handler.js +15 -0
- package/bundle/dist/sonamu.config.js +4 -7
- package/bundle/dist/utils/providers/anthropic/anthropic-dispatcher.js +4 -3
- package/bundle/dist/utils/providers/anthropic/claude-session.js +7 -5
- package/bundle/dist/utils/providers/common/json-schema-keywords.js +77 -0
- package/bundle/dist/utils/providers/common/schema-validation.js +178 -0
- package/bundle/dist/utils/providers/common/strictifier.js +332 -42
- package/bundle/dist/utils/providers/openai/codex-worker.js +16 -8
- package/bundle/dist/utils/providers/openai/openai-dispatcher.js +5 -37
- package/bundle/dist/utils/providers/openai/openai-worker-pool-config.js +37 -0
- package/bundle/src/application/monit/monit.frame.ts +1 -1
- package/bundle/src/application/qgrid/qgrid-run-lifecycle.test.ts +103 -0
- package/bundle/src/application/qgrid/qgrid.dispatcher.test.ts +337 -0
- package/bundle/src/application/qgrid/qgrid.dispatcher.ts +45 -25
- package/bundle/src/application/qgrid/qgrid.frame.test.ts +580 -8
- package/bundle/src/application/qgrid/qgrid.frame.ts +28 -1
- package/bundle/src/application/qgrid/tool-emulation-schema.test.ts +188 -0
- package/bundle/src/application/qgrid/tool-emulation-schema.ts +177 -0
- package/bundle/src/application/qgrid/tool-emulation.test.ts +285 -67
- package/bundle/src/application/qgrid/tool-emulation.ts +199 -102
- package/bundle/src/server-error-handler.ts +22 -0
- package/bundle/src/sonamu.config.test.ts +37 -0
- package/bundle/src/sonamu.config.ts +2 -5
- package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.test.ts +44 -0
- package/bundle/src/utils/providers/anthropic/anthropic-dispatcher.ts +2 -2
- package/bundle/src/utils/providers/anthropic/claude-session.test.ts +10 -0
- package/bundle/src/utils/providers/anthropic/claude-session.ts +6 -1
- package/bundle/src/utils/providers/common/json-schema-keywords.ts +72 -0
- package/bundle/src/utils/providers/common/schema-validation.test.ts +284 -0
- package/bundle/src/utils/providers/common/schema-validation.ts +308 -0
- package/bundle/src/utils/providers/common/strictifier.test.ts +746 -0
- package/bundle/src/utils/providers/common/strictifier.ts +520 -52
- package/bundle/src/utils/providers/openai/codex-worker.test.ts +31 -0
- package/bundle/src/utils/providers/openai/codex-worker.ts +12 -4
- package/bundle/src/utils/providers/openai/openai-dispatcher.test.ts +6 -4
- package/bundle/src/utils/providers/openai/openai-dispatcher.ts +6 -79
- package/bundle/src/utils/providers/openai/openai-worker-pool-config.ts +80 -0
- package/bundle/web-dist/client/assets/{index-DXdGBfB8.js → index-CN_kMswh.js} +3 -3
- package/bundle/web-dist/client/assets/{logs-D_j1yxC4.js → logs-6nYj9OKL.js} +1 -1
- package/bundle/web-dist/client/assets/{monit-CIToyoxW.js → monit-CaPH9Uwc.js} +1 -1
- package/bundle/web-dist/client/assets/{routes-Dd21A-wi.js → routes-Dh5HPi-b.js} +1 -1
- package/bundle/web-dist/client/assets/{show-DfePWRbc.js → show-Be_LVd9d.js} +1 -1
- package/bundle/web-dist/client/assets/{tokens-B4MV2fdk.js → tokens-CAi3zu_0.js} +1 -1
- package/bundle/web-dist/client/index.html +1 -1
- package/bundle/web-dist/server/entry-server.generated.js +1 -1
- package/package.json +1 -1
- package/skills/qgrid/references/ai-sdk-provider-contract.md +34 -2
- package/skills/qgrid/references/anthropic-claude-code-runtime.md +3 -0
- package/skills/qgrid/references/cli-env-and-server-boot.md +1 -1
- package/skills/qgrid/references/decision-rationale.md +10 -1
- package/skills/qgrid/references/docs-workflow.md +9 -0
- package/skills/qgrid/references/repo-map.md +2 -1
- package/skills/qgrid/references/tool-calling-and-multiturn.md +145 -6
- package/skills/qgrid/references/verification-and-debugging.md +16 -1
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
//#region src/utils/providers/common/schema-validation.ts
|
|
3
|
+
/**
|
|
4
|
+
* Caller-provided schemas are serialized, copied, and recursively normalized before dispatch.
|
|
5
|
+
* Keep these limits comfortably above normal generated schemas while bounding that preprocessing
|
|
6
|
+
* well below the API's 10 MiB request-body ceiling.
|
|
7
|
+
*/
|
|
8
|
+
const CALLER_SCHEMA_LIMITS = {
|
|
9
|
+
maxUtf8Bytes: 512 * 1024,
|
|
10
|
+
maxNodes: 2e4,
|
|
11
|
+
maxDepth: 128
|
|
12
|
+
};
|
|
13
|
+
const ARGV_SAFE_MAX_UTF8_BYTES = 64 * 1024;
|
|
14
|
+
const ANTHROPIC_SCHEMA_ARGV_MAX_UTF8_BYTES = ARGV_SAFE_MAX_UTF8_BYTES;
|
|
15
|
+
var CallerSchemaValidationError = class extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(`qgrid: ${message}`);
|
|
18
|
+
this.name = "CallerSchemaValidationError";
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
function parseAndValidateCallerSchemas(input) {
|
|
22
|
+
const state = {
|
|
23
|
+
utf8Bytes: 0,
|
|
24
|
+
nodes: 0,
|
|
25
|
+
schemas: 0
|
|
26
|
+
};
|
|
27
|
+
let outputSchema;
|
|
28
|
+
if (input.jsonSchema !== void 0) {
|
|
29
|
+
addUtf8Bytes(Buffer.byteLength(input.jsonSchema, "utf8"), "jsonSchema", state);
|
|
30
|
+
let parsed;
|
|
31
|
+
try {
|
|
32
|
+
parsed = JSON.parse(input.jsonSchema);
|
|
33
|
+
} catch {
|
|
34
|
+
throw new CallerSchemaValidationError("jsonSchema must contain valid JSON");
|
|
35
|
+
}
|
|
36
|
+
if (!isRecord(parsed) || parsed.type !== "object") throw new CallerSchemaValidationError("jsonSchema top-level type must be \"object\"");
|
|
37
|
+
addComplexity(parsed, "jsonSchema", state);
|
|
38
|
+
state.schemas += 1;
|
|
39
|
+
outputSchema = parsed;
|
|
40
|
+
}
|
|
41
|
+
for (const [index, tool] of (input.tools ?? []).entries()) {
|
|
42
|
+
addToolMetadataUtf8Bytes(tool, index, state);
|
|
43
|
+
const path = `tools[${index}].inputSchema`;
|
|
44
|
+
addComplexity(tool.inputSchema, path, state, true);
|
|
45
|
+
addUtf8Bytes(jsonStringContentUtf8ByteUpperBound(stringifyJsonOrThrow(tool.inputSchema, path)), path, state);
|
|
46
|
+
state.schemas += 1;
|
|
47
|
+
}
|
|
48
|
+
return outputSchema;
|
|
49
|
+
}
|
|
50
|
+
function serializeAndValidateDispatchSchema(schema, provider) {
|
|
51
|
+
if (schema === void 0) return void 0;
|
|
52
|
+
const serialized = stringifyJsonOrThrow(schema, "dispatch schema");
|
|
53
|
+
if (Buffer.byteLength(serialized, "utf8") > CALLER_SCHEMA_LIMITS.maxUtf8Bytes) throw new CallerSchemaValidationError(`dispatch schema exceeds UTF-8 byte limit of ${CALLER_SCHEMA_LIMITS.maxUtf8Bytes}`);
|
|
54
|
+
if (provider === "anthropic") assertAnthropicSchemaArgvSize(serialized);
|
|
55
|
+
return serialized;
|
|
56
|
+
}
|
|
57
|
+
function assertAnthropicSchemaArgvSize(jsonSchema) {
|
|
58
|
+
if (Buffer.byteLength(jsonSchema, "utf8") > 65536) throw new CallerSchemaValidationError(`Anthropic dispatch schema exceeds argv UTF-8 byte limit of ${ANTHROPIC_SCHEMA_ARGV_MAX_UTF8_BYTES}`);
|
|
59
|
+
}
|
|
60
|
+
function stringifyJsonOrThrow(value, subject) {
|
|
61
|
+
let serialized;
|
|
62
|
+
try {
|
|
63
|
+
serialized = JSON.stringify(value);
|
|
64
|
+
} catch {
|
|
65
|
+
throw new CallerSchemaValidationError(`${subject} must be JSON-serializable`);
|
|
66
|
+
}
|
|
67
|
+
if (serialized === void 0) throw new CallerSchemaValidationError(`${subject} must be JSON-serializable`);
|
|
68
|
+
return serialized;
|
|
69
|
+
}
|
|
70
|
+
function addUtf8Bytes(byteLength, path, state) {
|
|
71
|
+
state.utf8Bytes += byteLength;
|
|
72
|
+
if (state.utf8Bytes > CALLER_SCHEMA_LIMITS.maxUtf8Bytes) throwLimitError(path, "UTF-8 byte", CALLER_SCHEMA_LIMITS.maxUtf8Bytes, state);
|
|
73
|
+
}
|
|
74
|
+
const TOOL_DESCRIPTION_ENTRY_PREFIX = "- ";
|
|
75
|
+
const TOOL_DESCRIPTION_NAME_SEPARATOR = ": ";
|
|
76
|
+
const TOOL_DESCRIPTION_INPUT_SCHEMA_PREFIX = "\n inputSchema: ";
|
|
77
|
+
const TOOL_DESCRIPTION_ENTRY_SEPARATOR = "\n";
|
|
78
|
+
function addToolMetadataUtf8Bytes(tool, index, state) {
|
|
79
|
+
const toolPath = `tools[${index}]`;
|
|
80
|
+
addUtf8Bytes(jsonStringContentUtf8ByteUpperBound(tool.name) * 2 + 2, `${toolPath}.name`, state);
|
|
81
|
+
if (tool.description !== void 0) addUtf8Bytes(jsonStringContentUtf8ByteUpperBound(tool.description), `${toolPath}.description`, state);
|
|
82
|
+
addUtf8Bytes(jsonStringContentUtf8ByteUpperBound(TOOL_DESCRIPTION_ENTRY_PREFIX) + jsonStringContentUtf8ByteUpperBound(TOOL_DESCRIPTION_NAME_SEPARATOR) + jsonStringContentUtf8ByteUpperBound(TOOL_DESCRIPTION_INPUT_SCHEMA_PREFIX) + (index === 0 ? 0 : jsonStringContentUtf8ByteUpperBound(TOOL_DESCRIPTION_ENTRY_SEPARATOR) + 1), toolPath, state);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Returns a conservative UTF-8 byte count for a string when it becomes JSON string content,
|
|
86
|
+
* without allocating the escaped representation. JSON.stringify uses short two-byte escapes for
|
|
87
|
+
* five control characters; counting every control character as a six-byte `\u00XX` escape keeps
|
|
88
|
+
* this an upper bound while avoiding a second large temporary string.
|
|
89
|
+
*/
|
|
90
|
+
function jsonStringContentUtf8ByteUpperBound(value) {
|
|
91
|
+
let bytes = 0;
|
|
92
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
93
|
+
const codeUnit = value.charCodeAt(index);
|
|
94
|
+
if (codeUnit === 34 || codeUnit === 92) {
|
|
95
|
+
bytes += 2;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (codeUnit <= 31) {
|
|
99
|
+
bytes += 6;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (codeUnit >= 55296 && codeUnit <= 56319) {
|
|
103
|
+
const nextCodeUnit = value.charCodeAt(index + 1);
|
|
104
|
+
if (nextCodeUnit >= 56320 && nextCodeUnit <= 57343) {
|
|
105
|
+
bytes += 4;
|
|
106
|
+
index += 1;
|
|
107
|
+
} else bytes += 6;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (codeUnit >= 56320 && codeUnit <= 57343) {
|
|
111
|
+
bytes += 6;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
bytes += codeUnit <= 127 ? 1 : codeUnit <= 2047 ? 2 : 3;
|
|
115
|
+
}
|
|
116
|
+
return bytes;
|
|
117
|
+
}
|
|
118
|
+
function addComplexity(root, path, state, precheckUtf8Bytes = false) {
|
|
119
|
+
const rawUtf8Bytes = { value: 0 };
|
|
120
|
+
if (precheckUtf8Bytes && typeof root === "string") addRawUtf8LowerBound(root, path, state, rawUtf8Bytes);
|
|
121
|
+
addNode(path, state);
|
|
122
|
+
if (root === null || typeof root !== "object") return;
|
|
123
|
+
const stack = [{
|
|
124
|
+
value: root,
|
|
125
|
+
depth: 0
|
|
126
|
+
}];
|
|
127
|
+
while (stack.length > 0) {
|
|
128
|
+
const current = stack.pop();
|
|
129
|
+
if (Array.isArray(current.value)) {
|
|
130
|
+
for (let index = 0; index < current.value.length; index += 1) {
|
|
131
|
+
const depth = current.depth + 1;
|
|
132
|
+
assertDepthWithinLimit(depth, path);
|
|
133
|
+
addNode(path, state);
|
|
134
|
+
const child = current.value[index];
|
|
135
|
+
if (precheckUtf8Bytes && typeof child === "string") addRawUtf8LowerBound(child, path, state, rawUtf8Bytes);
|
|
136
|
+
if (child !== null && typeof child === "object") stack.push({
|
|
137
|
+
value: child,
|
|
138
|
+
depth
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
for (const key in current.value) {
|
|
144
|
+
if (!Object.hasOwn(current.value, key)) continue;
|
|
145
|
+
if (precheckUtf8Bytes) addRawUtf8LowerBound(key, path, state, rawUtf8Bytes);
|
|
146
|
+
const depth = current.depth + 1;
|
|
147
|
+
assertDepthWithinLimit(depth, path);
|
|
148
|
+
addNode(path, state);
|
|
149
|
+
const child = current.value[key];
|
|
150
|
+
if (precheckUtf8Bytes && typeof child === "string") addRawUtf8LowerBound(child, path, state, rawUtf8Bytes);
|
|
151
|
+
if (child !== null && typeof child === "object") stack.push({
|
|
152
|
+
value: child,
|
|
153
|
+
depth
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function addRawUtf8LowerBound(value, path, state, rawUtf8Bytes) {
|
|
159
|
+
rawUtf8Bytes.value += Buffer.byteLength(value, "utf8");
|
|
160
|
+
if (state.utf8Bytes + rawUtf8Bytes.value > CALLER_SCHEMA_LIMITS.maxUtf8Bytes) throwLimitError(path, "UTF-8 byte", CALLER_SCHEMA_LIMITS.maxUtf8Bytes, state);
|
|
161
|
+
}
|
|
162
|
+
function assertDepthWithinLimit(depth, path) {
|
|
163
|
+
if (depth > CALLER_SCHEMA_LIMITS.maxDepth) throw new CallerSchemaValidationError(`${path} exceeds depth limit of ${CALLER_SCHEMA_LIMITS.maxDepth}`);
|
|
164
|
+
}
|
|
165
|
+
function addNode(path, state) {
|
|
166
|
+
state.nodes += 1;
|
|
167
|
+
if (state.nodes > CALLER_SCHEMA_LIMITS.maxNodes) throwLimitError(path, "node", CALLER_SCHEMA_LIMITS.maxNodes, state);
|
|
168
|
+
}
|
|
169
|
+
function throwLimitError(path, kind, limit, state) {
|
|
170
|
+
throw new CallerSchemaValidationError(state.schemas === 0 ? `${path} exceeds ${kind} limit of ${limit}` : `caller schemas exceed aggregate ${kind} limit of ${limit}`);
|
|
171
|
+
}
|
|
172
|
+
function isRecord(value) {
|
|
173
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
export { ANTHROPIC_SCHEMA_ARGV_MAX_UTF8_BYTES, ARGV_SAFE_MAX_UTF8_BYTES, CALLER_SCHEMA_LIMITS, CallerSchemaValidationError, assertAnthropicSchemaArgvSize, parseAndValidateCallerSchemas, serializeAndValidateDispatchSchema };
|
|
177
|
+
|
|
178
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLXZhbGlkYXRpb24uanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3V0aWxzL3Byb3ZpZGVycy9jb21tb24vc2NoZW1hLXZhbGlkYXRpb24udHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQnVmZmVyIH0gZnJvbSBcIm5vZGU6YnVmZmVyXCI7XG5cbmltcG9ydCB7IHR5cGUgSnNvblZhbHVlIH0gZnJvbSBcIi4uLy4uLy4uL2NvZGV4LXByb3RvY29sL3NlcmRlX2pzb24vSnNvblZhbHVlXCI7XG5cbi8qKlxuICogQ2FsbGVyLXByb3ZpZGVkIHNjaGVtYXMgYXJlIHNlcmlhbGl6ZWQsIGNvcGllZCwgYW5kIHJlY3Vyc2l2ZWx5IG5vcm1hbGl6ZWQgYmVmb3JlIGRpc3BhdGNoLlxuICogS2VlcCB0aGVzZSBsaW1pdHMgY29tZm9ydGFibHkgYWJvdmUgbm9ybWFsIGdlbmVyYXRlZCBzY2hlbWFzIHdoaWxlIGJvdW5kaW5nIHRoYXQgcHJlcHJvY2Vzc2luZ1xuICogd2VsbCBiZWxvdyB0aGUgQVBJJ3MgMTAgTWlCIHJlcXVlc3QtYm9keSBjZWlsaW5nLlxuICovXG5leHBvcnQgY29uc3QgQ0FMTEVSX1NDSEVNQV9MSU1JVFMgPSB7XG4gIG1heFV0ZjhCeXRlczogNTEyICogMTAyNCxcbiAgbWF4Tm9kZXM6IDIwXzAwMCxcbiAgbWF4RGVwdGg6IDEyOCxcbn0gYXMgY29uc3Q7XG5cbi8vIExpbnV4IHBlcm1pdHMgYXQgbW9zdCAxMjggS2lCIChNQVhfQVJHX1NUUkxFTikgZm9yIG9uZSBhcmd2IHZhbHVlLiBxZ3JpZCB1c2VzIGEgNTAlIHNhZmV0eVxuLy8gbWFyZ2luIGZvciBldmVyeSBhcmd2LWRlbGl2ZXJlZCBwYXlsb2FkIOKAlCBDbGF1ZGUgQ29kZSAtLWpzb24tc2NoZW1hIGhlcmUgYW5kIHRoZSBzeXN0ZW0tcHJvbXB0XG4vLyB0cmFuc3BvcnQgKGNsYXVkZS1zZXNzaW9uKSBib3RoIGRlcml2ZSBmcm9tIHRoaXMgc2luZ2xlIGNvbnN0YW50LlxuZXhwb3J0IGNvbnN0IEFSR1ZfU0FGRV9NQVhfVVRGOF9CWVRFUyA9IDY0ICogMTAyNDtcbmV4cG9ydCBjb25zdCBBTlRIUk9QSUNfU0NIRU1BX0FSR1ZfTUFYX1VURjhfQllURVMgPSBBUkdWX1NBRkVfTUFYX1VURjhfQllURVM7XG5cbmV4cG9ydCBjbGFzcyBDYWxsZXJTY2hlbWFWYWxpZGF0aW9uRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2U6IHN0cmluZykge1xuICAgIHN1cGVyKGBxZ3JpZDogJHttZXNzYWdlfWApO1xuICAgIHRoaXMubmFtZSA9IFwiQ2FsbGVyU2NoZW1hVmFsaWRhdGlvbkVycm9yXCI7XG4gIH1cbn1cblxuZXhwb3J0IGludGVyZmFjZSBDYWxsZXJTY2hlbWFzSW5wdXQge1xuICBqc29uU2NoZW1hPzogc3RyaW5nO1xuICB0b29scz86IEFycmF5PHsgbmFtZTogc3RyaW5nOyBkZXNjcmlwdGlvbj86IHN0cmluZzsgaW5wdXRTY2hlbWE6IHVua25vd24gfT47XG59XG5cbmludGVyZmFjZSBDb21wbGV4aXR5U3RhdGUge1xuICB1dGY4Qnl0ZXM6IG51bWJlcjtcbiAgbm9kZXM6IG51bWJlcjtcbiAgc2NoZW1hczogbnVtYmVyO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VBbmRWYWxpZGF0ZUNhbGxlclNjaGVtYXMoaW5wdXQ6IENhbGxlclNjaGVtYXNJbnB1dCk6IEpzb25WYWx1ZSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHN0YXRlOiBDb21wbGV4aXR5U3RhdGUgPSB7XG4gICAgdXRmOEJ5dGVzOiAwLFxuICAgIG5vZGVzOiAwLFxuICAgIHNjaGVtYXM6IDAsXG4gIH07XG4gIGxldCBvdXRwdXRTY2hlbWE6IEpzb25WYWx1ZSB8IHVuZGVmaW5lZDtcblxuICBpZiAoaW5wdXQuanNvblNjaGVtYSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgYWRkVXRmOEJ5dGVzKEJ1ZmZlci5ieXRlTGVuZ3RoKGlucHV0Lmpzb25TY2hlbWEsIFwidXRmOFwiKSwgXCJqc29uU2NoZW1hXCIsIHN0YXRlKTtcblxuICAgIGxldCBwYXJzZWQ6IHVua25vd247XG4gICAgdHJ5IHtcbiAgICAgIHBhcnNlZCA9IEpTT04ucGFyc2UoaW5wdXQuanNvblNjaGVtYSk7XG4gICAgfSBjYXRjaCB7XG4gICAgICB0aHJvdyBuZXcgQ2FsbGVyU2NoZW1hVmFsaWRhdGlvbkVycm9yKFwianNvblNjaGVtYSBtdXN0IGNvbnRhaW4gdmFsaWQgSlNPTlwiKTtcbiAgICB9XG5cbiAgICBpZiAoIWlzUmVjb3JkKHBhcnNlZCkgfHwgcGFyc2VkLnR5cGUgIT09IFwib2JqZWN0XCIpIHtcbiAgICAgIHRocm93IG5ldyBDYWxsZXJTY2hlbWFWYWxpZGF0aW9uRXJyb3IoJ2pzb25TY2hlbWEgdG9wLWxldmVsIHR5cGUgbXVzdCBiZSBcIm9iamVjdFwiJyk7XG4gICAgfVxuXG4gICAgYWRkQ29tcGxleGl0eShwYXJzZWQsIFwianNvblNjaGVtYVwiLCBzdGF0ZSk7XG4gICAgc3RhdGUuc2NoZW1hcyArPSAxO1xuICAgIG91dHB1dFNjaGVtYSA9IHBhcnNlZCBhcyBKc29uVmFsdWU7XG4gIH1cblxuICBmb3IgKGNvbnN0IFtpbmRleCwgdG9vbF0gb2YgKGlucHV0LnRvb2xzID8/IFtdKS5lbnRyaWVzKCkpIHtcbiAgICBhZGRUb29sTWV0YWRhdGFVdGY4Qnl0ZXModG9vbCwgaW5kZXgsIHN0YXRlKTtcblxuICAgIGNvbnN0IHBhdGggPSBgdG9vbHNbJHtpbmRleH1dLmlucHV0U2NoZW1hYDtcbiAgICBhZGRDb21wbGV4aXR5KHRvb2wuaW5wdXRTY2hlbWEsIHBhdGgsIHN0YXRlLCB0cnVlKTtcblxuICAgIGNvbnN0IHNlcmlhbGl6ZWQgPSBzdHJpbmdpZnlKc29uT3JUaHJvdyh0b29sLmlucHV0U2NoZW1hLCBwYXRoKTtcblxuICAgIC8vIGJ1aWxkVG9vbENhbGxTY2hlbWEgZW1iZWRzIHRoaXMgc2VyaWFsaXphdGlvbiBpbnNpZGUgYW5vdGhlciBKU09OIHN0cmluZy4gQ2hhcmdlIHRoZVxuICAgIC8vIGVzY2FwZWQgb3V0ZXItc3RyaW5nIHJlcHJlc2VudGF0aW9uIGJlZm9yZSB0aGF0IGNvbXBvc2VkIGRlc2NyaXB0aW9uIGlzIG1hdGVyaWFsaXplZC5cbiAgICBhZGRVdGY4Qnl0ZXMoanNvblN0cmluZ0NvbnRlbnRVdGY4Qnl0ZVVwcGVyQm91bmQoc2VyaWFsaXplZCksIHBhdGgsIHN0YXRlKTtcbiAgICBzdGF0ZS5zY2hlbWFzICs9IDE7XG4gIH1cblxuICByZXR1cm4gb3V0cHV0U2NoZW1hO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc2VyaWFsaXplQW5kVmFsaWRhdGVEaXNwYXRjaFNjaGVtYShcbiAgc2NoZW1hOiB1bmtub3duLFxuICBwcm92aWRlcj86IHN0cmluZyxcbik6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGlmIChzY2hlbWEgPT09IHVuZGVmaW5lZCkgcmV0dXJuIHVuZGVmaW5lZDtcblxuICBjb25zdCBzZXJpYWxpemVkID0gc3RyaW5naWZ5SnNvbk9yVGhyb3coc2NoZW1hLCBcImRpc3BhdGNoIHNjaGVtYVwiKTtcblxuICBjb25zdCBieXRlTGVuZ3RoID0gQnVmZmVyLmJ5dGVMZW5ndGgoc2VyaWFsaXplZCwgXCJ1dGY4XCIpO1xuICBpZiAoYnl0ZUxlbmd0aCA+IENBTExFUl9TQ0hFTUFfTElNSVRTLm1heFV0ZjhCeXRlcykge1xuICAgIHRocm93IG5ldyBDYWxsZXJTY2hlbWFWYWxpZGF0aW9uRXJyb3IoXG4gICAgICBgZGlzcGF0Y2ggc2NoZW1hIGV4Y2VlZHMgVVRGLTggYnl0ZSBsaW1pdCBvZiAke0NBTExFUl9TQ0hFTUFfTElNSVRTLm1heFV0ZjhCeXRlc31gLFxuICAgICk7XG4gIH1cbiAgaWYgKHByb3ZpZGVyID09PSBcImFudGhyb3BpY1wiKSB7XG4gICAgYXNzZXJ0QW50aHJvcGljU2NoZW1hQXJndlNpemUoc2VyaWFsaXplZCk7XG4gIH1cblxuICByZXR1cm4gc2VyaWFsaXplZDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGFzc2VydEFudGhyb3BpY1NjaGVtYUFyZ3ZTaXplKGpzb25TY2hlbWE6IHN0cmluZyk6IHZvaWQge1xuICBpZiAoQnVmZmVyLmJ5dGVMZW5ndGgoanNvblNjaGVtYSwgXCJ1dGY4XCIpID4gQU5USFJPUElDX1NDSEVNQV9BUkdWX01BWF9VVEY4X0JZVEVTKSB7XG4gICAgdGhyb3cgbmV3IENhbGxlclNjaGVtYVZhbGlkYXRpb25FcnJvcihcbiAgICAgIGBBbnRocm9waWMgZGlzcGF0Y2ggc2NoZW1hIGV4Y2VlZHMgYXJndiBVVEYtOCBieXRlIGxpbWl0IG9mICR7QU5USFJPUElDX1NDSEVNQV9BUkdWX01BWF9VVEY4X0JZVEVTfWAsXG4gICAgKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBzdHJpbmdpZnlKc29uT3JUaHJvdyh2YWx1ZTogdW5rbm93biwgc3ViamVjdDogc3RyaW5nKTogc3RyaW5nIHtcbiAgbGV0IHNlcmlhbGl6ZWQ6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgdHJ5IHtcbiAgICBzZXJpYWxpemVkID0gSlNPTi5zdHJpbmdpZnkodmFsdWUpO1xuICB9IGNhdGNoIHtcbiAgICB0aHJvdyBuZXcgQ2FsbGVyU2NoZW1hVmFsaWRhdGlvbkVycm9yKGAke3N1YmplY3R9IG11c3QgYmUgSlNPTi1zZXJpYWxpemFibGVgKTtcbiAgfVxuICBpZiAoc2VyaWFsaXplZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgdGhyb3cgbmV3IENhbGxlclNjaGVtYVZhbGlkYXRpb25FcnJvcihgJHtzdWJqZWN0fSBtdXN0IGJlIEpTT04tc2VyaWFsaXphYmxlYCk7XG4gIH1cbiAgcmV0dXJuIHNlcmlhbGl6ZWQ7XG59XG5cbmZ1bmN0aW9uIGFkZFV0ZjhCeXRlcyhieXRlTGVuZ3RoOiBudW1iZXIsIHBhdGg6IHN0cmluZywgc3RhdGU6IENvbXBsZXhpdHlTdGF0ZSk6IHZvaWQge1xuICBzdGF0ZS51dGY4Qnl0ZXMgKz0gYnl0ZUxlbmd0aDtcbiAgaWYgKHN0YXRlLnV0ZjhCeXRlcyA+IENBTExFUl9TQ0hFTUFfTElNSVRTLm1heFV0ZjhCeXRlcykge1xuICAgIHRocm93TGltaXRFcnJvcihwYXRoLCBcIlVURi04IGJ5dGVcIiwgQ0FMTEVSX1NDSEVNQV9MSU1JVFMubWF4VXRmOEJ5dGVzLCBzdGF0ZSk7XG4gIH1cbn1cblxuY29uc3QgVE9PTF9ERVNDUklQVElPTl9FTlRSWV9QUkVGSVggPSBcIi0gXCI7XG5jb25zdCBUT09MX0RFU0NSSVBUSU9OX05BTUVfU0VQQVJBVE9SID0gXCI6IFwiO1xuY29uc3QgVE9PTF9ERVNDUklQVElPTl9JTlBVVF9TQ0hFTUFfUFJFRklYID0gXCJcXG4gIGlucHV0U2NoZW1hOiBcIjtcbmNvbnN0IFRPT0xfREVTQ1JJUFRJT05fRU5UUllfU0VQQVJBVE9SID0gXCJcXG5cIjtcblxuZnVuY3Rpb24gYWRkVG9vbE1ldGFkYXRhVXRmOEJ5dGVzKFxuICB0b29sOiBOb25OdWxsYWJsZTxDYWxsZXJTY2hlbWFzSW5wdXRbXCJ0b29sc1wiXT5bbnVtYmVyXSxcbiAgaW5kZXg6IG51bWJlcixcbiAgc3RhdGU6IENvbXBsZXhpdHlTdGF0ZSxcbik6IHZvaWQge1xuICBjb25zdCB0b29sUGF0aCA9IGB0b29sc1ske2luZGV4fV1gO1xuXG4gIC8vIGJ1aWxkVG9vbENhbGxTY2hlbWEgcGxhY2VzIGV2ZXJ5IHRvb2wgbmFtZSBpbiBib3RoIHRoZSBlbnVtIGFuZCB0aGUgaHVtYW4tcmVhZGFibGUgdG9vbFxuICAvLyBkZXNjcmlwdGlvbi4gQ291bnQgYm90aCBlc2NhcGVkIG9jY3VycmVuY2VzLCBwbHVzIHRoZSBlbnVtJ3MgSlNPTiBzdHJpbmcgZGVsaW1pdGVycy5cbiAgY29uc3QgZW5jb2RlZE5hbWVCeXRlcyA9IGpzb25TdHJpbmdDb250ZW50VXRmOEJ5dGVVcHBlckJvdW5kKHRvb2wubmFtZSk7XG4gIGFkZFV0ZjhCeXRlcyhlbmNvZGVkTmFtZUJ5dGVzICogMiArIDIsIGAke3Rvb2xQYXRofS5uYW1lYCwgc3RhdGUpO1xuXG4gIGlmICh0b29sLmRlc2NyaXB0aW9uICE9PSB1bmRlZmluZWQpIHtcbiAgICBhZGRVdGY4Qnl0ZXMoXG4gICAgICBqc29uU3RyaW5nQ29udGVudFV0ZjhCeXRlVXBwZXJCb3VuZCh0b29sLmRlc2NyaXB0aW9uKSxcbiAgICAgIGAke3Rvb2xQYXRofS5kZXNjcmlwdGlvbmAsXG4gICAgICBzdGF0ZSxcbiAgICApO1xuICB9XG5cbiAgLy8gUGVyLXRvb2wgZnJhbWluZyBhbHNvIGdyb3dzIHdpdGggdGhlIG51bWJlciBvZiB0b29scyBldmVuIHdoZW4gYWxsIGNhbGxlciBzdHJpbmdzIGFyZSBlbXB0eS5cbiAgLy8gSW5jbHVkZSBib3RoIHRoZSB0b29sRGVzY3JpcHRpb25zIGpvaW4gc2VwYXJhdG9yIGFuZCB0aGUgZW51bSBjb21tYSBhZnRlciB0aGUgZmlyc3QgZW50cnkuXG4gIGNvbnN0IGZyYW1pbmdCeXRlcyA9XG4gICAganNvblN0cmluZ0NvbnRlbnRVdGY4Qnl0ZVVwcGVyQm91bmQoVE9PTF9ERVNDUklQVElPTl9FTlRSWV9QUkVGSVgpICtcbiAgICBqc29uU3RyaW5nQ29udGVudFV0ZjhCeXRlVXBwZXJCb3VuZChUT09MX0RFU0NSSVBUSU9OX05BTUVfU0VQQVJBVE9SKSArXG4gICAganNvblN0cmluZ0NvbnRlbnRVdGY4Qnl0ZVVwcGVyQm91bmQoVE9PTF9ERVNDUklQVElPTl9JTlBVVF9TQ0hFTUFfUFJFRklYKSArXG4gICAgKGluZGV4ID09PSAwID8gMCA6IGpzb25TdHJpbmdDb250ZW50VXRmOEJ5dGVVcHBlckJvdW5kKFRPT0xfREVTQ1JJUFRJT05fRU5UUllfU0VQQVJBVE9SKSArIDEpO1xuICBhZGRVdGY4Qnl0ZXMoZnJhbWluZ0J5dGVzLCB0b29sUGF0aCwgc3RhdGUpO1xufVxuXG4vKipcbiAqIFJldHVybnMgYSBjb25zZXJ2YXRpdmUgVVRGLTggYnl0ZSBjb3VudCBmb3IgYSBzdHJpbmcgd2hlbiBpdCBiZWNvbWVzIEpTT04gc3RyaW5nIGNvbnRlbnQsXG4gKiB3aXRob3V0IGFsbG9jYXRpbmcgdGhlIGVzY2FwZWQgcmVwcmVzZW50YXRpb24uIEpTT04uc3RyaW5naWZ5IHVzZXMgc2hvcnQgdHdvLWJ5dGUgZXNjYXBlcyBmb3JcbiAqIGZpdmUgY29udHJvbCBjaGFyYWN0ZXJzOyBjb3VudGluZyBldmVyeSBjb250cm9sIGNoYXJhY3RlciBhcyBhIHNpeC1ieXRlIGBcXHUwMFhYYCBlc2NhcGUga2VlcHNcbiAqIHRoaXMgYW4gdXBwZXIgYm91bmQgd2hpbGUgYXZvaWRpbmcgYSBzZWNvbmQgbGFyZ2UgdGVtcG9yYXJ5IHN0cmluZy5cbiAqL1xuZnVuY3Rpb24ganNvblN0cmluZ0NvbnRlbnRVdGY4Qnl0ZVVwcGVyQm91bmQodmFsdWU6IHN0cmluZyk6IG51bWJlciB7XG4gIGxldCBieXRlcyA9IDA7XG5cbiAgZm9yIChsZXQgaW5kZXggPSAwOyBpbmRleCA8IHZhbHVlLmxlbmd0aDsgaW5kZXggKz0gMSkge1xuICAgIGNvbnN0IGNvZGVVbml0ID0gdmFsdWUuY2hhckNvZGVBdChpbmRleCk7XG5cbiAgICBpZiAoY29kZVVuaXQgPT09IDB4MjIgfHwgY29kZVVuaXQgPT09IDB4NWMpIHtcbiAgICAgIGJ5dGVzICs9IDI7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgaWYgKGNvZGVVbml0IDw9IDB4MWYpIHtcbiAgICAgIGJ5dGVzICs9IDY7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgaWYgKGNvZGVVbml0ID49IDB4ZDgwMCAmJiBjb2RlVW5pdCA8PSAweGRiZmYpIHtcbiAgICAgIGNvbnN0IG5leHRDb2RlVW5pdCA9IHZhbHVlLmNoYXJDb2RlQXQoaW5kZXggKyAxKTtcbiAgICAgIGlmIChuZXh0Q29kZVVuaXQgPj0gMHhkYzAwICYmIG5leHRDb2RlVW5pdCA8PSAweGRmZmYpIHtcbiAgICAgICAgYnl0ZXMgKz0gNDtcbiAgICAgICAgaW5kZXggKz0gMTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIFdlbGwtZm9ybWVkIEpTT04uc3RyaW5naWZ5IGVzY2FwZXMgbG9uZSBzdXJyb2dhdGVzIGFzIGBcXHVYWFhYYC5cbiAgICAgICAgYnl0ZXMgKz0gNjtcbiAgICAgIH1cbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBpZiAoY29kZVVuaXQgPj0gMHhkYzAwICYmIGNvZGVVbml0IDw9IDB4ZGZmZikge1xuICAgICAgYnl0ZXMgKz0gNjtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIGJ5dGVzICs9IGNvZGVVbml0IDw9IDB4N2YgPyAxIDogY29kZVVuaXQgPD0gMHg3ZmYgPyAyIDogMztcbiAgfVxuXG4gIHJldHVybiBieXRlcztcbn1cblxuZnVuY3Rpb24gYWRkQ29tcGxleGl0eShcbiAgcm9vdDogdW5rbm93bixcbiAgcGF0aDogc3RyaW5nLFxuICBzdGF0ZTogQ29tcGxleGl0eVN0YXRlLFxuICBwcmVjaGVja1V0ZjhCeXRlcyA9IGZhbHNlLFxuKTogdm9pZCB7XG4gIGNvbnN0IHJhd1V0ZjhCeXRlcyA9IHsgdmFsdWU6IDAgfTtcbiAgaWYgKHByZWNoZWNrVXRmOEJ5dGVzICYmIHR5cGVvZiByb290ID09PSBcInN0cmluZ1wiKSB7XG4gICAgYWRkUmF3VXRmOExvd2VyQm91bmQocm9vdCwgcGF0aCwgc3RhdGUsIHJhd1V0ZjhCeXRlcyk7XG4gIH1cbiAgYWRkTm9kZShwYXRoLCBzdGF0ZSk7XG4gIGlmIChyb290ID09PSBudWxsIHx8IHR5cGVvZiByb290ICE9PSBcIm9iamVjdFwiKSByZXR1cm47XG5cbiAgY29uc3Qgc3RhY2s6IEFycmF5PHsgdmFsdWU6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgdW5rbm93bltdOyBkZXB0aDogbnVtYmVyIH0+ID0gW1xuICAgIHsgdmFsdWU6IHJvb3QgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCB1bmtub3duW10sIGRlcHRoOiAwIH0sXG4gIF07XG5cbiAgd2hpbGUgKHN0YWNrLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBjdXJyZW50ID0gc3RhY2sucG9wKCkhO1xuICAgIGlmIChBcnJheS5pc0FycmF5KGN1cnJlbnQudmFsdWUpKSB7XG4gICAgICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgY3VycmVudC52YWx1ZS5sZW5ndGg7IGluZGV4ICs9IDEpIHtcbiAgICAgICAgY29uc3QgZGVwdGggPSBjdXJyZW50LmRlcHRoICsgMTtcbiAgICAgICAgYXNzZXJ0RGVwdGhXaXRoaW5MaW1pdChkZXB0aCwgcGF0aCk7XG4gICAgICAgIGFkZE5vZGUocGF0aCwgc3RhdGUpO1xuXG4gICAgICAgIGNvbnN0IGNoaWxkID0gY3VycmVudC52YWx1ZVtpbmRleF07XG4gICAgICAgIGlmIChwcmVjaGVja1V0ZjhCeXRlcyAmJiB0eXBlb2YgY2hpbGQgPT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgICBhZGRSYXdVdGY4TG93ZXJCb3VuZChjaGlsZCwgcGF0aCwgc3RhdGUsIHJhd1V0ZjhCeXRlcyk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNoaWxkICE9PSBudWxsICYmIHR5cGVvZiBjaGlsZCA9PT0gXCJvYmplY3RcIikge1xuICAgICAgICAgIHN0YWNrLnB1c2goeyB2YWx1ZTogY2hpbGQgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCB1bmtub3duW10sIGRlcHRoIH0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IGtleSBpbiBjdXJyZW50LnZhbHVlKSB7XG4gICAgICBpZiAoIU9iamVjdC5oYXNPd24oY3VycmVudC52YWx1ZSwga2V5KSkgY29udGludWU7XG4gICAgICBpZiAocHJlY2hlY2tVdGY4Qnl0ZXMpIHtcbiAgICAgICAgYWRkUmF3VXRmOExvd2VyQm91bmQoa2V5LCBwYXRoLCBzdGF0ZSwgcmF3VXRmOEJ5dGVzKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGRlcHRoID0gY3VycmVudC5kZXB0aCArIDE7XG4gICAgICBhc3NlcnREZXB0aFdpdGhpbkxpbWl0KGRlcHRoLCBwYXRoKTtcbiAgICAgIGFkZE5vZGUocGF0aCwgc3RhdGUpO1xuXG4gICAgICBjb25zdCBjaGlsZCA9IGN1cnJlbnQudmFsdWVba2V5XTtcbiAgICAgIGlmIChwcmVjaGVja1V0ZjhCeXRlcyAmJiB0eXBlb2YgY2hpbGQgPT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgYWRkUmF3VXRmOExvd2VyQm91bmQoY2hpbGQsIHBhdGgsIHN0YXRlLCByYXdVdGY4Qnl0ZXMpO1xuICAgICAgfVxuICAgICAgaWYgKGNoaWxkICE9PSBudWxsICYmIHR5cGVvZiBjaGlsZCA9PT0gXCJvYmplY3RcIikge1xuICAgICAgICBzdGFjay5wdXNoKHsgdmFsdWU6IGNoaWxkIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgdW5rbm93bltdLCBkZXB0aCB9KTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxuZnVuY3Rpb24gYWRkUmF3VXRmOExvd2VyQm91bmQoXG4gIHZhbHVlOiBzdHJpbmcsXG4gIHBhdGg6IHN0cmluZyxcbiAgc3RhdGU6IENvbXBsZXhpdHlTdGF0ZSxcbiAgcmF3VXRmOEJ5dGVzOiB7IHZhbHVlOiBudW1iZXIgfSxcbik6IHZvaWQge1xuICByYXdVdGY4Qnl0ZXMudmFsdWUgKz0gQnVmZmVyLmJ5dGVMZW5ndGgodmFsdWUsIFwidXRmOFwiKTtcbiAgaWYgKHN0YXRlLnV0ZjhCeXRlcyArIHJhd1V0ZjhCeXRlcy52YWx1ZSA+IENBTExFUl9TQ0hFTUFfTElNSVRTLm1heFV0ZjhCeXRlcykge1xuICAgIHRocm93TGltaXRFcnJvcihwYXRoLCBcIlVURi04IGJ5dGVcIiwgQ0FMTEVSX1NDSEVNQV9MSU1JVFMubWF4VXRmOEJ5dGVzLCBzdGF0ZSk7XG4gIH1cbn1cblxuZnVuY3Rpb24gYXNzZXJ0RGVwdGhXaXRoaW5MaW1pdChkZXB0aDogbnVtYmVyLCBwYXRoOiBzdHJpbmcpOiB2b2lkIHtcbiAgaWYgKGRlcHRoID4gQ0FMTEVSX1NDSEVNQV9MSU1JVFMubWF4RGVwdGgpIHtcbiAgICB0aHJvdyBuZXcgQ2FsbGVyU2NoZW1hVmFsaWRhdGlvbkVycm9yKFxuICAgICAgYCR7cGF0aH0gZXhjZWVkcyBkZXB0aCBsaW1pdCBvZiAke0NBTExFUl9TQ0hFTUFfTElNSVRTLm1heERlcHRofWAsXG4gICAgKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBhZGROb2RlKHBhdGg6IHN0cmluZywgc3RhdGU6IENvbXBsZXhpdHlTdGF0ZSk6IHZvaWQge1xuICBzdGF0ZS5ub2RlcyArPSAxO1xuICBpZiAoc3RhdGUubm9kZXMgPiBDQUxMRVJfU0NIRU1BX0xJTUlUUy5tYXhOb2Rlcykge1xuICAgIHRocm93TGltaXRFcnJvcihwYXRoLCBcIm5vZGVcIiwgQ0FMTEVSX1NDSEVNQV9MSU1JVFMubWF4Tm9kZXMsIHN0YXRlKTtcbiAgfVxufVxuXG5mdW5jdGlvbiB0aHJvd0xpbWl0RXJyb3IoXG4gIHBhdGg6IHN0cmluZyxcbiAga2luZDogXCJVVEYtOCBieXRlXCIgfCBcIm5vZGVcIixcbiAgbGltaXQ6IG51bWJlcixcbiAgc3RhdGU6IENvbXBsZXhpdHlTdGF0ZSxcbik6IG5ldmVyIHtcbiAgdGhyb3cgbmV3IENhbGxlclNjaGVtYVZhbGlkYXRpb25FcnJvcihcbiAgICBzdGF0ZS5zY2hlbWFzID09PSAwXG4gICAgICA/IGAke3BhdGh9IGV4Y2VlZHMgJHtraW5kfSBsaW1pdCBvZiAke2xpbWl0fWBcbiAgICAgIDogYGNhbGxlciBzY2hlbWFzIGV4Y2VlZCBhZ2dyZWdhdGUgJHtraW5kfSBsaW1pdCBvZiAke2xpbWl0fWAsXG4gICk7XG59XG5cbmZ1bmN0aW9uIGlzUmVjb3JkKHZhbHVlOiB1bmtub3duKTogdmFsdWUgaXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICByZXR1cm4gdmFsdWUgIT09IG51bGwgJiYgdHlwZW9mIHZhbHVlID09PSBcIm9iamVjdFwiICYmICFBcnJheS5pc0FycmF5KHZhbHVlKTtcbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7OztBQVNBLE1BQWEsdUJBQXVCO0NBQ2xDLGNBQWMsTUFBTTtDQUNwQixVQUFVO0NBQ1YsVUFBVTtBQUNaO0FBS0EsTUFBYSwyQkFBMkIsS0FBSztBQUM3QyxN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
|