@mml-io/networked-dom-web-runner 0.0.42

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.
@@ -0,0 +1 @@
1
+ export * from "../src/index";
package/build/index.js ADDED
@@ -0,0 +1,246 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ FakeWebsocket: () => FakeWebsocket,
24
+ IframeObservableDOMFactory: () => IframeObservableDOMFactory,
25
+ NetworkedDOMWebRunnerClient: () => NetworkedDOMWebRunnerClient,
26
+ RunnerIframe: () => RunnerIframe
27
+ });
28
+ module.exports = __toCommonJS(src_exports);
29
+ __reExport(src_exports, require("@mml-io/networked-dom-document"), module.exports);
30
+
31
+ // runner-iframe-js-text-namespace:/Users/marcuslongmuir/workspace/mml/packages/networked-dom-web-runner/networked-dom-web-runner-iframe/build/index.js
32
+ var build_default = 'var __defProp = Object.defineProperty;\nvar __defProps = Object.defineProperties;\nvar __getOwnPropDescs = Object.getOwnPropertyDescriptors;\nvar __getOwnPropSymbols = Object.getOwnPropertySymbols;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __propIsEnum = Object.prototype.propertyIsEnumerable;\nvar __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;\nvar __spreadValues = (a, b) => {\n for (var prop in b ||= {})\n if (__hasOwnProp.call(b, prop))\n __defNormalProp(a, prop, b[prop]);\n if (__getOwnPropSymbols)\n for (var prop of __getOwnPropSymbols(b)) {\n if (__propIsEnum.call(b, prop))\n __defNormalProp(a, prop, b[prop]);\n }\n return a;\n};\nvar __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));\n\n// ../../observable-dom/src/utils.ts\nfunction virtualDomElementToStatic(el) {\n return {\n nodeId: el.nodeId,\n tag: el.tag,\n attributes: el.attributes,\n childNodes: el.childNodes.map((child) => virtualDomElementToStatic(child)),\n textContent: el.textContent\n };\n}\n\n// ../../observable-dom/src/ObservableDom.ts\nvar ObservableDom = class {\n constructor(observableDOMParameters, callback, runnerFactory) {\n this.nodeToNodeId = /* @__PURE__ */ new Map();\n this.nodeIdToNode = /* @__PURE__ */ new Map();\n this.realElementToVirtualElement = /* @__PURE__ */ new Map();\n this.ignoreTextNodes = true;\n this.nextNodeId = 1;\n this.htmlPath = observableDOMParameters.htmlPath;\n this.ignoreTextNodes = observableDOMParameters.ignoreTextNodes;\n this.callback = callback;\n this.documentTimeIntervalTimer = setInterval(() => {\n this.callback({\n documentTime: this.getDocumentTime()\n });\n }, observableDOMParameters.pingIntervalMilliseconds || 5e3);\n this.domRunner = runnerFactory(\n observableDOMParameters.htmlPath,\n observableDOMParameters.htmlContents,\n observableDOMParameters.params,\n (domRunnerMessage) => {\n if (domRunnerMessage.loaded) {\n this.createVirtualDomElementWithChildren(\n this.domRunner.getDocument(),\n null\n );\n const snapshot = virtualDomElementToStatic(\n this.getVirtualDomElementForRealElementOrThrow(\n this.domRunner.getDocument()\n )\n );\n this.callback({\n snapshot,\n documentTime: this.getDocumentTime()\n });\n } else if (domRunnerMessage.mutationList) {\n this.processModificationList(domRunnerMessage.mutationList);\n } else if (domRunnerMessage.logMessage) {\n this.callback({\n logMessage: domRunnerMessage.logMessage,\n documentTime: this.getDocumentTime()\n });\n }\n }\n );\n }\n addIPCWebsocket(webSocket) {\n return this.domRunner.addIPCWebsocket(webSocket);\n }\n addConnectedUserId(connectionId) {\n this.domRunner.getWindow().dispatchEvent(\n new (this.domRunner.getWindow()).CustomEvent("connected", {\n detail: { connectionId }\n })\n );\n }\n removeConnectedUserId(connectionId) {\n this.domRunner.getWindow().dispatchEvent(\n new (this.domRunner.getWindow()).CustomEvent("disconnected", {\n detail: { connectionId }\n })\n );\n }\n processModificationList(mutationList) {\n const documentEl = this.domRunner.getDocument();\n const documentVirtualDomElement = this.realElementToVirtualElement.get(documentEl);\n if (!documentVirtualDomElement) {\n throw new Error(`document not created in processModificationList`);\n }\n if (mutationList.length > 1) {\n console.error(\n "More than one mutation record received. It is possible that intermediate states are incorrect."\n );\n }\n for (const mutation of mutationList) {\n if (this.isIgnoredElement(mutation.target)) {\n continue;\n }\n if (mutation.type === "attributes" && // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.isIgnoredAttribute(mutation.target, mutation.attributeName)) {\n continue;\n }\n this.addKnownNodesInMutation(mutation);\n const firstNonIgnoredPreviousSibling = mutation.previousSibling ? this.getFirstNonIgnoredPreviousSibling(mutation.previousSibling) : null;\n const targetElement = this.getVirtualDomElementForRealElementOrThrow(\n mutation.target\n );\n const addedNodes = [];\n for (const node of mutation.addedNodes) {\n if (this.isIgnoredElement(node)) {\n continue;\n }\n const virtualDomElement = this.getVirtualDomElementForRealElementOrThrow(\n node\n );\n addedNodes.push(virtualDomElementToStatic(virtualDomElement));\n }\n const removedNodeIds = [];\n for (const node of mutation.removedNodes) {\n if (this.isIgnoredElement(node)) {\n continue;\n }\n const virtualDomElement = this.getVirtualDomElementForRealElementOrThrow(\n node\n );\n removedNodeIds.push(virtualDomElement.nodeId);\n }\n const mutationRecord = {\n type: mutation.type,\n targetId: targetElement.nodeId,\n addedNodes,\n removedNodeIds,\n previousSiblingId: firstNonIgnoredPreviousSibling !== null ? this.getVirtualDomElementForRealElementOrThrow(firstNonIgnoredPreviousSibling).nodeId : null,\n attribute: mutation.attributeName ? {\n attributeName: mutation.attributeName,\n value: mutation.target.getAttribute(mutation.attributeName)\n } : null\n };\n this.callback({\n mutation: mutationRecord,\n documentTime: this.getDocumentTime()\n });\n this.removeKnownNodesInMutation(mutation);\n }\n }\n addKnownNodesInMutation(mutation) {\n const targetNode = mutation.target;\n const virtualDomElement = this.realElementToVirtualElement.get(targetNode);\n if (!virtualDomElement) {\n throw new Error(\n "Unknown node in addKnownNodesInMutation:" + targetNode + "," + mutation.type\n );\n }\n if (mutation.type === "childList") {\n let previousSibling = mutation.previousSibling;\n let index = 0;\n while (previousSibling && this.isIgnoredElement(previousSibling)) {\n previousSibling = previousSibling.previousSibling;\n }\n if (previousSibling) {\n const previousSiblingElement = this.realElementToVirtualElement.get(\n previousSibling\n );\n if (!previousSiblingElement) {\n throw new Error("Unknown previous sibling");\n }\n index = virtualDomElement.childNodes.indexOf(previousSiblingElement);\n if (index === -1) {\n throw new Error("Previous sibling is not currently a child of the parent element");\n }\n index += 1;\n }\n mutation.addedNodes.forEach((node) => {\n const asElementOrText = node;\n const childVirtualDomElement = this.createVirtualDomElementWithChildren(\n asElementOrText,\n virtualDomElement\n );\n if (childVirtualDomElement) {\n if (virtualDomElement.childNodes.indexOf(childVirtualDomElement) === -1) {\n virtualDomElement.childNodes.splice(index, 0, childVirtualDomElement);\n index++;\n }\n }\n });\n } else if (mutation.type === "attributes") {\n const attributeName = mutation.attributeName;\n if (this.isIgnoredAttribute(targetNode, attributeName)) {\n return;\n }\n const attributeValue = targetNode.getAttribute(attributeName);\n if (attributeValue === null) {\n delete virtualDomElement.attributes[attributeName];\n } else {\n virtualDomElement.attributes[attributeName] = attributeValue;\n }\n } else if (mutation.type === "characterData") {\n virtualDomElement.textContent = targetNode.textContent ? targetNode.textContent : void 0;\n }\n }\n removeKnownNodesInMutation(mutation) {\n const targetNode = mutation.target;\n const virtualDomElement = this.realElementToVirtualElement.get(targetNode);\n if (!virtualDomElement) {\n throw new Error("Unknown node in mutation list:" + targetNode + ", " + mutation.type);\n }\n if (mutation.type === "childList") {\n for (const node of mutation.removedNodes) {\n const asElementOrText = node;\n if (this.isIgnoredElement(asElementOrText)) {\n continue;\n }\n const childDomElement = this.realElementToVirtualElement.get(asElementOrText);\n if (!childDomElement) {\n console.warn(this.htmlPath, "Unknown node in removeKnownNodesInMutation");\n continue;\n } else {\n this.removeVirtualDomElement(childDomElement);\n const index = virtualDomElement.childNodes.indexOf(childDomElement);\n virtualDomElement.childNodes.splice(index, 1);\n }\n }\n return;\n }\n }\n removeVirtualDomElement(virtualDomElement) {\n this.nodeIdToNode.delete(virtualDomElement.nodeId);\n this.nodeToNodeId.delete(virtualDomElement);\n this.realElementToVirtualElement.delete(virtualDomElement.realElement);\n for (const child of virtualDomElement.childNodes) {\n this.removeVirtualDomElement(child);\n }\n }\n createVirtualDomElementWithChildren(node, parent) {\n const virtualElement = this.createVirtualDomElement(node, parent);\n if (!virtualElement) {\n return null;\n }\n if (node.childNodes) {\n for (let i = 0; i < node.childNodes.length; i++) {\n const child = node.childNodes[i];\n const childVirtualElement = this.createVirtualDomElementWithChildren(\n child,\n virtualElement\n );\n if (childVirtualElement) {\n virtualElement.childNodes.push(childVirtualElement);\n }\n }\n }\n return virtualElement;\n }\n createVirtualDomElement(node, parent) {\n if (this.isIgnoredElement(node)) {\n return null;\n }\n const existingValue = this.realElementToVirtualElement.get(node);\n if (existingValue !== void 0) {\n throw new Error("Node already has a virtual element: " + node.nodeName);\n }\n if (!node) {\n throw new Error("Cannot assign node id to null");\n }\n const attributes = {};\n if (node.attributes) {\n const asHTMLElement = node;\n for (const key of asHTMLElement.getAttributeNames()) {\n const value = asHTMLElement.getAttribute(key);\n if (value === null) {\n throw new Error("Null attribute value for key: " + key);\n }\n if (!this.isIgnoredAttribute(node, key)) {\n attributes[key] = value;\n }\n }\n }\n const nodeId = this.nextNodeId++;\n const virtualElement = {\n nodeId,\n tag: node.nodeName,\n attributes,\n childNodes: [],\n realElement: node,\n parent\n };\n if (node instanceof this.domRunner.getWindow().Text && node.textContent) {\n virtualElement.textContent = node.textContent;\n }\n this.nodeToNodeId.set(virtualElement, nodeId);\n this.nodeIdToNode.set(nodeId, virtualElement);\n this.realElementToVirtualElement.set(node, virtualElement);\n return virtualElement;\n }\n getFirstNonIgnoredPreviousSibling(node) {\n let currentNode = node;\n if (!this.isIgnoredElement(currentNode)) {\n return currentNode;\n }\n while (currentNode && currentNode.previousSibling) {\n currentNode = currentNode.previousSibling;\n if (!this.isIgnoredElement(currentNode)) {\n return currentNode;\n }\n }\n return null;\n }\n getVirtualDomElementForRealElementOrThrow(realElement) {\n const virtualElement = this.realElementToVirtualElement.get(realElement);\n if (!virtualElement) {\n throw new Error(`Virtual element not found for real element`);\n }\n return virtualElement;\n }\n isIgnoredElement(node) {\n if (this.ignoreTextNodes && node instanceof this.domRunner.getWindow().Text) {\n return true;\n } else if (node instanceof this.domRunner.getWindow().HTMLScriptElement) {\n return true;\n } else if (node instanceof this.domRunner.getWindow().Comment) {\n return true;\n }\n return false;\n }\n isIgnoredAttribute(node, attributeName) {\n return attributeName.startsWith("on");\n }\n dispatchRemoteEventFromConnectionId(connectionId, remoteEvent) {\n const domNode = this.nodeIdToNode.get(remoteEvent.nodeId);\n if (!domNode) {\n console.error("Unknown node ID in remote event: " + remoteEvent.nodeId);\n return;\n }\n if (domNode instanceof this.domRunner.getWindow().Text) {\n console.warn("Cannot dispatch remote event to text node");\n return;\n }\n this.domRunner.dispatchRemoteEventFromConnectionId(\n connectionId,\n domNode.realElement,\n remoteEvent\n );\n }\n dispose() {\n clearInterval(this.documentTimeIntervalTimer);\n this.domRunner.dispose();\n }\n getDocumentTime() {\n return this.domRunner.getDocumentTime();\n }\n};\n\n// src/WebBrowserDOMRunner.ts\nvar WebBrowserDOMRunnerFactory = (htmlPath, htmlContents, params, callback) => {\n return new WebBrowserDOMRunner(htmlPath, htmlContents, params, callback);\n};\nvar documentLoadTime = Date.now();\nvar WebBrowserDOMRunner = class {\n constructor(htmlPath, htmlContents, params, callback) {\n this.htmlPath = htmlPath;\n this.callback = callback;\n let didSendLoad = false;\n this.mutationObserver = new window.MutationObserver((mutationList) => {\n if (!document) {\n return;\n }\n if (!didSendLoad) {\n throw new Error("MutationObserver called before load");\n }\n this.callback({\n mutationList\n });\n });\n window.params = params;\n const finishLoad = () => {\n if (didSendLoad) {\n throw new Error("finishLoad called twice");\n }\n didSendLoad = true;\n this.callback({\n loaded: true\n });\n this.mutationObserver.observe(window.document, {\n attributes: true,\n childList: true,\n subtree: true,\n characterData: true\n });\n };\n if (document.body) {\n setTimeout(finishLoad, 0);\n } else {\n window.addEventListener("DOMContentLoaded", finishLoad);\n }\n }\n addIPCWebsocket() {\n throw new Error("Not implemented.");\n }\n dispatchRemoteEventFromConnectionId(connectionId, realElement, remoteEvent) {\n const bubbles = remoteEvent.bubbles || false;\n const remoteEventObject = new CustomEvent(remoteEvent.name, {\n bubbles,\n detail: __spreadProps(__spreadValues({}, remoteEvent.params), { connectionId })\n });\n realElement.dispatchEvent(remoteEventObject);\n }\n dispose() {\n console.log("WebBrowserDOMRunner.dispose");\n }\n getDocument() {\n return document;\n }\n getDocumentTime() {\n if (document.timeline && document.timeline.currentTime) {\n return document.timeline.currentTime;\n }\n return Date.now() - documentLoadTime;\n }\n // TODO - resolve types (Window needs to expose classes such as CustomEvent as properties)\n getWindow() {\n return window;\n }\n};\n\n// src/IframeWebRunner.ts\nfunction setupIframeWebRunner(argsString) {\n const observableDOMParams = JSON.parse(atob(argsString));\n const sendMessageToHandler = (message) => {\n window.parent.postMessage(JSON.stringify(message), "*");\n };\n const observableDOM = new ObservableDom(\n __spreadProps(__spreadValues({}, observableDOMParams), {\n htmlContents: ""\n // This must be empty as the contents are assumed to be provided by the srcdoc\n }),\n (observableDomMessage) => {\n sendMessageToHandler({\n type: "dom",\n message: observableDomMessage\n });\n },\n WebBrowserDOMRunnerFactory\n );\n window.addEventListener("message", (e) => {\n const parsed = JSON.parse(e.data);\n if (parsed.type === "dispatchRemoteEventFromConnectionId") {\n observableDOM.dispatchRemoteEventFromConnectionId(parsed.connectionId, parsed.event);\n }\n });\n}\n\n// src/index.ts\nvar args = window.args;\nsetupIframeWebRunner(args);\n//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vb2JzZXJ2YWJsZS1kb20vc3JjL3V0aWxzLnRzIiwgIi4uLy4uLy4uL29ic2VydmFibGUtZG9tL3NyYy9PYnNlcnZhYmxlRG9tLnRzIiwgIi4uL3NyYy9XZWJCcm93c2VyRE9NUnVubmVyLnRzIiwgIi4uL3NyYy9JZnJhbWVXZWJSdW5uZXIudHMiLCAiLi4vc3JjL2luZGV4LnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJpbXBvcnQgeyBTdGF0aWNWaXJ0dWFsRG9tRWxlbWVudCB9IGZyb20gXCJAbW1sLWlvL29ic2VydmFibGUtZG9tLWNvbW1vblwiO1xuXG5pbXBvcnQgeyBMaXZlVmlydHVhbERvbUVsZW1lbnQgfSBmcm9tIFwiLi9PYnNlcnZhYmxlRG9tXCI7XG5cbmV4cG9ydCBmdW5jdGlvbiB2aXJ0dWFsRG9tRWxlbWVudFRvU3RhdGljKGVsOiBMaXZlVmlydHVhbERvbUVsZW1lbnQpOiBTdGF0aWNWaXJ0dWFsRG9tRWxlbWVudCB7XG4gIHJldHVybiB7XG4gICAgbm9kZUlkOiBlbC5ub2RlSWQsXG4gICAgdGFnOiBlbC50YWcsXG4gICAgYXR0cmlidXRlczogZWwuYXR0cmlidXRlcyxcbiAgICBjaGlsZE5vZGVzOiBlbC5jaGlsZE5vZGVzLm1hcCgoY2hpbGQpID0+IHZpcnR1YWxEb21FbGVtZW50VG9TdGF0aWMoY2hpbGQpKSxcbiAgICB0ZXh0Q29udGVudDogZWwudGV4dENvbnRlbnQsXG4gIH07XG59XG4iLCAiaW1wb3J0IHtcbiAgTG9nTWVzc2FnZSxcbiAgT2JzZXJ2YWJsZURvbUludGVyZmFjZSxcbiAgT2JzZXJ2YWJsZURvbU1lc3NhZ2UsXG4gIE9ic2VydmFibGVET01QYXJhbWV0ZXJzLFxuICBSZW1vdGVFdmVudCxcbiAgU3RhdGljVmlydHVhbERvbUVsZW1lbnQsXG4gIFN0YXRpY1ZpcnR1YWxEb21NdXRhdGlvbklkc1JlY29yZCxcbn0gZnJvbSBcIkBtbWwtaW8vb2JzZXJ2YWJsZS1kb20tY29tbW9uXCI7XG5cbmltcG9ydCB7IHZpcnR1YWxEb21FbGVtZW50VG9TdGF0aWMgfSBmcm9tIFwiLi91dGlsc1wiO1xuXG5leHBvcnQgdHlwZSBET01SdW5uZXJNZXNzYWdlID0ge1xuICBsb2FkZWQ/OiBib29sZWFuO1xuICBtdXRhdGlvbkxpc3Q/OiBBcnJheTxNdXRhdGlvblJlY29yZD47XG4gIGxvZ01lc3NhZ2U/OiBMb2dNZXNzYWdlO1xufTtcblxuZXhwb3J0IHR5cGUgRE9NUnVubmVySW50ZXJmYWNlID0ge1xuICBnZXREb2N1bWVudCgpOiBEb2N1bWVudDtcbiAgZ2V0V2luZG93KCk6IFdpbmRvdyAmIHtcbiAgICBDdXN0b21FdmVudDogdHlwZW9mIEN1c3RvbUV2ZW50O1xuICAgIFRleHQ6IHR5cGVvZiBUZXh0O1xuICAgIEhUTUxTY3JpcHRFbGVtZW50OiB0eXBlb2YgSFRNTFNjcmlwdEVsZW1lbnQ7XG4gICAgQ29tbWVudDogdHlwZW9mIENvbW1lbnQ7XG4gIH07IC8vIFRPRE8gLSBEZWZpbmUgdGhpcyB3aXRob3V0IHVzaW5nIEpTRE9NIHR5cGVzXG4gIGFkZElQQ1dlYnNvY2tldCh3ZWJTb2NrZXQ6IFdlYlNvY2tldCk6IHZvaWQ7XG4gIGRpc3BhdGNoUmVtb3RlRXZlbnRGcm9tQ29ubmVjdGlvbklkKFxuICAgIGNvbm5lY3Rpb25JZDogbnVtYmVyLFxuICAgIHJlYWxFbGVtZW50OiBFbGVtZW50LFxuICAgIHJlbW90ZUV2ZW50OiBSZW1vdGVFdmVudCxcbiAgKTogdm9pZDtcbiAgZGlzcG9zZSgpOiB2b2lkO1xuICBnZXREb2N1bWVudFRpbWUoKTogbnVtYmVyO1xufTtcblxuZXhwb3J0IHR5cGUgRE9NUnVubmVyRmFjdG9yeSA9IChcbiAgaHRtbFBhdGg6IHN0cmluZyxcbiAgaHRtbENvbnRlbnRzOiBzdHJpbmcsXG4gIHBhcmFtczogb2JqZWN0LFxuICBjYWxsYmFjazogKGRvbVJ1bm5lck1lc3NhZ2U6IERPTVJ1bm5lck1lc3NhZ2UpID0+IHZvaWQsXG4pID0+IERPTVJ1bm5lckludGVyZmFjZTtcblxuZXhwb3J0IHR5cGUgTGl2ZVZpcnR1YWxEb21FbGVtZW50ID0gT21pdDxTdGF0aWNWaXJ0dWFsRG9tRWxlbWVudCwgXCJjaGlsZE5vZGVzXCI+ICYge1xuICByZWFsRWxlbWVudDogRWxlbWVudCB8IFRleHQ7XG4gIGNoaWxkTm9kZXM6IEFycmF5PExpdmVWaXJ0dWFsRG9tRWxlbWVudD47XG4gIHBhcmVudDogTGl2ZVZpcnR1YWxEb21FbGVtZW50IHwgbnVsbDtcbn07XG5cbmV4cG9ydCBjbGFzcyBPYnNlcnZhYmxlRG9tIGltcGxlbWVudHMgT2JzZXJ2YWJsZURvbUludGVyZmFjZSB7XG4gIHByaXZhdGUgbm9kZVRvTm9kZUlkID0gbmV3IE1hcDxMaXZlVmlydHVhbERvbUVsZW1lbnQsIG51bWJlcj4oKTtcbiAgcHJpdmF0ZSBub2RlSWRUb05vZGUgPSBuZXcgTWFwPG51bWJlciwgTGl2ZVZpcnR1YWxEb21FbGVtZW50PigpO1xuICBwcml2YXRlIHJlYWxFbGVtZW50VG9WaXJ0dWFsRWxlbWVudCA9IG5ldyBNYXA8RWxlbWVudCB8IFRleHQsIExpdmVWaXJ0dWFsRG9tRWxlbWVudD4oKTtcbiAgcHJpdmF0ZSBpZ25vcmVUZXh0Tm9kZXMgPSB0cnVlO1xuICBwcml2YXRlIGNhbGxiYWNrOiAobWVzc2FnZTogT2JzZXJ2YWJsZURvbU1lc3NhZ2UpID0+IHZvaWQ7XG4gIHByaXZhdGUgbmV4dE5vZGVJZCA9IDE7XG4gIHByaXZhdGUgaHRtbFBhdGg6IHN0cmluZztcbiAgcHJpdmF0ZSBkb21SdW5uZXI6IERPTVJ1bm5lckludGVyZmFjZTtcblxuICBwcml2YXRlIGRvY3VtZW50VGltZUludGVydmFsVGltZXI6IE5vZGVKUy5UaW1lcjtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBvYnNlcnZhYmxlRE9NUGFyYW1ldGVyczogT2JzZXJ2YWJsZURPTVBhcmFtZXRlcnMsXG4gICAgY2FsbGJhY2s6IChtZXNzYWdlOiBPYnNlcnZhYmxlRG9tTWVzc2FnZSkgPT4gdm9pZCxcbiAgICBydW5uZXJGYWN0b3J5OiBET01SdW5uZXJGYWN0b3J5LFxuICApIHtcbiAgICB0aGlzLmh0bWxQYXRoID0gb2JzZXJ2YWJsZURPTVBhcmFtZXRlcnMuaHRtbFBhdGg7XG4gICAgdGhpcy5pZ25vcmVUZXh0Tm9kZXMgPSBvYnNlcnZhYmxlRE9NUGFyYW1ldGVycy5pZ25vcmVUZXh0Tm9kZXM7XG4gICAgdGhpcy5jYWxsYmFjayA9IGNhbGxiYWNrO1xuXG4gICAgdGhpcy5kb2N1bWVudFRpbWVJbnRlcnZhbFRpbWVyID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICAgICAgdGhpcy5jYWxsYmFjayh7XG4gICAgICAgIGRvY3VtZW50VGltZTogdGhpcy5nZXREb2N1bWVudFRpbWUoKSxcbiAgICAgIH0pO1xuICAgIH0sIG9ic2VydmFibGVET01QYXJhbWV0ZXJzLnBpbmdJbnRlcnZhbE1pbGxpc2Vjb25kcyB8fCA1MDAwKTtcblxuICAgIHRoaXMuZG9tUnVubmVyID0gcnVubmVyRmFjdG9yeShcbiAgICAgIG9ic2VydmFibGVET01QYXJhbWV0ZXJzLmh0bWxQYXRoLFxuICAgICAgb2JzZXJ2YWJsZURPTVBhcmFtZXRlcnMuaHRtbENvbnRlbnRzLFxuICAgICAgb2JzZXJ2YWJsZURPTVBhcmFtZXRlcnMucGFyYW1zLFxuICAgICAgKGRvbVJ1bm5lck1lc3NhZ2U6IERPTVJ1bm5lck1lc3NhZ2UpID0+IHtcbiAgICAgICAgaWYgKGRvbVJ1bm5lck1lc3NhZ2UubG9hZGVkKSB7XG4gICAgICAgICAgdGhpcy5jcmVhdGVWaXJ0dWFsRG9tRWxlbWVudFdpdGhDaGlsZHJlbihcbiAgICAgICAgICAgIHRoaXMuZG9tUnVubmVyLmdldERvY3VtZW50KCkgYXMgdW5rbm93biBhcyBFbGVtZW50LFxuICAgICAgICAgICAgbnVsbCxcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgY29uc3Qgc25hcHNob3QgPSB2aXJ0dWFsRG9tRWxlbWVudFRvU3RhdGljKFxuICAgICAgICAgICAgdGhpcy5nZXRWaXJ0dWFsRG9tRWxlbWVudEZvclJlYWxFbGVtZW50T3JUaHJvdyhcbiAgICAgICAgICAgICAgdGhpcy5kb21SdW5uZXIuZ2V0RG9jdW1lbnQoKSBhcyB1bmtub3duIGFzIEVsZW1lbnQsXG4gICAgICAgICAgICApLFxuICAgICAgICAgICk7XG5cbiAgICAgICAgICB0aGlzLmNhbGxiYWNrKHtcbiAgICAgICAgICAgIHNuYXBzaG90LFxuICAgICAgICAgICAgZG9jdW1lbnRUaW1lOiB0aGlzLmdldERvY3VtZW50VGltZSgpLFxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2UgaWYgKGRvbVJ1bm5lck1lc3NhZ2UubXV0YXRpb25MaXN0KSB7XG4gICAgICAgICAgdGhpcy5wcm9jZXNzTW9kaWZpY2F0aW9uTGlzdChkb21SdW5uZXJNZXNzYWdlLm11dGF0aW9uTGlzdCk7XG4gICAgICAgIH0gZWxzZSBpZiAoZG9tUnVubmVyTWVzc2FnZS5sb2dNZXNzYWdlKSB7XG4gICAgICAgICAgdGhpcy5jYWxsYmFjayh7XG4gICAgICAgICAgICBsb2dNZXNzYWdlOiBkb21SdW5uZXJNZXNzYWdlLmxvZ01lc3NhZ2UsXG4gICAgICAgICAgICBkb2N1bWVudFRpbWU6IHRoaXMuZ2V0RG9jdW1lbnRUaW1lKCksXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgKTtcbiAgfVxuXG4gIHB1YmxpYyBhZGRJUENXZWJzb2NrZXQod2ViU29ja2V0OiBXZWJTb2NrZXQpIHtcbiAgICByZXR1cm4gdGhpcy5kb21SdW5uZXIuYWRkSVBDV2Vic29ja2V0KHdlYlNvY2tldCk7XG4gIH1cblxuICBwdWJsaWMgYWRkQ29ubmVjdGVkVXNlcklkKGNvbm5lY3Rpb25JZDogbnVtYmVyKTogdm9pZCB7XG4gICAgdGhpcy5kb21SdW5uZXIuZ2V0V2luZG93KCkuZGlzcGF0Y2hFdmVudChcbiAgICAgIG5ldyAodGhpcy5kb21SdW5uZXIuZ2V0V2luZG93KCkuQ3VzdG9tRXZlbnQpKFwiY29ubmVjdGVkXCIsIHtcbiAgICAgICAgZGV0YWlsOiB7IGNvbm5lY3Rpb25JZCB9LFxuICAgICAgfSksXG4gICAgKTtcbiAgfVxuXG4gIHB1YmxpYyByZW1vdmVDb25uZWN0ZWRVc2VySWQoY29ubmVjdGlvbklkOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLmRvbVJ1bm5lci5nZXRXaW5kb3coKS5kaXNwYXRjaEV2ZW50KFxuICAgICAgbmV3ICh0aGlzLmRvbVJ1bm5lci5nZXRXaW5kb3coKS5DdXN0b21FdmVudCkoXCJkaXNjb25uZWN0ZWRcIiwge1xuICAgICAgICBkZXRhaWw6IHsgY29ubmVjdGlvbklkIH0sXG4gICAgICB9KSxcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBwcm9jZXNzTW9kaWZpY2F0aW9uTGlzdChtdXRhdGlvbkxpc3Q6IEFycmF5PE11dGF0aW9uUmVjb3JkPik6IHZvaWQge1xuICAgIGNvbnN0IGRvY3VtZW50RWwgPSB0aGlzLmRvbVJ1bm5lci5nZXREb2N1bWVudCgpIGFzIHVua25vd24gYXMgRWxlbWVudDtcbiAgICBjb25zdCBkb2N1bWVudFZpcnR1YWxEb21FbGVtZW50ID0gdGhpcy5yZWFsRWxlbWVudFRvVmlydHVhbEVsZW1lbnQuZ2V0KGRvY3VtZW50RWwpO1xuICAgIGlmICghZG9jdW1lbnRWaXJ0dWFsRG9tRWxlbWVudCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBkb2N1bWVudCBub3QgY3JlYXRlZCBpbiBwcm9jZXNzTW9kaWZpY2F0aW9uTGlzdGApO1xuICAgIH1cblxuICAgIGlmIChtdXRhdGlvbkxpc3QubGVuZ3RoID4gMSkge1xuICAgICAgLy8gVE9ETyAtIHdhbGsgYmFjayB0aHJvdWdoIHRoZSByZWNvcmRzIHRvIGRlcml2ZSB0aGUgaW50ZXJtZWRpYXRlIHN0YXRlcyAoZS5nLiBpZiBhbiBhdHRyaWJ1dGUgaXMgbGF0ZXIgYWRkZWQgdG9cbiAgICAgIC8vICBhbiBlbGVtZW50IGNyZWF0ZWQgaW4gYW4gZWFybGllciByZWNvcmQgdGhlbiBpdCBzaG91bGQgbm90IGhhdmUgdGhhdCBhdHRyaWJ1dGUgd2hlbiB0aGUgZWxlbWVudCBpcyBhZGRlZC5cbiAgICAgIC8vICBUaGlzIGlzIGltcG9ydGFudCBhcyBpbmNvcnJlY3QgYXR0cmlidXRlIHNldHMgY2FuIGFmZmVjdCB2aXNpYmlsaXR5IGFuZCBleHBlY3RlZCBjbGllbnQgcGVyZm9ybWFuY2UuXG4gICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICBcIk1vcmUgdGhhbiBvbmUgbXV0YXRpb24gcmVjb3JkIHJlY2VpdmVkLiBJdCBpcyBwb3NzaWJsZSB0aGF0IGludGVybWVkaWF0ZSBzdGF0ZXMgYXJlIGluY29ycmVjdC5cIixcbiAgICAgICk7XG4gICAgfVxuXG4gICAgZm9yIChjb25zdCBtdXRhdGlvbiBvZiBtdXRhdGlvbkxpc3QpIHtcbiAgICAgIGlmICh0aGlzLmlzSWdub3JlZEVsZW1lbnQobXV0YXRpb24udGFyZ2V0IGFzIEVsZW1lbnQgfCBUZXh0KSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgaWYgKFxuICAgICAgICBtdXRhdGlvbi50eXBlID09PSBcImF0dHJpYnV0ZXNcIiAmJlxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLW5vbi1udWxsLWFzc2VydGlvblxuICAgICAgICB0aGlzLmlzSWdub3JlZEF0dHJpYnV0ZShtdXRhdGlvbi50YXJnZXQgYXMgRWxlbWVudCB8IFRleHQsIG11dGF0aW9uLmF0dHJpYnV0ZU5hbWUhKVxuICAgICAgKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmFkZEtub3duTm9kZXNJbk11dGF0aW9uKG11dGF0aW9uKTtcblxuICAgICAgLy8gQ29udmVydCB0aGUgXCJyZWFsXCIgRE9NIE11dGF0aW9uUmVjb3JkIGludG8gYSBcInZpcnR1YWxcIiBET00gTXV0YXRpb25SZWNvcmQgdGhhdCByZWZlcmVuY2VzIHRoZSBWaXJ0dWFsRE9NRWxlbWVudHNcbiAgICAgIC8vIFRoaXMgaXMgZG9uZSBzbyB0aGF0IHRoZSBzYW1lIHByb2Nlc3MgZm9yIGhhbmRsaW5nIG11dGF0aW9ucyBjYW4gYmUgdXNlZCBmb3IgYm90aCBjaGFuZ2VzIHRvIGEgbGl2ZSBET00gYW5kIGFsc29cbiAgICAgIC8vIHRvIGRpZmZzIGJldHdlZW4gRE9NIHNuYXBzaG90cyB3aGVuIHJlbG9hZGluZ1xuICAgICAgY29uc3QgZmlyc3ROb25JZ25vcmVkUHJldmlvdXNTaWJsaW5nID0gbXV0YXRpb24ucHJldmlvdXNTaWJsaW5nXG4gICAgICAgID8gdGhpcy5nZXRGaXJzdE5vbklnbm9yZWRQcmV2aW91c1NpYmxpbmcobXV0YXRpb24ucHJldmlvdXNTaWJsaW5nIGFzIEVsZW1lbnQgfCBUZXh0KVxuICAgICAgICA6IG51bGw7XG4gICAgICBjb25zdCB0YXJnZXRFbGVtZW50ID0gdGhpcy5nZXRWaXJ0dWFsRG9tRWxlbWVudEZvclJlYWxFbGVtZW50T3JUaHJvdyhcbiAgICAgICAgbXV0YXRpb24udGFyZ2V0IGFzIEVsZW1lbnQgfCBUZXh0LFxuICAgICAgKTtcbiAgICAgIGNvbnN0IGFkZGVkTm9kZXM6IEFycmF5PFN0YXRpY1ZpcnR1YWxEb21FbGVtZW50PiA9IFtdO1xuICAgICAgZm9yIChjb25zdCBub2RlIG9mIG11dGF0aW9uLmFkZGVkTm9kZXMpIHtcbiAgICAgICAgaWYgKHRoaXMuaXNJZ25vcmVkRWxlbWVudChub2RlIGFzIEVsZW1lbnQgfCBUZXh0KSkge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHZpcnR1YWxEb21FbGVtZW50ID0gdGhpcy5nZXRWaXJ0dWFsRG9tRWxlbWVudEZvclJlYWxFbGVtZW50T3JUaHJvdyhcbiAgICAgICAgICBub2RlIGFzIEVsZW1lbnQgfCBUZXh0LFxuICAgICAgICApO1xuICAgICAgICBhZGRlZE5vZGVzLnB1c2godmlydHVhbERvbUVsZW1lbnRUb1N0YXRpYyh2aXJ0dWFsRG9tRWxlbWVudCkpO1xuICAgICAgfVxuXG4gICAgICBjb25zdCByZW1vdmVkTm9kZUlkczogQXJyYXk8bnVtYmVyPiA9IFtdO1xuICAgICAgZm9yIChjb25zdCBub2RlIG9mIG11dGF0aW9uLnJlbW92ZWROb2Rlcykge1xuICAgICAgICBpZiAodGhpcy5pc0lnbm9yZWRFbGVtZW50KG5vZGUgYXMgRWxlbWVudCB8IFRleHQpKSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgdmlydHVhbERvbUVsZW1lbnQgPSB0aGlzLmdldFZpcnR1YWxEb21FbGVtZW50Rm9yUmVhbEVsZW1lbnRPclRocm93KFxuICAgICAgICAgIG5vZGUgYXMgRWxlbWVudCB8IFRleHQsXG4gICAgICAgICk7XG4gICAgICAgIHJlbW92ZWROb2RlSWRzLnB1c2godmlydHVhbERvbUVsZW1lbnQubm9kZUlkKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgbXV0YXRpb25SZWNvcmQ6IFN0YXRpY1ZpcnR1YWxEb21NdXRhdGlvbklkc1JlY29yZCA9IHtcbiAgICAgICAgdHlwZTogbXV0YXRpb24udHlwZSxcbiAgICAgICAgdGFyZ2V0SWQ6IHRhcmdldEVsZW1lbnQubm9kZUlkLFxuICAgICAgICBhZGRlZE5vZGVzLFxuICAgICAgICByZW1vdmVkTm9kZUlkcyxcbiAgICAgICAgcHJldmlvdXNTaWJsaW5nSWQ6XG4gICAgICAgICAgZmlyc3ROb25JZ25vcmVkUHJldmlvdXNTaWJsaW5nICE9PSBudWxsXG4gICAgICAgICAgICA/IHRoaXMuZ2V0VmlydHVhbERvbUVsZW1lbnRGb3JSZWFsRWxlbWVudE9yVGhyb3coZmlyc3ROb25JZ25vcmVkUHJldmlvdXNTaWJsaW5nKS5ub2RlSWRcbiAgICAgICAgICAgIDogbnVsbCxcbiAgICAgICAgYXR0cmlidXRlOiBtdXRhdGlvbi5hdHRyaWJ1dGVOYW1lXG4gICAgICAgICAgPyB7XG4gICAgICAgICAgICAgIGF0dHJpYnV0ZU5hbWU6IG11dGF0aW9uLmF0dHJpYnV0ZU5hbWUsXG4gICAgICAgICAgICAgIHZhbHVlOiAobXV0YXRpb24udGFyZ2V0IGFzIEVsZW1lbnQpLmdldEF0dHJpYnV0ZShtdXRhdGlvbi5hdHRyaWJ1dGVOYW1lKSxcbiAgICAgICAgICAgIH1cbiAgICAgICAgICA6IG51bGwsXG4gICAgICB9O1xuXG4gICAgICB0aGlzLmNhbGxiYWNrKHtcbiAgICAgICAgbXV0YXRpb246IG11dGF0aW9uUmVjb3JkLFxuICAgICAgICBkb2N1bWVudFRpbWU6IHRoaXMuZ2V0RG9jdW1lbnRUaW1lKCksXG4gICAgICB9KTtcblxuICAgICAgdGhpcy5yZW1vdmVLbm93bk5vZGVzSW5NdXRhdGlvbihtdXRhdGlvbik7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhZGRLbm93bk5vZGVzSW5NdXRhdGlvbihtdXRhdGlvbjogTXV0YXRpb25SZWNvcmQpOiB2b2lkIHtcbiAgICBjb25zdCB0YXJnZXROb2RlID0gbXV0YXRpb24udGFyZ2V0IGFzIEVsZW1lbnQgfCBUZXh0O1xuICAgIGNvbnN0IHZpcnR1YWxEb21FbGVtZW50ID0gdGhpcy5yZWFsRWxlbWVudFRvVmlydHVhbEVsZW1lbnQuZ2V0KHRhcmdldE5vZGUpO1xuICAgIGlmICghdmlydHVhbERvbUVsZW1lbnQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgXCJVbmtub3duIG5vZGUgaW4gYWRkS25vd25Ob2Rlc0luTXV0YXRpb246XCIgKyB0YXJnZXROb2RlICsgXCIsXCIgKyBtdXRhdGlvbi50eXBlLFxuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKG11dGF0aW9uLnR5cGUgPT09IFwiY2hpbGRMaXN0XCIpIHtcbiAgICAgIGxldCBwcmV2aW91c1NpYmxpbmcgPSBtdXRhdGlvbi5wcmV2aW91c1NpYmxpbmc7XG4gICAgICBsZXQgaW5kZXggPSAwO1xuICAgICAgd2hpbGUgKHByZXZpb3VzU2libGluZyAmJiB0aGlzLmlzSWdub3JlZEVsZW1lbnQocHJldmlvdXNTaWJsaW5nIGFzIEVsZW1lbnQgfCBUZXh0KSkge1xuICAgICAgICBwcmV2aW91c1NpYmxpbmcgPSBwcmV2aW91c1NpYmxpbmcucHJldmlvdXNTaWJsaW5nO1xuICAgICAgfVxuICAgICAgaWYgKHByZXZpb3VzU2libGluZykge1xuICAgICAgICBjb25zdCBwcmV2aW91c1NpYmxpbmdFbGVtZW50ID0gdGhpcy5yZWFsRWxlbWVudFRvVmlydHVhbEVsZW1lbnQuZ2V0KFxuICAgICAgICAgIHByZXZpb3VzU2libGluZyBhcyBFbGVtZW50IHwgVGV4dCxcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKCFwcmV2aW91c1NpYmxpbmdFbGVtZW50KSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiVW5rbm93biBwcmV2aW91cyBzaWJsaW5nXCIpO1xuICAgICAgICB9XG4gICAgICAgIGluZGV4ID0gdmlydHVhbERvbUVsZW1lbnQuY2hpbGROb2Rlcy5pbmRleE9mKHByZXZpb3VzU2libGluZ0VsZW1lbnQpO1xuICAgICAgICBpZiAoaW5kZXggPT09IC0xKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiUHJldmlvdXMgc2libGluZyBpcyBub3QgY3VycmVudGx5IGEgY2hpbGQgb2YgdGhlIHBhcmVudCBlbGVtZW50XCIpO1xuICAgICAgICB9XG4gICAgICAgIGluZGV4ICs9IDE7XG4gICAgICB9XG4gICAgICBtdXRhdGlvbi5hZGRlZE5vZGVzLmZvckVhY2goKG5vZGU6IE5vZGUpID0+IHtcbiAgICAgICAgY29uc3QgYXNFbGVtZW50T3JUZXh0ID0gbm9kZSBhcyBFbGVtZW50IHwgVGV4dDtcbiAgICAgICAgY29uc3QgY2hpbGRWaXJ0dWFsRG9tRWxlbWVudCA9IHRoaXMuY3JlYXRlVmlydHVhbERvbUVsZW1lbnRXaXRoQ2hpbGRyZW4oXG4gICAgICAgICAgYXNFbGVtZW50T3JUZXh0LFxuICAgICAgICAgIHZpcnR1YWxEb21FbGVtZW50LFxuICAgICAgICApO1xuICAgICAgICBpZiAoY2hpbGRWaXJ0dWFsRG9tRWxlbWVudCkge1xuICAgICAgICAgIGlmICh2aXJ0dWFsRG9tRWxlbWVudC5jaGlsZE5vZGVzLmluZGV4T2YoY2hpbGRWaXJ0dWFsRG9tRWxlbWVudCkgPT09IC0xKSB7XG4gICAgICAgICAgICB2aXJ0dWFsRG9tRWxlbWVudC5jaGlsZE5vZGVzLnNwbGljZShpbmRleCwgMCwgY2hpbGRWaXJ0dWFsRG9tRWxlbWVudCk7XG4gICAgICAgICAgICBpbmRleCsrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSBlbHNlIGlmIChtdXRhdGlvbi50eXBlID09PSBcImF0dHJpYnV0ZXNcIikge1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1ub24tbnVsbC1hc3NlcnRpb25cbiAgICAgIGNvbnN0IGF0dHJpYnV0ZU5hbWUgPSBtdXRhdGlvbi5hdHRyaWJ1dGVOYW1lITtcbiAgICAgIGlmICh0aGlzLmlzSWdub3JlZEF0dHJpYnV0ZSh0YXJnZXROb2RlLCBhdHRyaWJ1dGVOYW1lKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCBhdHRyaWJ1dGVWYWx1ZSA9ICh0YXJnZXROb2RlIGFzIEVsZW1lbnQpLmdldEF0dHJpYnV0ZShhdHRyaWJ1dGVOYW1lKTtcbiAgICAgIGlmIChhdHRyaWJ1dGVWYWx1ZSA9PT0gbnVsbCkge1xuICAgICAgICBkZWxldGUgdmlydHVhbERvbUVsZW1lbnQuYXR0cmlidXRlc1thdHRyaWJ1dGVOYW1lXTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHZpcnR1YWxEb21FbGVtZW50LmF0dHJpYnV0ZXNbYXR0cmlidXRlTmFtZV0gPSBhdHRyaWJ1dGVWYWx1ZTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKG11dGF0aW9uLnR5cGUgPT09IFwiY2hhcmFjdGVyRGF0YVwiKSB7XG4gICAgICB2aXJ0dWFsRG9tRWxlbWVudC50ZXh0Q29udGVudCA9IHRhcmdldE5vZGUudGV4dENvbnRlbnQgPyB0YXJnZXROb2RlLnRleHRDb250ZW50IDogdW5kZWZpbmVkO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgcmVtb3ZlS25vd25Ob2Rlc0luTXV0YXRpb24obXV0YXRpb246IE11dGF0aW9uUmVjb3JkKTogdm9pZCB7XG4gICAgY29uc3QgdGFyZ2V0Tm9kZSA9IG11dGF0aW9uLnRhcmdldCBhcyBFbGVtZW50IHwgVGV4dDtcbiAgICBjb25zdCB2aXJ0dWFsRG9tRWxlbWVudCA9IHRoaXMucmVhbEVsZW1lbnRUb1ZpcnR1YWxFbGVtZW50LmdldCh0YXJnZXROb2RlKTtcbiAgICBpZiAoIXZpcnR1YWxEb21FbGVtZW50KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJVbmtub3duIG5vZGUgaW4gbXV0YXRpb24gbGlzdDpcIiArIHRhcmdldE5vZGUgKyBcIiwgXCIgKyBtdXRhdGlvbi50eXBlKTtcbiAgICB9XG4gICAgaWYgKG11dGF0aW9uLnR5cGUgPT09IFwiY2hpbGRMaXN0XCIpIHtcbiAgICAgIGZvciAoY29uc3Qgbm9kZSBvZiBtdXRhdGlvbi5yZW1vdmVkTm9kZXMpIHtcbiAgICAgICAgY29uc3QgYXNFbGVtZW50T3JUZXh0ID0gbm9kZSBhcyBFbGVtZW50IHwgVGV4dDtcbiAgICAgICAgaWYgKHRoaXMuaXNJZ25vcmVkRWxlbWVudChhc0VsZW1lbnRPclRleHQpKSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgY2hpbGREb21FbGVtZW50ID0gdGhpcy5yZWFsRWxlbWVudFRvVmlydHVhbEVsZW1lbnQuZ2V0KGFzRWxlbWVudE9yVGV4dCk7XG4gICAgICAgIGlmICghY2hpbGREb21FbGVtZW50KSB7XG4gICAgICAgICAgY29uc29sZS53YXJuKHRoaXMuaHRtbFBhdGgsIFwiVW5rbm93biBub2RlIGluIHJlbW92ZUtub3duTm9kZXNJbk11dGF0aW9uXCIpO1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRoaXMucmVtb3ZlVmlydHVhbERvbUVsZW1lbnQoY2hpbGREb21FbGVtZW50KTtcbiAgICAgICAgICBjb25zdCBpbmRleCA9IHZpcnR1YWxEb21FbGVtZW50LmNoaWxkTm9kZXMuaW5kZXhPZihjaGlsZERvbUVsZW1lbnQpO1xuICAgICAgICAgIHZpcnR1YWxEb21FbGVtZW50LmNoaWxkTm9kZXMuc3BsaWNlKGluZGV4LCAxKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgcmVtb3ZlVmlydHVhbERvbUVsZW1lbnQodmlydHVhbERvbUVsZW1lbnQ6IExpdmVWaXJ0dWFsRG9tRWxlbWVudCk6IHZvaWQge1xuICAgIHRoaXMubm9kZUlkVG9Ob2RlLmRlbGV0ZSh2aXJ0dWFsRG9tRWxlbWVudC5ub2RlSWQpO1xuICAgIHRoaXMubm9kZVRvTm9kZUlkLmRlbGV0ZSh2aXJ0dWFsRG9tRWxlbWVudCk7XG4gICAgdGhpcy5yZWFsRWxlbWVudFRvVmlydHVhbEVsZW1lbnQuZGVsZXRlKHZpcnR1YWxEb21FbGVtZW50LnJlYWxFbGVtZW50KTtcbiAgICBmb3IgKGNvbnN0IGNoaWxkIG9mIHZpcnR1YWxEb21FbGVtZW50LmNoaWxkTm9kZXMpIHtcbiAgICAgIHRoaXMucmVtb3ZlVmlydHVhbERvbUVsZW1lbnQoY2hpbGQpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgY3JlYXRlVmlydHVhbERvbUVsZW1lbnRXaXRoQ2hpbGRyZW4oXG4gICAgbm9kZTogRWxlbWVudCB8IFRleHQsXG4gICAgcGFyZW50OiBMaXZlVmlydHVhbERvbUVsZW1lbnQgfCBudWxsLFxuICApOiBMaXZlVmlydHVhbERvbUVsZW1lbnQgfCBudWxsIHtcbiAgICBjb25zdCB2aXJ0dWFsRWxlbWVudCA9IHRoaXMuY3JlYXRlVmlydHVhbERvbUVsZW1lbnQobm9kZSwgcGFyZW50KTtcbiAgICBpZiAoIXZpcnR1YWxFbGVtZW50KSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgaWYgKChub2RlIGFzIEVsZW1lbnQpLmNoaWxkTm9kZXMpIHtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgKG5vZGUgYXMgRWxlbWVudCkuY2hpbGROb2Rlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCBjaGlsZCA9IChub2RlIGFzIEVsZW1lbnQpLmNoaWxkTm9kZXNbaV07XG4gICAgICAgIGNvbnN0IGNoaWxkVmlydHVhbEVsZW1lbnQgPSB0aGlzLmNyZWF0ZVZpcnR1YWxEb21FbGVtZW50V2l0aENoaWxkcmVuKFxuICAgICAgICAgIGNoaWxkIGFzIEVsZW1lbnQgfCBUZXh0LFxuICAgICAgICAgIHZpcnR1YWxFbGVtZW50LFxuICAgICAgICApO1xuICAgICAgICBpZiAoY2hpbGRWaXJ0dWFsRWxlbWVudCkge1xuICAgICAgICAgIHZpcnR1YWxFbGVtZW50LmNoaWxkTm9kZXMucHVzaChjaGlsZFZpcnR1YWxFbGVtZW50KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB2aXJ0dWFsRWxlbWVudDtcbiAgfVxuXG4gIHByaXZhdGUgY3JlYXRlVmlydHVhbERvbUVsZW1lbnQoXG4gICAgbm9kZTogRWxlbWVudCB8IFRleHQsXG4gICAgcGFyZW50OiBMaXZlVmlydHVhbERvbUVsZW1lbnQgfCBudWxsLFxuICApOiBMaXZlVmlydHVhbERvbUVsZW1lbnQgfCBudWxsIHtcbiAgICBpZiAodGhpcy5pc0lnbm9yZWRFbGVtZW50KG5vZGUpKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgY29uc3QgZXhpc3RpbmdWYWx1ZSA9IHRoaXMucmVhbEVsZW1lbnRUb1ZpcnR1YWxFbGVtZW50LmdldChub2RlKTtcbiAgICBpZiAoZXhpc3RpbmdWYWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJOb2RlIGFscmVhZHkgaGFzIGEgdmlydHVhbCBlbGVtZW50OiBcIiArIG5vZGUubm9kZU5hbWUpO1xuICAgIH1cbiAgICBpZiAoIW5vZGUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIkNhbm5vdCBhc3NpZ24gbm9kZSBpZCB0byBudWxsXCIpO1xuICAgIH1cblxuICAgIGNvbnN0IGF0dHJpYnV0ZXM6IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIH0gPSB7fTtcbiAgICBpZiAoKG5vZGUgYXMgYW55KS5hdHRyaWJ1dGVzKSB7XG4gICAgICBjb25zdCBhc0hUTUxFbGVtZW50ID0gbm9kZSBhcyBIVE1MRWxlbWVudDtcbiAgICAgIGZvciAoY29uc3Qga2V5IG9mIGFzSFRNTEVsZW1lbnQuZ2V0QXR0cmlidXRlTmFtZXMoKSkge1xuICAgICAgICBjb25zdCB2YWx1ZSA9IGFzSFRNTEVsZW1lbnQuZ2V0QXR0cmlidXRlKGtleSk7XG4gICAgICAgIGlmICh2YWx1ZSA9PT0gbnVsbCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIk51bGwgYXR0cmlidXRlIHZhbHVlIGZvciBrZXk6IFwiICsga2V5KTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIXRoaXMuaXNJZ25vcmVkQXR0cmlidXRlKG5vZGUsIGtleSkpIHtcbiAgICAgICAgICBhdHRyaWJ1dGVzW2tleV0gPSB2YWx1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IG5vZGVJZCA9IHRoaXMubmV4dE5vZGVJZCsrO1xuICAgIGNvbnN0IHZpcnR1YWxFbGVtZW50OiBMaXZlVmlydHVhbERvbUVsZW1lbnQgPSB7XG4gICAgICBub2RlSWQsXG4gICAgICB0YWc6IG5vZGUubm9kZU5hbWUsXG4gICAgICBhdHRyaWJ1dGVzLFxuICAgICAgY2hpbGROb2RlczogW10sXG4gICAgICByZWFsRWxlbWVudDogbm9kZSxcbiAgICAgIHBhcmVudCxcbiAgICB9O1xuICAgIGlmIChub2RlIGluc3RhbmNlb2YgdGhpcy5kb21SdW5uZXIuZ2V0V2luZG93KCkuVGV4dCAmJiBub2RlLnRleHRDb250ZW50KSB7XG4gICAgICB2aXJ0dWFsRWxlbWVudC50ZXh0Q29udGVudCA9IG5vZGUudGV4dENvbnRlbnQ7XG4gICAgfVxuICAgIHRoaXMubm9kZVRvTm9kZUlkLnNldCh2aXJ0dWFsRWxlbWVudCwgbm9kZUlkKTtcbiAgICB0aGlzLm5vZGVJZFRvTm9kZS5zZXQobm9kZUlkLCB2aXJ0dWFsRWxlbWVudCk7XG4gICAgdGhpcy5yZWFsRWxlbWVudFRvVmlydHVhbEVsZW1lbnQuc2V0KG5vZGUsIHZpcnR1YWxFbGVtZW50KTtcbiAgICByZXR1cm4gdmlydHVhbEVsZW1lbnQ7XG4gIH1cblxuICBwcml2YXRlIGdldEZpcnN0Tm9uSWdub3JlZFByZXZpb3VzU2libGluZyhub2RlOiBFbGVtZW50IHwgVGV4dCk6IEVsZW1lbnQgfCBUZXh0IHwgbnVsbCB7XG4gICAgbGV0IGN1cnJlbnROb2RlID0gbm9kZTtcbiAgICBpZiAoIXRoaXMuaXNJZ25vcmVkRWxlbWVudChjdXJyZW50Tm9kZSkpIHtcbiAgICAgIHJldHVybiBjdXJyZW50Tm9kZTtcbiAgICB9XG4gICAgd2hpbGUgKGN1cnJlbnROb2RlICYmIGN1cnJlbnROb2RlLnByZXZpb3VzU2libGluZykge1xuICAgICAgY3VycmVudE5vZGUgPSBjdXJyZW50Tm9kZS5wcmV2aW91c1NpYmxpbmcgYXMgRWxlbWVudCB8IFRleHQ7XG4gICAgICBpZiAoIXRoaXMuaXNJZ25vcmVkRWxlbWVudChjdXJyZW50Tm9kZSkpIHtcbiAgICAgICAgcmV0dXJuIGN1cnJlbnROb2RlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0VmlydHVhbERvbUVsZW1lbnRGb3JSZWFsRWxlbWVudE9yVGhyb3coXG4gICAgcmVhbEVsZW1lbnQ6IEVsZW1lbnQgfCBUZXh0LFxuICApOiBMaXZlVmlydHVhbERvbUVsZW1lbnQge1xuICAgIGNvbnN0IHZpcnR1YWxFbGVtZW50ID0gdGhpcy5yZWFsRWxlbWVudFRvVmlydHVhbEVsZW1lbnQuZ2V0KHJlYWxFbGVtZW50KTtcbiAgICBpZiAoIXZpcnR1YWxFbGVtZW50KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFZpcnR1YWwgZWxlbWVudCBub3QgZm91bmQgZm9yIHJlYWwgZWxlbWVudGApO1xuICAgIH1cbiAgICByZXR1cm4gdmlydHVhbEVsZW1lbnQ7XG4gIH1cblxuICBwcml2YXRlIGlzSWdub3JlZEVsZW1lbnQobm9kZTogRWxlbWVudCB8IFRleHQpOiBib29sZWFuIHtcbiAgICBpZiAodGhpcy5pZ25vcmVUZXh0Tm9kZXMgJiYgbm9kZSBpbnN0YW5jZW9mIHRoaXMuZG9tUnVubmVyLmdldFdpbmRvdygpLlRleHQpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gZWxzZSBpZiAobm9kZSBpbnN0YW5jZW9mIHRoaXMuZG9tUnVubmVyLmdldFdpbmRvdygpLkhUTUxTY3JpcHRFbGVtZW50KSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9IGVsc2UgaWYgKG5vZGUgaW5zdGFuY2VvZiB0aGlzLmRvbVJ1bm5lci5nZXRXaW5kb3coKS5Db21tZW50KSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgcHJpdmF0ZSBpc0lnbm9yZWRBdHRyaWJ1dGUobm9kZTogRWxlbWVudCB8IFRleHQsIGF0dHJpYnV0ZU5hbWU6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBhdHRyaWJ1dGVOYW1lLnN0YXJ0c1dpdGgoXCJvblwiKTtcbiAgfVxuXG4gIHB1YmxpYyBkaXNwYXRjaFJlbW90ZUV2ZW50RnJvbUNvbm5lY3Rpb25JZChjb25uZWN0aW9uSWQ6IG51bWJlciwgcmVtb3RlRXZlbnQ6IFJlbW90ZUV2ZW50KTogdm9pZCB7XG4gICAgY29uc3QgZG9tTm9kZSA9IHRoaXMubm9kZUlkVG9Ob2RlLmdldChyZW1vdGVFdmVudC5ub2RlSWQpO1xuICAgIGlmICghZG9tTm9kZSkge1xuICAgICAgY29uc29sZS5lcnJvcihcIlVua25vd24gbm9kZSBJRCBpbiByZW1vdGUgZXZlbnQ6IFwiICsgcmVtb3RlRXZlbnQubm9kZUlkKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoZG9tTm9kZSBpbnN0YW5jZW9mIHRoaXMuZG9tUnVubmVyLmdldFdpbmRvdygpLlRleHQpIHtcbiAgICAgIGNvbnNvbGUud2FybihcIkNhbm5vdCBkaXNwYXRjaCByZW1vdGUgZXZlbnQgdG8gdGV4dCBub2RlXCIpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuZG9tUnVubmVyLmRpc3BhdGNoUmVtb3RlRXZlbnRGcm9tQ29ubmVjdGlvbklkKFxuICAgICAgY29ubmVjdGlvbklkLFxuICAgICAgZG9tTm9kZS5yZWFsRWxlbWVudCBhcyBFbGVtZW50LFxuICAgICAgcmVtb3RlRXZlbnQsXG4gICAgKTtcbiAgfVxuXG4gIHB1YmxpYyBkaXNwb3NlKCkge1xuICAgIGNsZWFySW50ZXJ2YWwodGhpcy5kb2N1bWVudFRpbWVJbnRlcnZhbFRpbWVyKTtcbiAgICB0aGlzLmRvbVJ1bm5lci5kaXNwb3NlKCk7XG4gIH1cblxuICBwcml2YXRlIGdldERvY3VtZW50VGltZSgpIHtcbiAgICByZXR1cm4gdGhpcy5kb21SdW5uZXIuZ2V0RG9jdW1lbnRUaW1lKCk7XG4gIH1cbn1cbiIsICJpbXBvcnQgeyBSZW1vdGVFdmVudCB9IGZyb20gXCJAbW1sLWlvL25ldHdvcmtlZC1kb20tcHJvdG9jb2xcIjtcbmltcG9ydCB7IERPTVJ1bm5lckZhY3RvcnksIERPTVJ1bm5lckludGVyZmFjZSwgRE9NUnVubmVyTWVzc2FnZSB9IGZyb20gXCJAbW1sLWlvL29ic2VydmFibGUtZG9tXCI7XG5cbmV4cG9ydCBjb25zdCBXZWJCcm93c2VyRE9NUnVubmVyRmFjdG9yeTogRE9NUnVubmVyRmFjdG9yeSA9IChcbiAgaHRtbFBhdGg6IHN0cmluZyxcbiAgaHRtbENvbnRlbnRzOiBzdHJpbmcsXG4gIHBhcmFtczogb2JqZWN0LFxuICBjYWxsYmFjazogKG11dGF0aW9uTGlzdDogRE9NUnVubmVyTWVzc2FnZSkgPT4gdm9pZCxcbik6IERPTVJ1bm5lckludGVyZmFjZSA9PiB7XG4gIHJldHVybiBuZXcgV2ViQnJvd3NlckRPTVJ1bm5lcihodG1sUGF0aCwgaHRtbENvbnRlbnRzLCBwYXJhbXMsIGNhbGxiYWNrKTtcbn07XG5cbmNvbnN0IGRvY3VtZW50TG9hZFRpbWUgPSBEYXRlLm5vdygpO1xuXG5leHBvcnQgY2xhc3MgV2ViQnJvd3NlckRPTVJ1bm5lciBpbXBsZW1lbnRzIERPTVJ1bm5lckludGVyZmFjZSB7XG4gIHByaXZhdGUgbXV0YXRpb25PYnNlcnZlcjogTXV0YXRpb25PYnNlcnZlcjtcbiAgcHJpdmF0ZSBodG1sUGF0aDogc3RyaW5nO1xuICBwcml2YXRlIGNhbGxiYWNrOiAoZG9tUnVubmVyTWVzc2FnZTogRE9NUnVubmVyTWVzc2FnZSkgPT4gdm9pZDtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBodG1sUGF0aDogc3RyaW5nLFxuICAgIGh0bWxDb250ZW50czogc3RyaW5nLFxuICAgIHBhcmFtczogb2JqZWN0LFxuICAgIGNhbGxiYWNrOiAoZG9tUnVubmVyTWVzc2FnZTogRE9NUnVubmVyTWVzc2FnZSkgPT4gdm9pZCxcbiAgKSB7XG4gICAgdGhpcy5odG1sUGF0aCA9IGh0bWxQYXRoO1xuICAgIHRoaXMuY2FsbGJhY2sgPSBjYWxsYmFjaztcblxuICAgIGxldCBkaWRTZW5kTG9hZCA9IGZhbHNlO1xuXG4gICAgdGhpcy5tdXRhdGlvbk9ic2VydmVyID0gbmV3IHdpbmRvdy5NdXRhdGlvbk9ic2VydmVyKChtdXRhdGlvbkxpc3QpID0+IHtcbiAgICAgIGlmICghZG9jdW1lbnQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaWYgKCFkaWRTZW5kTG9hZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJNdXRhdGlvbk9ic2VydmVyIGNhbGxlZCBiZWZvcmUgbG9hZFwiKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuY2FsbGJhY2soe1xuICAgICAgICBtdXRhdGlvbkxpc3QsXG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgICh3aW5kb3cgYXMgYW55KS5wYXJhbXMgPSBwYXJhbXM7XG5cbiAgICBjb25zdCBmaW5pc2hMb2FkID0gKCkgPT4ge1xuICAgICAgaWYgKGRpZFNlbmRMb2FkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcImZpbmlzaExvYWQgY2FsbGVkIHR3aWNlXCIpO1xuICAgICAgfVxuICAgICAgZGlkU2VuZExvYWQgPSB0cnVlO1xuICAgICAgdGhpcy5jYWxsYmFjayh7XG4gICAgICAgIGxvYWRlZDogdHJ1ZSxcbiAgICAgIH0pO1xuICAgICAgdGhpcy5tdXRhdGlvbk9ic2VydmVyLm9ic2VydmUod2luZG93LmRvY3VtZW50LCB7XG4gICAgICAgIGF0dHJpYnV0ZXM6IHRydWUsXG4gICAgICAgIGNoaWxkTGlzdDogdHJ1ZSxcbiAgICAgICAgc3VidHJlZTogdHJ1ZSxcbiAgICAgICAgY2hhcmFjdGVyRGF0YTogdHJ1ZSxcbiAgICAgIH0pO1xuICAgIH07XG4gICAgaWYgKGRvY3VtZW50LmJvZHkpIHtcbiAgICAgIHNldFRpbWVvdXQoZmluaXNoTG9hZCwgMCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKFwiRE9NQ29udGVudExvYWRlZFwiLCBmaW5pc2hMb2FkKTtcbiAgICB9XG4gIH1cblxuICBhZGRJUENXZWJzb2NrZXQoKTogdm9pZCB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFwiTm90IGltcGxlbWVudGVkLlwiKTtcbiAgfVxuXG4gIGRpc3BhdGNoUmVtb3RlRXZlbnRGcm9tQ29ubmVjdGlvbklkKFxuICAgIGNvbm5lY3Rpb25JZDogbnVtYmVyLFxuICAgIHJlYWxFbGVtZW50OiBFbGVtZW50LFxuICAgIHJlbW90ZUV2ZW50OiBSZW1vdGVFdmVudCxcbiAgKTogdm9pZCB7XG4gICAgY29uc3QgYnViYmxlcyA9IHJlbW90ZUV2ZW50LmJ1YmJsZXMgfHwgZmFsc2U7XG4gICAgY29uc3QgcmVtb3RlRXZlbnRPYmplY3QgPSBuZXcgQ3VzdG9tRXZlbnQocmVtb3RlRXZlbnQubmFtZSwge1xuICAgICAgYnViYmxlcyxcbiAgICAgIGRldGFpbDogeyAuLi5yZW1vdGVFdmVudC5wYXJhbXMsIGNvbm5lY3Rpb25JZCB9LFxuICAgIH0pO1xuXG4gICAgLy8gRGlzcGF0Y2ggdGhlIGV2ZW50IHZpYSBKYXZhU2NyaXB0LlxuICAgIHJlYWxFbGVtZW50LmRpc3BhdGNoRXZlbnQocmVtb3RlRXZlbnRPYmplY3QpO1xuICB9XG5cbiAgZGlzcG9zZSgpOiB2b2lkIHtcbiAgICAvLyBUT0RPIC0gaGFuZGxlIGRpc3Bvc2VcbiAgICBjb25zb2xlLmxvZyhcIldlYkJyb3dzZXJET01SdW5uZXIuZGlzcG9zZVwiKTtcbiAgfVxuXG4gIGdldERvY3VtZW50KCk6IERvY3VtZW50IHtcbiAgICByZXR1cm4gZG9jdW1lbnQ7XG4gIH1cblxuICBnZXREb2N1bWVudFRpbWUoKTogbnVtYmVyIHtcbiAgICBpZiAoZG9jdW1lbnQudGltZWxpbmUgJiYgZG9jdW1lbnQudGltZWxpbmUuY3VycmVudFRpbWUpIHtcbiAgICAgIHJldHVybiBkb2N1bWVudC50aW1lbGluZS5jdXJyZW50VGltZTtcbiAgICB9XG4gICAgcmV0dXJuIERhdGUubm93KCkgLSBkb2N1bWVudExvYWRUaW1lO1xuICB9XG5cbiAgLy8gVE9ETyAtIHJlc29sdmUgdHlwZXMgKFdpbmRvdyBuZWVkcyB0byBleHBvc2UgY2xhc3NlcyBzdWNoIGFzIEN1c3RvbUV2ZW50IGFzIHByb3BlcnRpZXMpXG4gIGdldFdpbmRvdygpOiBhbnkge1xuICAgIHJldHVybiB3aW5kb3c7XG4gIH1cbn1cbiIsICJpbXBvcnQge1xuICBGcm9tSW5zdGFuY2VNZXNzYWdlVHlwZXMsXG4gIFRvSW5zdGFuY2VNZXNzYWdlVHlwZXMsXG59IGZyb20gXCJAbW1sLWlvL25ldHdvcmtlZC1kb20td2ViLXJ1bm5lci9zcmMvbWVzc2FnZS10eXBlc1wiO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZURvbSB9IGZyb20gXCJAbW1sLWlvL29ic2VydmFibGUtZG9tL3NyYy9PYnNlcnZhYmxlRG9tXCI7XG5pbXBvcnQgeyBPYnNlcnZhYmxlRG9tTWVzc2FnZSwgT2JzZXJ2YWJsZURPTVBhcmFtZXRlcnMgfSBmcm9tIFwiQG1tbC1pby9vYnNlcnZhYmxlLWRvbS1jb21tb25cIjtcblxuaW1wb3J0IHsgV2ViQnJvd3NlckRPTVJ1bm5lckZhY3RvcnkgfSBmcm9tIFwiLi9XZWJCcm93c2VyRE9NUnVubmVyXCI7XG5cbi8vIFRoaXMgcnVucyBpbiB0aGUgaWZyYW1lIHRoYXQgd2lsbCBleGVjdXRlIHRoZSBkb2N1bWVudCBzY3JpcHQgdG8gc2V0dXAgdGhlIGxpc3RlbmluZyBmb3IgZXZlbnRzIG1lc3NhZ2VzXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBJZnJhbWVXZWJSdW5uZXIoYXJnc1N0cmluZzogc3RyaW5nKSB7XG4gIGNvbnN0IG9ic2VydmFibGVET01QYXJhbXMgPSBKU09OLnBhcnNlKGF0b2IoYXJnc1N0cmluZykpIGFzIE9ic2VydmFibGVET01QYXJhbWV0ZXJzO1xuXG4gIGNvbnN0IHNlbmRNZXNzYWdlVG9IYW5kbGVyID0gKG1lc3NhZ2U6IEZyb21JbnN0YW5jZU1lc3NhZ2VUeXBlcykgPT4ge1xuICAgIHdpbmRvdy5wYXJlbnQucG9zdE1lc3NhZ2UoSlNPTi5zdHJpbmdpZnkobWVzc2FnZSksIFwiKlwiKTtcbiAgfTtcblxuICBjb25zdCBvYnNlcnZhYmxlRE9NID0gbmV3IE9ic2VydmFibGVEb20oXG4gICAge1xuICAgICAgLi4ub2JzZXJ2YWJsZURPTVBhcmFtcyxcbiAgICAgIGh0bWxDb250ZW50czogXCJcIiwgLy8gVGhpcyBtdXN0IGJlIGVtcHR5IGFzIHRoZSBjb250ZW50cyBhcmUgYXNzdW1lZCB0byBiZSBwcm92aWRlZCBieSB0aGUgc3JjZG9jXG4gICAgfSxcbiAgICAob2JzZXJ2YWJsZURvbU1lc3NhZ2U6IE9ic2VydmFibGVEb21NZXNzYWdlKSA9PiB7XG4gICAgICBzZW5kTWVzc2FnZVRvSGFuZGxlcih7XG4gICAgICAgIHR5cGU6IFwiZG9tXCIsXG4gICAgICAgIG1lc3NhZ2U6IG9ic2VydmFibGVEb21NZXNzYWdlLFxuICAgICAgfSk7XG4gICAgfSxcbiAgICBXZWJCcm93c2VyRE9NUnVubmVyRmFjdG9yeSxcbiAgKTtcblxuICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIiwgKGUpID0+IHtcbiAgICBjb25zdCBwYXJzZWQgPSBKU09OLnBhcnNlKGUuZGF0YSkgYXMgVG9JbnN0YW5jZU1lc3NhZ2VUeXBlcztcbiAgICBpZiAocGFyc2VkLnR5cGUgPT09IFwiZGlzcGF0Y2hSZW1vdGVFdmVudEZyb21Db25uZWN0aW9uSWRcIikge1xuICAgICAgb2JzZXJ2YWJsZURPTS5kaXNwYXRjaFJlbW90ZUV2ZW50RnJvbUNvbm5lY3Rpb25JZChwYXJzZWQuY29ubmVjdGlvbklkLCBwYXJzZWQuZXZlbnQpO1xuICAgIH1cbiAgfSk7XG59XG4iLCAiaW1wb3J0IHsgc2V0dXBJZnJhbWVXZWJSdW5uZXIgfSBmcm9tIFwiLi9JZnJhbWVXZWJSdW5uZXJcIjtcblxuY29uc3QgYXJncyA9ICh3aW5kb3cgYXMgYW55KS5hcmdzO1xuc2V0dXBJZnJhbWVXZWJSdW5uZXIoYXJncyk7XG4iXSwKICAibWFwcGluZ3MiOiAiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFJTyxTQUFTLDBCQUEwQixJQUFvRDtBQUM1RixTQUFPO0FBQUEsSUFDTCxRQUFRLEdBQUc7QUFBQSxJQUNYLEtBQUssR0FBRztBQUFBLElBQ1IsWUFBWSxHQUFHO0FBQUEsSUFDZixZQUFZLEdBQUcsV0FBVyxJQUFJLENBQUMsVUFBVSwwQkFBMEIsS0FBSyxDQUFDO0FBQUEsSUFDekUsYUFBYSxHQUFHO0FBQUEsRUFDbEI7QUFDRjs7O0FDcUNPLElBQU0sZ0JBQU4sTUFBc0Q7QUFBQSxFQVkzRCxZQUNFLHlCQUNBLFVBQ0EsZUFDQTtBQWZGLFNBQVEsZUFBZSxvQkFBSSxJQUFtQztBQUM5RCxTQUFRLGVBQWUsb0JBQUksSUFBbUM7QUFDOUQsU0FBUSw4QkFBOEIsb0JBQUksSUFBMkM7QUFDckYsU0FBUSxrQkFBa0I7QUFFMUIs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\n';
33
+
34
+ // src/RunnerIframe.ts
35
+ var RunnerIframe = class {
36
+ constructor(observableDOMParameters, onMessageCallback) {
37
+ this.iframe = document.createElement("iframe");
38
+ this.iframe.setAttribute("sandbox", "allow-scripts");
39
+ this.iframe.style.position = "fixed";
40
+ this.iframe.style.top = "0";
41
+ this.iframe.style.left = "0";
42
+ this.iframe.style.width = "0";
43
+ this.iframe.style.height = "0";
44
+ this.iframe.style.border = "none";
45
+ const paramsMinusCode = {
46
+ ...observableDOMParameters
47
+ };
48
+ delete paramsMinusCode.htmlContents;
49
+ const args = btoa(JSON.stringify(paramsMinusCode));
50
+ const isJSDOM = navigator.userAgent.includes("jsdom");
51
+ if (isJSDOM) {
52
+ document.body.append(this.iframe);
53
+ const iframeBody = this.iframe.contentWindow.document.body;
54
+ const argsScriptElement = document.createElement("script");
55
+ argsScriptElement.innerHTML = `window.args="${args}";`;
56
+ iframeBody.append(argsScriptElement);
57
+ const runnerScriptElement = document.createElement("script");
58
+ runnerScriptElement.innerHTML = build_default;
59
+ iframeBody.append(runnerScriptElement);
60
+ const contentHolder = document.createElement("div");
61
+ iframeBody.append(contentHolder);
62
+ contentHolder.innerHTML = observableDOMParameters.htmlContents;
63
+ } else {
64
+ this.iframe.setAttribute(
65
+ "srcdoc",
66
+ `
67
+ <script>window.args="${args}";</script>
68
+ <script>${build_default}</script>
69
+ ${observableDOMParameters.htmlContents}
70
+ `
71
+ );
72
+ document.body.append(this.iframe);
73
+ }
74
+ this.onMessageCallback = onMessageCallback;
75
+ this.postMessageListener = (e) => {
76
+ if (e.source === this.iframe.contentWindow || isJSDOM && e.source === null) {
77
+ const parsed = JSON.parse(e.data);
78
+ onMessageCallback(parsed);
79
+ }
80
+ };
81
+ window.addEventListener("message", this.postMessageListener);
82
+ }
83
+ sendMessageToRunner(message) {
84
+ this.iframe.contentWindow.postMessage(JSON.stringify(message), "*");
85
+ }
86
+ dispose() {
87
+ window.removeEventListener("message", this.postMessageListener);
88
+ this.iframe.remove();
89
+ }
90
+ };
91
+
92
+ // src/NetworkedDOMWebRunnerClient.ts
93
+ var import_networked_dom_web = require("@mml-io/networked-dom-web");
94
+
95
+ // src/FakeWebsocket.ts
96
+ var WebsocketEnd = class extends EventTarget {
97
+ constructor(protocol, sendCallback) {
98
+ super();
99
+ this.protocol = protocol;
100
+ this.sendCallback = sendCallback;
101
+ }
102
+ close() {
103
+ this.dispatchEvent(new CloseEvent("close"));
104
+ }
105
+ addEventListener(type, listener, options) {
106
+ if (type === "open") {
107
+ listener.bind(this)(new Event("open"));
108
+ return;
109
+ }
110
+ super.addEventListener(type, listener, options);
111
+ }
112
+ send(data) {
113
+ this.sendCallback(data);
114
+ }
115
+ };
116
+ var FakeWebsocket = class {
117
+ constructor(protocol) {
118
+ this.clientSideWebsocket = new WebsocketEnd(protocol, (data) => {
119
+ this.serverSideWebsocket.dispatchEvent(
120
+ new MessageEvent("message", {
121
+ data
122
+ })
123
+ );
124
+ });
125
+ this.serverSideWebsocket = new WebsocketEnd(protocol, (data) => {
126
+ this.clientSideWebsocket.dispatchEvent(
127
+ new MessageEvent("message", {
128
+ data
129
+ })
130
+ );
131
+ });
132
+ }
133
+ };
134
+
135
+ // src/NetworkedDOMWebRunnerClient.ts
136
+ var NetworkedDOMWebRunnerClient = class {
137
+ constructor(enableEventHandling = true) {
138
+ this.connectedState = null;
139
+ this.enableEventHandling = enableEventHandling;
140
+ this.element = document.createElement("div");
141
+ this.element.style.position = "relative";
142
+ this.element.style.width = "100%";
143
+ this.element.style.height = "100%";
144
+ this.element.style.border = "1px solid black";
145
+ this.remoteDocumentHolder = document.createElement("div");
146
+ this.element.append(this.remoteDocumentHolder);
147
+ }
148
+ disconnect() {
149
+ if (!this.connectedState) {
150
+ return;
151
+ }
152
+ this.connectedState.document.removeWebSocket(
153
+ this.connectedState.fakeWebsocket.serverSideWebsocket
154
+ );
155
+ this.connectedState = null;
156
+ }
157
+ dispose() {
158
+ this.disconnect();
159
+ this.remoteDocumentHolder.remove();
160
+ this.element.remove();
161
+ }
162
+ connect(document2) {
163
+ if (this.connectedState) {
164
+ this.disconnect();
165
+ }
166
+ const fakeWebsocket = new FakeWebsocket("networked-dom-v0.1");
167
+ let overriddenHandler = null;
168
+ const eventHandler = (element, event) => {
169
+ if (!overriddenHandler) {
170
+ throw new Error("overriddenHandler not set");
171
+ }
172
+ overriddenHandler(element, event);
173
+ };
174
+ if (this.enableEventHandling) {
175
+ this.remoteDocumentHolder.addEventListener("click", (event) => {
176
+ eventHandler(event.target, event);
177
+ event.stopPropagation();
178
+ event.preventDefault();
179
+ return false;
180
+ });
181
+ }
182
+ const domWebsocket = new import_networked_dom_web.NetworkedDOMWebsocket(
183
+ "ws://localhost",
184
+ () => fakeWebsocket.clientSideWebsocket,
185
+ this.remoteDocumentHolder
186
+ );
187
+ overriddenHandler = (element, event) => {
188
+ domWebsocket.handleEvent(element, event);
189
+ };
190
+ document2.addWebSocket(fakeWebsocket.serverSideWebsocket);
191
+ this.connectedState = {
192
+ document: document2,
193
+ fakeWebsocket,
194
+ domWebsocket
195
+ };
196
+ }
197
+ };
198
+
199
+ // src/IframeObservableDOMFactory.ts
200
+ var IframeObservableDOMFactory = (observableDOMParameters, callback) => {
201
+ const runnerIframe = new RunnerIframe(
202
+ observableDOMParameters,
203
+ (msg) => {
204
+ if (msg.type === "dom") {
205
+ callback(msg.message);
206
+ }
207
+ }
208
+ );
209
+ const remoteObservableDOM = {
210
+ addConnectedUserId(connectionId) {
211
+ runnerIframe.sendMessageToRunner({
212
+ type: "addConnectedUserId",
213
+ connectionId
214
+ });
215
+ },
216
+ addIPCWebsocket() {
217
+ throw new Error("Not implemented");
218
+ },
219
+ dispatchRemoteEventFromConnectionId(connectionId, remoteEvent) {
220
+ runnerIframe.sendMessageToRunner({
221
+ type: "dispatchRemoteEventFromConnectionId",
222
+ connectionId,
223
+ event: remoteEvent
224
+ });
225
+ },
226
+ dispose() {
227
+ runnerIframe.dispose();
228
+ },
229
+ removeConnectedUserId(connectionId) {
230
+ runnerIframe.sendMessageToRunner({
231
+ type: "removeConnectedUserId",
232
+ connectionId
233
+ });
234
+ }
235
+ };
236
+ return remoteObservableDOM;
237
+ };
238
+ // Annotate the CommonJS export names for ESM import in node:
239
+ 0 && (module.exports = {
240
+ FakeWebsocket,
241
+ IframeObservableDOMFactory,
242
+ NetworkedDOMWebRunnerClient,
243
+ RunnerIframe,
244
+ ...require("@mml-io/networked-dom-document")
245
+ });
246
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/RunnerIframe.ts", "../src/NetworkedDOMWebRunnerClient.ts", "../src/FakeWebsocket.ts", "../src/IframeObservableDOMFactory.ts"],
4
+ "sourcesContent": ["export * from \"@mml-io/networked-dom-document\";\n\nexport * from \"./RunnerIframe\";\nexport * from \"./NetworkedDOMWebRunnerClient\";\nexport * from \"./FakeWebsocket\";\nexport * from \"./IframeObservableDOMFactory\";\n", "import { ObservableDOMParameters } from \"@mml-io/observable-dom-common\";\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore\n// eslint-disable-next-line import/no-unresolved\nimport runnerText from \"runner-iframe-js-text\";\n\nimport { FromInstanceMessageTypes, ToInstanceMessageTypes } from \"./message-types\";\n\nexport class RunnerIframe {\n private iframe: HTMLIFrameElement;\n private onMessageCallback: (msg: FromInstanceMessageTypes) => void;\n private postMessageListener: (msg: MessageEvent) => void;\n\n constructor(\n observableDOMParameters: ObservableDOMParameters,\n onMessageCallback: (msg: FromInstanceMessageTypes) => void,\n ) {\n this.iframe = document.createElement(\"iframe\");\n this.iframe.setAttribute(\"sandbox\", \"allow-scripts\");\n this.iframe.style.position = \"fixed\";\n this.iframe.style.top = \"0\";\n this.iframe.style.left = \"0\";\n this.iframe.style.width = \"0\";\n this.iframe.style.height = \"0\";\n this.iframe.style.border = \"none\";\n\n const paramsMinusCode: Partial<ObservableDOMParameters> = {\n ...observableDOMParameters,\n };\n delete paramsMinusCode.htmlContents;\n\n const args = btoa(JSON.stringify(paramsMinusCode));\n\n const isJSDOM = navigator.userAgent.includes(\"jsdom\");\n if (isJSDOM) {\n // srcdoc not supported, so we have to append elements to the iframe's document\n document.body.append(this.iframe);\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const iframeBody = this.iframe.contentWindow!.document.body;\n const argsScriptElement = document.createElement(\"script\");\n argsScriptElement.innerHTML = `window.args=\"${args}\";`;\n iframeBody.append(argsScriptElement);\n const runnerScriptElement = document.createElement(\"script\");\n runnerScriptElement.innerHTML = runnerText;\n iframeBody.append(runnerScriptElement);\n const contentHolder = document.createElement(\"div\");\n iframeBody.append(contentHolder);\n contentHolder.innerHTML = observableDOMParameters.htmlContents;\n } else {\n this.iframe.setAttribute(\n \"srcdoc\",\n `\n <script>window.args=\"${args}\";</script>\n <script>${runnerText}</script>\n ${observableDOMParameters.htmlContents}\n `,\n );\n document.body.append(this.iframe);\n }\n\n this.onMessageCallback = onMessageCallback;\n\n this.postMessageListener = (e: MessageEvent) => {\n if (e.source === this.iframe.contentWindow || (isJSDOM && e.source === null)) {\n const parsed = JSON.parse(e.data) as FromInstanceMessageTypes;\n onMessageCallback(parsed);\n }\n };\n window.addEventListener(\"message\", this.postMessageListener);\n }\n\n sendMessageToRunner(message: ToInstanceMessageTypes) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.iframe.contentWindow!.postMessage(JSON.stringify(message), \"*\");\n }\n\n dispose() {\n window.removeEventListener(\"message\", this.postMessageListener);\n this.iframe.remove();\n }\n}\n", "import { EditableNetworkedDOM, NetworkedDOM } from \"@mml-io/networked-dom-document\";\nimport { NetworkedDOMWebsocket } from \"@mml-io/networked-dom-web\";\n\nimport { FakeWebsocket } from \"./FakeWebsocket\";\n\nexport class NetworkedDOMWebRunnerClient {\n public readonly element: HTMLDivElement;\n protected remoteDocumentHolder: HTMLElement;\n\n protected connectedState: {\n document: NetworkedDOM | EditableNetworkedDOM;\n domWebsocket: NetworkedDOMWebsocket;\n fakeWebsocket: FakeWebsocket;\n } | null = null;\n private enableEventHandling: boolean;\n\n constructor(enableEventHandling = true) {\n this.enableEventHandling = enableEventHandling;\n this.element = document.createElement(\"div\");\n this.element.style.position = \"relative\";\n this.element.style.width = \"100%\";\n this.element.style.height = \"100%\";\n this.element.style.border = \"1px solid black\";\n\n this.remoteDocumentHolder = document.createElement(\"div\");\n this.element.append(this.remoteDocumentHolder);\n }\n\n public disconnect() {\n if (!this.connectedState) {\n return;\n }\n this.connectedState.document.removeWebSocket(\n this.connectedState.fakeWebsocket.serverSideWebsocket as unknown as WebSocket,\n );\n this.connectedState = null;\n }\n\n public dispose() {\n this.disconnect();\n this.remoteDocumentHolder.remove();\n this.element.remove();\n }\n\n public connect(document: NetworkedDOM | EditableNetworkedDOM) {\n if (this.connectedState) {\n this.disconnect();\n }\n const fakeWebsocket = new FakeWebsocket(\"networked-dom-v0.1\");\n let overriddenHandler: ((element: HTMLElement, event: CustomEvent) => void) | null = null;\n const eventHandler = (element: HTMLElement, event: CustomEvent) => {\n if (!overriddenHandler) {\n throw new Error(\"overriddenHandler not set\");\n }\n overriddenHandler(element, event);\n };\n\n if (this.enableEventHandling) {\n this.remoteDocumentHolder.addEventListener(\"click\", (event: Event) => {\n eventHandler(event.target as HTMLElement, event as CustomEvent);\n event.stopPropagation();\n event.preventDefault();\n return false;\n });\n }\n\n const domWebsocket = new NetworkedDOMWebsocket(\n \"ws://localhost\",\n () => fakeWebsocket.clientSideWebsocket as unknown as WebSocket,\n this.remoteDocumentHolder,\n );\n overriddenHandler = (element: HTMLElement, event: CustomEvent) => {\n domWebsocket.handleEvent(element, event);\n };\n document.addWebSocket(fakeWebsocket.serverSideWebsocket as unknown as WebSocket);\n this.connectedState = {\n document,\n fakeWebsocket,\n domWebsocket,\n };\n }\n}\n", "class WebsocketEnd extends EventTarget {\n private readonly sendCallback: (data: string | ArrayBufferLike | Blob | ArrayBufferView) => void;\n public readonly protocol: string;\n\n constructor(\n protocol: string,\n sendCallback: (data: string | ArrayBufferLike | Blob | ArrayBufferView) => void,\n ) {\n super();\n this.protocol = protocol;\n this.sendCallback = sendCallback;\n }\n\n public close() {\n this.dispatchEvent(new CloseEvent(\"close\"));\n }\n\n public addEventListener<K extends keyof WebSocketEventMap>(\n type: K,\n listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,\n options?: boolean | AddEventListenerOptions,\n ) {\n if (type === \"open\") {\n listener.bind(this)(new Event(\"open\"));\n return;\n }\n super.addEventListener(type, listener, options);\n }\n\n public send(data: string | ArrayBufferLike | Blob | ArrayBufferView) {\n this.sendCallback(data);\n }\n}\n\nexport class FakeWebsocket {\n public clientSideWebsocket: WebsocketEnd;\n public serverSideWebsocket: WebsocketEnd;\n\n constructor(protocol: string) {\n this.clientSideWebsocket = new WebsocketEnd(protocol, (data) => {\n this.serverSideWebsocket.dispatchEvent(\n new MessageEvent(\"message\", {\n data,\n }),\n );\n });\n\n this.serverSideWebsocket = new WebsocketEnd(protocol, (data) => {\n this.clientSideWebsocket.dispatchEvent(\n new MessageEvent(\"message\", {\n data,\n }),\n );\n });\n }\n}\n", "import { ObservableDomFactory } from \"@mml-io/networked-dom-document\";\nimport {\n ObservableDomInterface,\n ObservableDomMessage,\n ObservableDOMParameters,\n RemoteEvent,\n} from \"@mml-io/observable-dom-common\";\n\nimport { FromInstanceMessageTypes } from \"./message-types\";\nimport { RunnerIframe } from \"./RunnerIframe\";\n\nexport const IframeObservableDOMFactory: ObservableDomFactory = (\n observableDOMParameters: ObservableDOMParameters,\n callback: (message: ObservableDomMessage) => void,\n) => {\n const runnerIframe = new RunnerIframe(\n observableDOMParameters,\n (msg: FromInstanceMessageTypes) => {\n if (msg.type === \"dom\") {\n callback(msg.message);\n }\n },\n );\n\n const remoteObservableDOM: ObservableDomInterface = {\n addConnectedUserId(connectionId: number): void {\n runnerIframe.sendMessageToRunner({\n type: \"addConnectedUserId\",\n connectionId,\n });\n },\n addIPCWebsocket(): void {\n throw new Error(\"Not implemented\");\n },\n dispatchRemoteEventFromConnectionId(connectionId: number, remoteEvent: RemoteEvent): void {\n runnerIframe.sendMessageToRunner({\n type: \"dispatchRemoteEventFromConnectionId\",\n connectionId,\n event: remoteEvent,\n });\n },\n dispose(): void {\n runnerIframe.dispose();\n },\n removeConnectedUserId(connectionId: number): void {\n runnerIframe.sendMessageToRunner({\n type: \"removeConnectedUserId\",\n connectionId,\n });\n },\n };\n return remoteObservableDOM;\n};\n"],
5
+ "mappings": "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",
6
+ "names": ["document"]
7
+ }
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@mml-io/networked-dom-web-runner",
3
+ "version": "0.0.42",
4
+ "main": "./build/index.js",
5
+ "types": "./build/index.d.ts",
6
+ "files": [
7
+ "/build",
8
+ "/src"
9
+ ],
10
+ "scripts": {
11
+ "type-check": "tsc --noEmit",
12
+ "build": "node ./build.js --build",
13
+ "iterate": "node ./build.js --watch",
14
+ "npm-publish": "npm run build && publish-if-not-exists --access=public",
15
+ "lint": "eslint \"./src/**/*.{js,jsx,ts,tsx}\" --max-warnings 0",
16
+ "lint-fix": "eslint \"./src/**/*.{js,jsx,ts,tsx}\" --fix",
17
+ "test": "npm run build && jest",
18
+ "test-iterate": "jest --watch"
19
+ },
20
+ "dependencies": {
21
+ "@mml-io/networked-dom-web": "^0.0.42"
22
+ },
23
+ "devDependencies": {
24
+ "@mml-io/networked-dom-web-runner-iframe": "file:../networked-dom-web-runner-iframe",
25
+ "@types/three": "0.151.0",
26
+ "jest-canvas-mock": "2.5.0",
27
+ "jest-environment-jsdom": "29.5.0",
28
+ "jest-expect-message": "1.1.3",
29
+ "standardized-audio-context-mock": "9.6.17"
30
+ }
31
+ }
@@ -0,0 +1,56 @@
1
+ class WebsocketEnd extends EventTarget {
2
+ private readonly sendCallback: (data: string | ArrayBufferLike | Blob | ArrayBufferView) => void;
3
+ public readonly protocol: string;
4
+
5
+ constructor(
6
+ protocol: string,
7
+ sendCallback: (data: string | ArrayBufferLike | Blob | ArrayBufferView) => void,
8
+ ) {
9
+ super();
10
+ this.protocol = protocol;
11
+ this.sendCallback = sendCallback;
12
+ }
13
+
14
+ public close() {
15
+ this.dispatchEvent(new CloseEvent("close"));
16
+ }
17
+
18
+ public addEventListener<K extends keyof WebSocketEventMap>(
19
+ type: K,
20
+ listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any,
21
+ options?: boolean | AddEventListenerOptions,
22
+ ) {
23
+ if (type === "open") {
24
+ listener.bind(this)(new Event("open"));
25
+ return;
26
+ }
27
+ super.addEventListener(type, listener, options);
28
+ }
29
+
30
+ public send(data: string | ArrayBufferLike | Blob | ArrayBufferView) {
31
+ this.sendCallback(data);
32
+ }
33
+ }
34
+
35
+ export class FakeWebsocket {
36
+ public clientSideWebsocket: WebsocketEnd;
37
+ public serverSideWebsocket: WebsocketEnd;
38
+
39
+ constructor(protocol: string) {
40
+ this.clientSideWebsocket = new WebsocketEnd(protocol, (data) => {
41
+ this.serverSideWebsocket.dispatchEvent(
42
+ new MessageEvent("message", {
43
+ data,
44
+ }),
45
+ );
46
+ });
47
+
48
+ this.serverSideWebsocket = new WebsocketEnd(protocol, (data) => {
49
+ this.clientSideWebsocket.dispatchEvent(
50
+ new MessageEvent("message", {
51
+ data,
52
+ }),
53
+ );
54
+ });
55
+ }
56
+ }
@@ -0,0 +1,53 @@
1
+ import { ObservableDomFactory } from "@mml-io/networked-dom-document";
2
+ import {
3
+ ObservableDomInterface,
4
+ ObservableDomMessage,
5
+ ObservableDOMParameters,
6
+ RemoteEvent,
7
+ } from "@mml-io/observable-dom-common";
8
+
9
+ import { FromInstanceMessageTypes } from "./message-types";
10
+ import { RunnerIframe } from "./RunnerIframe";
11
+
12
+ export const IframeObservableDOMFactory: ObservableDomFactory = (
13
+ observableDOMParameters: ObservableDOMParameters,
14
+ callback: (message: ObservableDomMessage) => void,
15
+ ) => {
16
+ const runnerIframe = new RunnerIframe(
17
+ observableDOMParameters,
18
+ (msg: FromInstanceMessageTypes) => {
19
+ if (msg.type === "dom") {
20
+ callback(msg.message);
21
+ }
22
+ },
23
+ );
24
+
25
+ const remoteObservableDOM: ObservableDomInterface = {
26
+ addConnectedUserId(connectionId: number): void {
27
+ runnerIframe.sendMessageToRunner({
28
+ type: "addConnectedUserId",
29
+ connectionId,
30
+ });
31
+ },
32
+ addIPCWebsocket(): void {
33
+ throw new Error("Not implemented");
34
+ },
35
+ dispatchRemoteEventFromConnectionId(connectionId: number, remoteEvent: RemoteEvent): void {
36
+ runnerIframe.sendMessageToRunner({
37
+ type: "dispatchRemoteEventFromConnectionId",
38
+ connectionId,
39
+ event: remoteEvent,
40
+ });
41
+ },
42
+ dispose(): void {
43
+ runnerIframe.dispose();
44
+ },
45
+ removeConnectedUserId(connectionId: number): void {
46
+ runnerIframe.sendMessageToRunner({
47
+ type: "removeConnectedUserId",
48
+ connectionId,
49
+ });
50
+ },
51
+ };
52
+ return remoteObservableDOM;
53
+ };
@@ -0,0 +1,82 @@
1
+ import { EditableNetworkedDOM, NetworkedDOM } from "@mml-io/networked-dom-document";
2
+ import { NetworkedDOMWebsocket } from "@mml-io/networked-dom-web";
3
+
4
+ import { FakeWebsocket } from "./FakeWebsocket";
5
+
6
+ export class NetworkedDOMWebRunnerClient {
7
+ public readonly element: HTMLDivElement;
8
+ protected remoteDocumentHolder: HTMLElement;
9
+
10
+ protected connectedState: {
11
+ document: NetworkedDOM | EditableNetworkedDOM;
12
+ domWebsocket: NetworkedDOMWebsocket;
13
+ fakeWebsocket: FakeWebsocket;
14
+ } | null = null;
15
+ private enableEventHandling: boolean;
16
+
17
+ constructor(enableEventHandling = true) {
18
+ this.enableEventHandling = enableEventHandling;
19
+ this.element = document.createElement("div");
20
+ this.element.style.position = "relative";
21
+ this.element.style.width = "100%";
22
+ this.element.style.height = "100%";
23
+ this.element.style.border = "1px solid black";
24
+
25
+ this.remoteDocumentHolder = document.createElement("div");
26
+ this.element.append(this.remoteDocumentHolder);
27
+ }
28
+
29
+ public disconnect() {
30
+ if (!this.connectedState) {
31
+ return;
32
+ }
33
+ this.connectedState.document.removeWebSocket(
34
+ this.connectedState.fakeWebsocket.serverSideWebsocket as unknown as WebSocket,
35
+ );
36
+ this.connectedState = null;
37
+ }
38
+
39
+ public dispose() {
40
+ this.disconnect();
41
+ this.remoteDocumentHolder.remove();
42
+ this.element.remove();
43
+ }
44
+
45
+ public connect(document: NetworkedDOM | EditableNetworkedDOM) {
46
+ if (this.connectedState) {
47
+ this.disconnect();
48
+ }
49
+ const fakeWebsocket = new FakeWebsocket("networked-dom-v0.1");
50
+ let overriddenHandler: ((element: HTMLElement, event: CustomEvent) => void) | null = null;
51
+ const eventHandler = (element: HTMLElement, event: CustomEvent) => {
52
+ if (!overriddenHandler) {
53
+ throw new Error("overriddenHandler not set");
54
+ }
55
+ overriddenHandler(element, event);
56
+ };
57
+
58
+ if (this.enableEventHandling) {
59
+ this.remoteDocumentHolder.addEventListener("click", (event: Event) => {
60
+ eventHandler(event.target as HTMLElement, event as CustomEvent);
61
+ event.stopPropagation();
62
+ event.preventDefault();
63
+ return false;
64
+ });
65
+ }
66
+
67
+ const domWebsocket = new NetworkedDOMWebsocket(
68
+ "ws://localhost",
69
+ () => fakeWebsocket.clientSideWebsocket as unknown as WebSocket,
70
+ this.remoteDocumentHolder,
71
+ );
72
+ overriddenHandler = (element: HTMLElement, event: CustomEvent) => {
73
+ domWebsocket.handleEvent(element, event);
74
+ };
75
+ document.addWebSocket(fakeWebsocket.serverSideWebsocket as unknown as WebSocket);
76
+ this.connectedState = {
77
+ document,
78
+ fakeWebsocket,
79
+ domWebsocket,
80
+ };
81
+ }
82
+ }
@@ -0,0 +1,81 @@
1
+ import { ObservableDOMParameters } from "@mml-io/observable-dom-common";
2
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
3
+ // @ts-ignore
4
+ // eslint-disable-next-line import/no-unresolved
5
+ import runnerText from "runner-iframe-js-text";
6
+
7
+ import { FromInstanceMessageTypes, ToInstanceMessageTypes } from "./message-types";
8
+
9
+ export class RunnerIframe {
10
+ private iframe: HTMLIFrameElement;
11
+ private onMessageCallback: (msg: FromInstanceMessageTypes) => void;
12
+ private postMessageListener: (msg: MessageEvent) => void;
13
+
14
+ constructor(
15
+ observableDOMParameters: ObservableDOMParameters,
16
+ onMessageCallback: (msg: FromInstanceMessageTypes) => void,
17
+ ) {
18
+ this.iframe = document.createElement("iframe");
19
+ this.iframe.setAttribute("sandbox", "allow-scripts");
20
+ this.iframe.style.position = "fixed";
21
+ this.iframe.style.top = "0";
22
+ this.iframe.style.left = "0";
23
+ this.iframe.style.width = "0";
24
+ this.iframe.style.height = "0";
25
+ this.iframe.style.border = "none";
26
+
27
+ const paramsMinusCode: Partial<ObservableDOMParameters> = {
28
+ ...observableDOMParameters,
29
+ };
30
+ delete paramsMinusCode.htmlContents;
31
+
32
+ const args = btoa(JSON.stringify(paramsMinusCode));
33
+
34
+ const isJSDOM = navigator.userAgent.includes("jsdom");
35
+ if (isJSDOM) {
36
+ // srcdoc not supported, so we have to append elements to the iframe's document
37
+ document.body.append(this.iframe);
38
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
39
+ const iframeBody = this.iframe.contentWindow!.document.body;
40
+ const argsScriptElement = document.createElement("script");
41
+ argsScriptElement.innerHTML = `window.args="${args}";`;
42
+ iframeBody.append(argsScriptElement);
43
+ const runnerScriptElement = document.createElement("script");
44
+ runnerScriptElement.innerHTML = runnerText;
45
+ iframeBody.append(runnerScriptElement);
46
+ const contentHolder = document.createElement("div");
47
+ iframeBody.append(contentHolder);
48
+ contentHolder.innerHTML = observableDOMParameters.htmlContents;
49
+ } else {
50
+ this.iframe.setAttribute(
51
+ "srcdoc",
52
+ `
53
+ <script>window.args="${args}";</script>
54
+ <script>${runnerText}</script>
55
+ ${observableDOMParameters.htmlContents}
56
+ `,
57
+ );
58
+ document.body.append(this.iframe);
59
+ }
60
+
61
+ this.onMessageCallback = onMessageCallback;
62
+
63
+ this.postMessageListener = (e: MessageEvent) => {
64
+ if (e.source === this.iframe.contentWindow || (isJSDOM && e.source === null)) {
65
+ const parsed = JSON.parse(e.data) as FromInstanceMessageTypes;
66
+ onMessageCallback(parsed);
67
+ }
68
+ };
69
+ window.addEventListener("message", this.postMessageListener);
70
+ }
71
+
72
+ sendMessageToRunner(message: ToInstanceMessageTypes) {
73
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
74
+ this.iframe.contentWindow!.postMessage(JSON.stringify(message), "*");
75
+ }
76
+
77
+ dispose() {
78
+ window.removeEventListener("message", this.postMessageListener);
79
+ this.iframe.remove();
80
+ }
81
+ }
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * from "@mml-io/networked-dom-document";
2
+
3
+ export * from "./RunnerIframe";
4
+ export * from "./NetworkedDOMWebRunnerClient";
5
+ export * from "./FakeWebsocket";
6
+ export * from "./IframeObservableDOMFactory";
@@ -0,0 +1,30 @@
1
+ import { RemoteEvent } from "@mml-io/networked-dom-protocol";
2
+ import { ObservableDomMessage } from "@mml-io/observable-dom-common";
3
+
4
+ export type AddConnectedUserIdMessage = {
5
+ type: "addConnectedUserId";
6
+ connectionId: number;
7
+ };
8
+
9
+ export type RemoveConnectedUserIdMessage = {
10
+ type: "removeConnectedUserId";
11
+ connectionId: number;
12
+ };
13
+
14
+ export type DispatchRemoteEventFromConnectionIdMessage = {
15
+ type: "dispatchRemoteEventFromConnectionId";
16
+ connectionId: number;
17
+ event: RemoteEvent;
18
+ };
19
+
20
+ export type ToInstanceMessageTypes =
21
+ | AddConnectedUserIdMessage
22
+ | RemoveConnectedUserIdMessage
23
+ | DispatchRemoteEventFromConnectionIdMessage;
24
+
25
+ type DOMMessage = {
26
+ type: "dom";
27
+ message: ObservableDomMessage;
28
+ };
29
+
30
+ export type FromInstanceMessageTypes = DOMMessage;