@mml-io/observable-dom 0.1.1 → 0.1.3

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/build/index.js CHANGED
@@ -380,6 +380,9 @@ var ObservableDom = class {
380
380
  // src/JSDOMRunner.ts
381
381
  var import_vm = __toESM(require("vm"));
382
382
  var import_jsdom = require("jsdom");
383
+ var nodeFetch = __toESM(require("node-fetch"));
384
+ var import_node_fetch = __toESM(require("node-fetch"));
385
+ var ErrDomWindowNotInitialized = "DOMWindow not initialized";
383
386
  var monkeyPatchedMutationRecordCallbacks = /* @__PURE__ */ new Set();
384
387
  function installMutationObserverMonkeyPatch() {
385
388
  const MutationRecordExports = require("jsdom/lib/jsdom/living/generated/MutationRecord");
@@ -404,6 +407,8 @@ var RejectionResourceLoader = class extends import_jsdom.ResourceLoader {
404
407
  var JSDOMRunner = class {
405
408
  constructor(htmlPath, htmlContents, params, callback) {
406
409
  this.ipcWebsockets = /* @__PURE__ */ new Set();
410
+ this.domWindow = null;
411
+ this.mutationObserver = null;
407
412
  this.ipcListeners = /* @__PURE__ */ new Set();
408
413
  this.documentStartTime = Date.now();
409
414
  this.isLoaded = false;
@@ -415,12 +420,13 @@ var JSDOMRunner = class {
415
420
  monkeyPatchInstalled = true;
416
421
  }
417
422
  this.monkeyPatchMutationRecordCallback = () => {
418
- const records = this.mutationObserver.takeRecords();
419
- if (records.length > 1) {
423
+ var _a;
424
+ const records = (_a = this.mutationObserver) == null ? void 0 : _a.takeRecords();
425
+ if (records && records.length > 1) {
420
426
  throw new Error(
421
427
  "The monkey patching should have prevented more than one record being handled at a time"
422
428
  );
423
- } else if (records.length > 0) {
429
+ } else if (records && records.length > 0) {
424
430
  this.callback({
425
431
  mutationList: records
426
432
  });
@@ -434,10 +440,10 @@ var JSDOMRunner = class {
434
440
  virtualConsole: this.createVirtualConsole(),
435
441
  beforeParse: (window) => {
436
442
  this.domWindow = window;
437
- this.domWindow.fetch = fetch;
438
- this.domWindow.Headers = Headers;
439
- this.domWindow.Request = Request;
440
- this.domWindow.Response = Response;
443
+ this.domWindow.fetch = import_node_fetch.default;
444
+ this.domWindow.Headers = nodeFetch.Headers;
445
+ this.domWindow.Request = nodeFetch.Request;
446
+ this.domWindow.Response = nodeFetch.Response;
441
447
  const timeline = {};
442
448
  Object.defineProperty(timeline, "currentTime", {
443
449
  get: () => {
@@ -468,7 +474,8 @@ var JSDOMRunner = class {
468
474
  });
469
475
  });
470
476
  window.addEventListener("load", () => {
471
- this.mutationObserver.observe(window.document, {
477
+ var _a;
478
+ (_a = this.mutationObserver) == null ? void 0 : _a.observe(window.document, {
472
479
  attributes: true,
473
480
  childList: true,
474
481
  subtree: true,
@@ -501,12 +508,18 @@ var JSDOMRunner = class {
501
508
  });
502
509
  }
503
510
  getDocument() {
511
+ if (!this.domWindow) {
512
+ throw new Error(ErrDomWindowNotInitialized);
513
+ }
504
514
  return this.domWindow.document;
505
515
  }
506
516
  getWindow() {
507
517
  return this.domWindow;
508
518
  }
509
519
  addIPCWebsocket(webSocket) {
520
+ if (!this.domWindow) {
521
+ throw new Error(ErrDomWindowNotInitialized);
522
+ }
510
523
  if (this.ipcListeners.size === 0) {
511
524
  console.error("ipc requested, but no ipc listeners registered on document:", this.htmlPath);
512
525
  webSocket.close();
@@ -527,18 +540,22 @@ var JSDOMRunner = class {
527
540
  return;
528
541
  }
529
542
  dispose() {
530
- const records = this.mutationObserver.takeRecords();
543
+ var _a, _b;
544
+ const records = (_a = this.mutationObserver) == null ? void 0 : _a.takeRecords();
531
545
  this.callback({
532
546
  mutationList: records
533
547
  });
534
548
  monkeyPatchedMutationRecordCallbacks.delete(this.monkeyPatchMutationRecordCallback);
535
- this.mutationObserver.disconnect();
549
+ (_b = this.mutationObserver) == null ? void 0 : _b.disconnect();
536
550
  this.jsDom.window.close();
537
551
  }
538
552
  getDocumentTime() {
539
553
  return Date.now() - this.documentStartTime;
540
554
  }
541
555
  dispatchRemoteEventFromConnectionId(connectionId, domNode, remoteEvent) {
556
+ if (!this.domWindow) {
557
+ throw new Error(ErrDomWindowNotInitialized);
558
+ }
542
559
  const bubbles = remoteEvent.bubbles || false;
543
560
  const remoteEventObject = new this.domWindow.CustomEvent(remoteEvent.name, {
544
561
  bubbles,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts", "../src/utils.ts", "../src/ObservableDom.ts", "../src/JSDOMRunner.ts"],
4
- "sourcesContent": ["export * from \"./ObservableDom\";\nexport * from \"./JSDOMRunner\";\n", "import { StaticVirtualDomElement } from \"@mml-io/observable-dom-common\";\n\nimport { LiveVirtualDomElement } from \"./ObservableDom\";\n\nexport function virtualDomElementToStatic(el: LiveVirtualDomElement): StaticVirtualDomElement {\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", "import {\n LogMessage,\n ObservableDomInterface,\n ObservableDomMessage,\n ObservableDOMParameters,\n RemoteEvent,\n StaticVirtualDomElement,\n StaticVirtualDomMutationIdsRecord,\n} from \"@mml-io/observable-dom-common\";\n\nimport { virtualDomElementToStatic } from \"./utils\";\n\nexport type DOMRunnerMessage = {\n loaded?: boolean;\n mutationList?: Array<MutationRecord>;\n logMessage?: LogMessage;\n};\n\nexport type DOMRunnerInterface = {\n getDocument(): Document;\n getWindow(): Window & {\n CustomEvent: typeof CustomEvent;\n Text: typeof Text;\n HTMLScriptElement: typeof HTMLScriptElement;\n Comment: typeof Comment;\n }; // TODO - Define this without using JSDOM types\n addIPCWebsocket(webSocket: WebSocket): void;\n dispatchRemoteEventFromConnectionId(\n connectionId: number,\n realElement: Element,\n remoteEvent: RemoteEvent,\n ): void;\n dispose(): void;\n getDocumentTime(): number;\n};\n\nexport type DOMRunnerFactory = (\n htmlPath: string,\n htmlContents: string,\n params: object,\n callback: (domRunnerMessage: DOMRunnerMessage) => void,\n) => DOMRunnerInterface;\n\nexport type LiveVirtualDomElement = Omit<StaticVirtualDomElement, \"childNodes\"> & {\n realElement: Element | Text;\n childNodes: Array<LiveVirtualDomElement>;\n parent: LiveVirtualDomElement | null;\n};\n\nexport class ObservableDom implements ObservableDomInterface {\n private nodeToNodeId = new Map<LiveVirtualDomElement, number>();\n private nodeIdToNode = new Map<number, LiveVirtualDomElement>();\n private realElementToVirtualElement = new Map<Element | Text, LiveVirtualDomElement>();\n private ignoreTextNodes = true;\n private callback: (message: ObservableDomMessage) => void;\n private nextNodeId = 1;\n private htmlPath: string;\n private domRunner: DOMRunnerInterface;\n\n private documentTimeIntervalTimer: NodeJS.Timer;\n\n constructor(\n observableDOMParameters: ObservableDOMParameters,\n callback: (message: ObservableDomMessage) => void,\n runnerFactory: DOMRunnerFactory,\n ) {\n this.htmlPath = observableDOMParameters.htmlPath;\n this.ignoreTextNodes = observableDOMParameters.ignoreTextNodes;\n this.callback = callback;\n\n this.documentTimeIntervalTimer = setInterval(() => {\n this.callback({\n documentTime: this.getDocumentTime(),\n });\n }, observableDOMParameters.pingIntervalMilliseconds || 5000);\n\n this.domRunner = runnerFactory(\n observableDOMParameters.htmlPath,\n observableDOMParameters.htmlContents,\n observableDOMParameters.params,\n (domRunnerMessage: DOMRunnerMessage) => {\n if (domRunnerMessage.loaded) {\n this.createVirtualDomElementWithChildren(\n this.domRunner.getDocument() as unknown as Element,\n null,\n );\n\n const snapshot = virtualDomElementToStatic(\n this.getVirtualDomElementForRealElementOrThrow(\n this.domRunner.getDocument() as unknown as Element,\n ),\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\n public addIPCWebsocket(webSocket: WebSocket) {\n return this.domRunner.addIPCWebsocket(webSocket);\n }\n\n public addConnectedUserId(connectionId: number): void {\n this.domRunner.getWindow().dispatchEvent(\n new (this.domRunner.getWindow().CustomEvent)(\"connected\", {\n detail: { connectionId },\n }),\n );\n }\n\n public removeConnectedUserId(connectionId: number): void {\n this.domRunner.getWindow().dispatchEvent(\n new (this.domRunner.getWindow().CustomEvent)(\"disconnected\", {\n detail: { connectionId },\n }),\n );\n }\n\n private processModificationList(mutationList: Array<MutationRecord>): void {\n const documentEl = this.domRunner.getDocument() as unknown as Element;\n const documentVirtualDomElement = this.realElementToVirtualElement.get(documentEl);\n if (!documentVirtualDomElement) {\n throw new Error(`document not created in processModificationList`);\n }\n\n if (mutationList.length > 1) {\n // TODO - walk back through the records to derive the intermediate states (e.g. if an attribute is later added to\n // an element created in an earlier record then it should not have that attribute when the element is added.\n // This is important as incorrect attribute sets can affect visibility and expected client performance.\n console.error(\n \"More than one mutation record received. It is possible that intermediate states are incorrect.\",\n );\n }\n\n for (const mutation of mutationList) {\n if (this.isIgnoredElement(mutation.target as Element | Text)) {\n continue;\n }\n\n if (\n mutation.type === \"attributes\" &&\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.isIgnoredAttribute(mutation.target as Element | Text, mutation.attributeName!)\n ) {\n continue;\n }\n\n this.addKnownNodesInMutation(mutation);\n\n // Convert the \"real\" DOM MutationRecord into a \"virtual\" DOM MutationRecord that references the VirtualDOMElements\n // This is done so that the same process for handling mutations can be used for both changes to a live DOM and also\n // to diffs between DOM snapshots when reloading\n const firstNonIgnoredPreviousSibling = mutation.previousSibling\n ? this.getFirstNonIgnoredPreviousSibling(mutation.previousSibling as Element | Text)\n : null;\n const targetElement = this.getVirtualDomElementForRealElementOrThrow(\n mutation.target as Element | Text,\n );\n const addedNodes: Array<StaticVirtualDomElement> = [];\n for (const node of mutation.addedNodes) {\n if (this.isIgnoredElement(node as Element | Text)) {\n continue;\n }\n const virtualDomElement = this.getVirtualDomElementForRealElementOrThrow(\n node as Element | Text,\n );\n addedNodes.push(virtualDomElementToStatic(virtualDomElement));\n }\n\n const removedNodeIds: Array<number> = [];\n for (const node of mutation.removedNodes) {\n if (this.isIgnoredElement(node as Element | Text)) {\n continue;\n }\n const virtualDomElement = this.getVirtualDomElementForRealElementOrThrow(\n node as Element | Text,\n );\n removedNodeIds.push(virtualDomElement.nodeId);\n }\n\n const mutationRecord: StaticVirtualDomMutationIdsRecord = {\n type: mutation.type,\n targetId: targetElement.nodeId,\n addedNodes,\n removedNodeIds,\n previousSiblingId:\n firstNonIgnoredPreviousSibling !== null\n ? this.getVirtualDomElementForRealElementOrThrow(firstNonIgnoredPreviousSibling).nodeId\n : null,\n attribute: mutation.attributeName\n ? {\n attributeName: mutation.attributeName,\n value: (mutation.target as Element).getAttribute(mutation.attributeName),\n }\n : null,\n };\n\n this.callback({\n mutation: mutationRecord,\n documentTime: this.getDocumentTime(),\n });\n\n this.removeKnownNodesInMutation(mutation);\n }\n }\n\n private addKnownNodesInMutation(mutation: MutationRecord): void {\n const targetNode = mutation.target as Element | Text;\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 as Element | Text)) {\n previousSibling = previousSibling.previousSibling;\n }\n if (previousSibling) {\n const previousSiblingElement = this.realElementToVirtualElement.get(\n previousSibling as Element | Text,\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: Node) => {\n const asElementOrText = node as Element | Text;\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 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const attributeName = mutation.attributeName!;\n if (this.isIgnoredAttribute(targetNode, attributeName)) {\n return;\n }\n const attributeValue = (targetNode as Element).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 : undefined;\n }\n }\n\n private removeKnownNodesInMutation(mutation: MutationRecord): void {\n const targetNode = mutation.target as Element | Text;\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 as Element | Text;\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\n private removeVirtualDomElement(virtualDomElement: LiveVirtualDomElement): void {\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\n private createVirtualDomElementWithChildren(\n node: Element | Text,\n parent: LiveVirtualDomElement | null,\n ): LiveVirtualDomElement | null {\n const virtualElement = this.createVirtualDomElement(node, parent);\n if (!virtualElement) {\n return null;\n }\n if ((node as Element).childNodes) {\n for (let i = 0; i < (node as Element).childNodes.length; i++) {\n const child = (node as Element).childNodes[i];\n const childVirtualElement = this.createVirtualDomElementWithChildren(\n child as Element | Text,\n virtualElement,\n );\n if (childVirtualElement) {\n virtualElement.childNodes.push(childVirtualElement);\n }\n }\n }\n\n return virtualElement;\n }\n\n private createVirtualDomElement(\n node: Element | Text,\n parent: LiveVirtualDomElement | null,\n ): LiveVirtualDomElement | null {\n if (this.isIgnoredElement(node)) {\n return null;\n }\n const existingValue = this.realElementToVirtualElement.get(node);\n if (existingValue !== undefined) {\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\n const attributes: { [key: string]: string } = {};\n if ((node as any).attributes) {\n const asHTMLElement = node as HTMLElement;\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\n const nodeId = this.nextNodeId++;\n const virtualElement: LiveVirtualDomElement = {\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\n private getFirstNonIgnoredPreviousSibling(node: Element | Text): Element | Text | null {\n let currentNode = node;\n if (!this.isIgnoredElement(currentNode)) {\n return currentNode;\n }\n while (currentNode && currentNode.previousSibling) {\n currentNode = currentNode.previousSibling as Element | Text;\n if (!this.isIgnoredElement(currentNode)) {\n return currentNode;\n }\n }\n return null;\n }\n\n private getVirtualDomElementForRealElementOrThrow(\n realElement: Element | Text,\n ): LiveVirtualDomElement {\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\n private isIgnoredElement(node: Element | Text): boolean {\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\n private isIgnoredAttribute(node: Element | Text, attributeName: string): boolean {\n return attributeName.startsWith(\"on\");\n }\n\n public dispatchRemoteEventFromConnectionId(connectionId: number, remoteEvent: RemoteEvent): void {\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\n if (domNode instanceof this.domRunner.getWindow().Text) {\n console.warn(\"Cannot dispatch remote event to text node\");\n return;\n }\n\n this.domRunner.dispatchRemoteEventFromConnectionId(\n connectionId,\n domNode.realElement as Element,\n remoteEvent,\n );\n }\n\n public dispose() {\n clearInterval(this.documentTimeIntervalTimer);\n this.domRunner.dispose();\n }\n\n private getDocumentTime() {\n return this.domRunner.getDocumentTime();\n }\n}\n", "import vm from \"vm\";\n\nimport { LogMessage, RemoteEvent } from \"@mml-io/observable-dom-common\";\nimport { AbortablePromise, DOMWindow, JSDOM, ResourceLoader, VirtualConsole } from \"jsdom\";\n\nimport { DOMRunnerFactory, DOMRunnerInterface, DOMRunnerMessage } from \"./ObservableDom\";\n\n// TODO - remove this monkeypatching if it's possible to avoid the race conditions in naive MutationObserver usage\nconst monkeyPatchedMutationRecordCallbacks = new Set<() => void>();\nfunction installMutationObserverMonkeyPatch() {\n /*\n This monkey patch replaces the `createImpl` exported function implementation in the `MutationRecord` class in JSDOM\n to insert an iteration through callbacks that are therefore fired before a subsequent MutationRecord is created.\n This provides an opportunity to invoke the MutationObservers with a single MutationRecord rather than multiple.\n\n This is necessary as (at least intuitive) usage of the MutationObserver API does not enable creating accurate\n incremental diffs as the handling of all-but-the-last MutationRecord in a list requires collecting state from the\n DOM that has been since been mutated further. (e.g. if an attribute is changed twice in a row the first event cannot\n discover the intermediate value of the attribute as it can only query the latest DOM state). Whilst this simple case\n is solvable by walking backwards through the list of MutationRecords and using `oldValue` there are cases where adding\n child elements with the correct attributes is not possible when handling intermediate diffs.\n */\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const MutationRecordExports = require(\"jsdom/lib/jsdom/living/generated/MutationRecord\");\n const originalCreateImpl = MutationRecordExports.createImpl;\n // This overwrites the function property on the exports that mutation-observers.js uses to create MutationRecords.\n MutationRecordExports.createImpl = (...args: any[]) => {\n for (const callback of monkeyPatchedMutationRecordCallbacks) {\n callback();\n }\n return originalCreateImpl.call(null, ...args);\n };\n}\nlet monkeyPatchInstalled = false;\n\nexport const JSDOMRunnerFactory: DOMRunnerFactory = (\n htmlPath: string,\n htmlContents: string,\n params: object,\n callback: (mutationList: DOMRunnerMessage) => void,\n): DOMRunnerInterface => {\n return new JSDOMRunner(htmlPath, htmlContents, params, callback);\n};\n\n// This is used to stop JSDOM trying to load resources\nclass RejectionResourceLoader extends ResourceLoader {\n public fetch(url: string): AbortablePromise<Buffer> | null {\n console.error(\"RejectionResourceLoader.fetch\", url);\n return null;\n }\n}\n\nexport class JSDOMRunner {\n private monkeyPatchMutationRecordCallback: () => void;\n\n private ipcWebsockets = new Set<WebSocket>();\n\n public domWindow: DOMWindow;\n private jsDom: JSDOM;\n\n private callback: (message: DOMRunnerMessage) => void;\n private mutationObserver: MutationObserver;\n private htmlPath: string;\n private ipcListeners = new Set<(event: any) => void>();\n\n private documentStartTime = Date.now();\n\n private isLoaded = false;\n private logBuffer: LogMessage[] = [];\n\n constructor(\n htmlPath: string,\n htmlContents: string,\n params: object,\n callback: (domRunnerMessage: DOMRunnerMessage) => void,\n ) {\n this.htmlPath = htmlPath;\n this.callback = callback;\n\n if (!monkeyPatchInstalled) {\n installMutationObserverMonkeyPatch();\n monkeyPatchInstalled = true;\n }\n\n this.monkeyPatchMutationRecordCallback = () => {\n /*\n This is called before every creation of a MutationRecord so that it can be used to process an existing record to\n avoid handling multiple MutationRecords at a time (see comment at the top of this file).\n */\n const records = this.mutationObserver.takeRecords();\n if (records.length > 1) {\n throw new Error(\n \"The monkey patching should have prevented more than one record being handled at a time\",\n );\n } else if (records.length > 0) {\n this.callback({\n mutationList: records,\n });\n }\n };\n monkeyPatchedMutationRecordCallbacks.add(this.monkeyPatchMutationRecordCallback);\n\n this.jsDom = new JSDOM(htmlContents, {\n runScripts: \"dangerously\",\n resources: new RejectionResourceLoader(),\n url: this.htmlPath,\n virtualConsole: this.createVirtualConsole(),\n beforeParse: (window) => {\n this.domWindow = window;\n\n this.domWindow.fetch = fetch;\n this.domWindow.Headers = Headers;\n this.domWindow.Request = Request;\n this.domWindow.Response = Response;\n\n // This is a polyfill for https://developer.mozilla.org/en-US/docs/Web/API/Document/timeline\n const timeline = {};\n Object.defineProperty(timeline, \"currentTime\", {\n get: () => {\n return this.getDocumentTime();\n },\n });\n (window.document as any).timeline = timeline;\n\n // JSON stringify and parse to avoid potential reference leaks from the params object\n window.params = JSON.parse(JSON.stringify(params));\n\n const oldDocumentAddEventListener = window.document.addEventListener;\n window.document.addEventListener = (...args: Array<any>) => {\n const [eventName, listener] = args;\n if (eventName === \"ipc\") {\n this.ipcListeners.add(listener);\n }\n return oldDocumentAddEventListener.call(window.document, ...args);\n };\n\n const oldDocumentRemoveEventListener = window.document.addEventListener;\n window.document.removeEventListener = (...args: Array<any>) => {\n const [eventName, listener] = args;\n if (eventName === \"ipc\") {\n this.ipcListeners.delete(listener);\n }\n return oldDocumentRemoveEventListener.call(window.document, ...args);\n };\n\n this.mutationObserver = new window.MutationObserver((mutationList) => {\n this.callback({\n mutationList,\n });\n });\n\n window.addEventListener(\"load\", () => {\n this.mutationObserver.observe(window.document, {\n attributes: true,\n childList: true,\n subtree: true,\n characterData: true,\n });\n\n this.isLoaded = true;\n\n this.callback({\n loaded: true,\n });\n\n this.flushLogBuffer();\n });\n },\n });\n }\n\n private flushLogBuffer() {\n for (const logMessage of this.logBuffer) {\n this.callback({\n logMessage,\n });\n }\n\n this.logBuffer = [];\n }\n\n private log(message: LogMessage) {\n if (!this.isLoaded) {\n this.logBuffer.push(message);\n return;\n }\n\n this.callback({\n logMessage: message,\n });\n }\n\n public getDocument(): Document {\n return this.domWindow.document;\n }\n\n public getWindow(): any {\n return this.domWindow;\n }\n\n public addIPCWebsocket(webSocket: WebSocket) {\n if (this.ipcListeners.size === 0) {\n console.error(\"ipc requested, but no ipc listeners registered on document:\", this.htmlPath);\n webSocket.close();\n return;\n }\n this.ipcWebsockets.add(webSocket);\n const event = new this.domWindow.CustomEvent(\"ipc\", {\n detail: {\n webSocket,\n },\n });\n for (const ipcListener of this.ipcListeners) {\n ipcListener(event);\n }\n webSocket.addEventListener(\"close\", () => {\n this.ipcWebsockets.delete(webSocket);\n });\n return;\n }\n\n public dispose() {\n const records = this.mutationObserver.takeRecords();\n this.callback({\n mutationList: records,\n });\n monkeyPatchedMutationRecordCallbacks.delete(this.monkeyPatchMutationRecordCallback);\n this.mutationObserver.disconnect();\n this.jsDom.window.close();\n }\n\n public getDocumentTime() {\n return Date.now() - this.documentStartTime;\n }\n\n public dispatchRemoteEventFromConnectionId(\n connectionId: number,\n domNode: Element,\n remoteEvent: RemoteEvent,\n ) {\n const bubbles = remoteEvent.bubbles || false;\n const remoteEventObject = new this.domWindow.CustomEvent(remoteEvent.name, {\n bubbles,\n detail: { ...remoteEvent.params, connectionId },\n });\n\n const eventTypeLowerCase = remoteEvent.name.toLowerCase();\n\n // TODO - check if there are other events that automatically wire up similarly to click->onclick and avoid those too\n if (eventTypeLowerCase !== \"click\") {\n const handlerAttributeName = \"on\" + eventTypeLowerCase;\n const handlerAttributeValue = domNode.getAttribute(handlerAttributeName);\n if (handlerAttributeValue) {\n // This event is defined as an HTML event attribute.\n const script = handlerAttributeValue;\n const vmContext = this.jsDom.getInternalVMContext();\n try {\n const invoke = vm.runInContext(`(function(event){ ${script} })`, vmContext);\n Reflect.apply(invoke, domNode, [remoteEventObject]);\n } catch (e) {\n console.error(\"Error running event handler:\", e);\n }\n }\n }\n\n // Dispatch the event via JavaScript.\n domNode.dispatchEvent(remoteEventObject);\n }\n\n private createVirtualConsole(): VirtualConsole {\n const virtualConsole = new VirtualConsole();\n virtualConsole.on(\"jsdomError\", (...args) => {\n this.log({\n level: \"system\",\n content: args,\n });\n });\n virtualConsole.on(\"error\", (...args) => {\n this.log({\n level: \"error\",\n content: args,\n });\n });\n virtualConsole.on(\"warn\", (...args) => {\n this.log({\n level: \"warn\",\n content: args,\n });\n });\n virtualConsole.on(\"log\", (...args) => {\n this.log({\n level: \"log\",\n content: args,\n });\n });\n virtualConsole.on(\"info\", (...args) => {\n this.log({\n level: \"info\",\n content: args,\n });\n });\n return virtualConsole;\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["vm"]
4
+ "sourcesContent": ["export * from \"./ObservableDom\";\nexport * from \"./JSDOMRunner\";\n", "import { StaticVirtualDomElement } from \"@mml-io/observable-dom-common\";\n\nimport { LiveVirtualDomElement } from \"./ObservableDom\";\n\nexport function virtualDomElementToStatic(el: LiveVirtualDomElement): StaticVirtualDomElement {\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", "import {\n LogMessage,\n ObservableDomInterface,\n ObservableDomMessage,\n ObservableDOMParameters,\n RemoteEvent,\n StaticVirtualDomElement,\n StaticVirtualDomMutationIdsRecord,\n} from \"@mml-io/observable-dom-common\";\n\nimport { virtualDomElementToStatic } from \"./utils\";\n\nexport type DOMRunnerMessage = {\n loaded?: boolean;\n mutationList?: Array<MutationRecord>;\n logMessage?: LogMessage;\n};\n\nexport type DOMRunnerInterface = {\n getDocument(): Document;\n getWindow(): Window & {\n CustomEvent: typeof CustomEvent;\n Text: typeof Text;\n HTMLScriptElement: typeof HTMLScriptElement;\n Comment: typeof Comment;\n }; // TODO - Define this without using JSDOM types\n addIPCWebsocket(webSocket: WebSocket): void;\n dispatchRemoteEventFromConnectionId(\n connectionId: number,\n realElement: Element,\n remoteEvent: RemoteEvent,\n ): void;\n dispose(): void;\n getDocumentTime(): number;\n};\n\nexport type DOMRunnerFactory = (\n htmlPath: string,\n htmlContents: string,\n params: object,\n callback: (domRunnerMessage: DOMRunnerMessage) => void,\n) => DOMRunnerInterface;\n\nexport type LiveVirtualDomElement = Omit<StaticVirtualDomElement, \"childNodes\"> & {\n realElement: Element | Text;\n childNodes: Array<LiveVirtualDomElement>;\n parent: LiveVirtualDomElement | null;\n};\n\nexport class ObservableDom implements ObservableDomInterface {\n private nodeToNodeId = new Map<LiveVirtualDomElement, number>();\n private nodeIdToNode = new Map<number, LiveVirtualDomElement>();\n private realElementToVirtualElement = new Map<Element | Text, LiveVirtualDomElement>();\n private ignoreTextNodes = true;\n private callback: (message: ObservableDomMessage) => void;\n private nextNodeId = 1;\n private htmlPath: string;\n private domRunner: DOMRunnerInterface;\n\n private documentTimeIntervalTimer: NodeJS.Timer;\n\n constructor(\n observableDOMParameters: ObservableDOMParameters,\n callback: (message: ObservableDomMessage) => void,\n runnerFactory: DOMRunnerFactory,\n ) {\n this.htmlPath = observableDOMParameters.htmlPath;\n this.ignoreTextNodes = observableDOMParameters.ignoreTextNodes;\n this.callback = callback;\n\n this.documentTimeIntervalTimer = setInterval(() => {\n this.callback({\n documentTime: this.getDocumentTime(),\n });\n }, observableDOMParameters.pingIntervalMilliseconds || 5000);\n\n this.domRunner = runnerFactory(\n observableDOMParameters.htmlPath,\n observableDOMParameters.htmlContents,\n observableDOMParameters.params,\n (domRunnerMessage: DOMRunnerMessage) => {\n if (domRunnerMessage.loaded) {\n this.createVirtualDomElementWithChildren(\n this.domRunner.getDocument() as unknown as Element,\n null,\n );\n\n const snapshot = virtualDomElementToStatic(\n this.getVirtualDomElementForRealElementOrThrow(\n this.domRunner.getDocument() as unknown as Element,\n ),\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\n public addIPCWebsocket(webSocket: WebSocket) {\n return this.domRunner.addIPCWebsocket(webSocket);\n }\n\n public addConnectedUserId(connectionId: number): void {\n this.domRunner.getWindow().dispatchEvent(\n new (this.domRunner.getWindow().CustomEvent)(\"connected\", {\n detail: { connectionId },\n }),\n );\n }\n\n public removeConnectedUserId(connectionId: number): void {\n this.domRunner.getWindow().dispatchEvent(\n new (this.domRunner.getWindow().CustomEvent)(\"disconnected\", {\n detail: { connectionId },\n }),\n );\n }\n\n private processModificationList(mutationList: Array<MutationRecord>): void {\n const documentEl = this.domRunner.getDocument() as unknown as Element;\n const documentVirtualDomElement = this.realElementToVirtualElement.get(documentEl);\n if (!documentVirtualDomElement) {\n throw new Error(`document not created in processModificationList`);\n }\n\n if (mutationList.length > 1) {\n // TODO - walk back through the records to derive the intermediate states (e.g. if an attribute is later added to\n // an element created in an earlier record then it should not have that attribute when the element is added.\n // This is important as incorrect attribute sets can affect visibility and expected client performance.\n console.error(\n \"More than one mutation record received. It is possible that intermediate states are incorrect.\",\n );\n }\n\n for (const mutation of mutationList) {\n if (this.isIgnoredElement(mutation.target as Element | Text)) {\n continue;\n }\n\n if (\n mutation.type === \"attributes\" &&\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.isIgnoredAttribute(mutation.target as Element | Text, mutation.attributeName!)\n ) {\n continue;\n }\n\n this.addKnownNodesInMutation(mutation);\n\n // Convert the \"real\" DOM MutationRecord into a \"virtual\" DOM MutationRecord that references the VirtualDOMElements\n // This is done so that the same process for handling mutations can be used for both changes to a live DOM and also\n // to diffs between DOM snapshots when reloading\n const firstNonIgnoredPreviousSibling = mutation.previousSibling\n ? this.getFirstNonIgnoredPreviousSibling(mutation.previousSibling as Element | Text)\n : null;\n const targetElement = this.getVirtualDomElementForRealElementOrThrow(\n mutation.target as Element | Text,\n );\n const addedNodes: Array<StaticVirtualDomElement> = [];\n for (const node of mutation.addedNodes) {\n if (this.isIgnoredElement(node as Element | Text)) {\n continue;\n }\n const virtualDomElement = this.getVirtualDomElementForRealElementOrThrow(\n node as Element | Text,\n );\n addedNodes.push(virtualDomElementToStatic(virtualDomElement));\n }\n\n const removedNodeIds: Array<number> = [];\n for (const node of mutation.removedNodes) {\n if (this.isIgnoredElement(node as Element | Text)) {\n continue;\n }\n const virtualDomElement = this.getVirtualDomElementForRealElementOrThrow(\n node as Element | Text,\n );\n removedNodeIds.push(virtualDomElement.nodeId);\n }\n\n const mutationRecord: StaticVirtualDomMutationIdsRecord = {\n type: mutation.type,\n targetId: targetElement.nodeId,\n addedNodes,\n removedNodeIds,\n previousSiblingId:\n firstNonIgnoredPreviousSibling !== null\n ? this.getVirtualDomElementForRealElementOrThrow(firstNonIgnoredPreviousSibling).nodeId\n : null,\n attribute: mutation.attributeName\n ? {\n attributeName: mutation.attributeName,\n value: (mutation.target as Element).getAttribute(mutation.attributeName),\n }\n : null,\n };\n\n this.callback({\n mutation: mutationRecord,\n documentTime: this.getDocumentTime(),\n });\n\n this.removeKnownNodesInMutation(mutation);\n }\n }\n\n private addKnownNodesInMutation(mutation: MutationRecord): void {\n const targetNode = mutation.target as Element | Text;\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 as Element | Text)) {\n previousSibling = previousSibling.previousSibling;\n }\n if (previousSibling) {\n const previousSiblingElement = this.realElementToVirtualElement.get(\n previousSibling as Element | Text,\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: Node) => {\n const asElementOrText = node as Element | Text;\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 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const attributeName = mutation.attributeName!;\n if (this.isIgnoredAttribute(targetNode, attributeName)) {\n return;\n }\n const attributeValue = (targetNode as Element).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 : undefined;\n }\n }\n\n private removeKnownNodesInMutation(mutation: MutationRecord): void {\n const targetNode = mutation.target as Element | Text;\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 as Element | Text;\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\n private removeVirtualDomElement(virtualDomElement: LiveVirtualDomElement): void {\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\n private createVirtualDomElementWithChildren(\n node: Element | Text,\n parent: LiveVirtualDomElement | null,\n ): LiveVirtualDomElement | null {\n const virtualElement = this.createVirtualDomElement(node, parent);\n if (!virtualElement) {\n return null;\n }\n if ((node as Element).childNodes) {\n for (let i = 0; i < (node as Element).childNodes.length; i++) {\n const child = (node as Element).childNodes[i];\n const childVirtualElement = this.createVirtualDomElementWithChildren(\n child as Element | Text,\n virtualElement,\n );\n if (childVirtualElement) {\n virtualElement.childNodes.push(childVirtualElement);\n }\n }\n }\n\n return virtualElement;\n }\n\n private createVirtualDomElement(\n node: Element | Text,\n parent: LiveVirtualDomElement | null,\n ): LiveVirtualDomElement | null {\n if (this.isIgnoredElement(node)) {\n return null;\n }\n const existingValue = this.realElementToVirtualElement.get(node);\n if (existingValue !== undefined) {\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\n const attributes: { [key: string]: string } = {};\n if ((node as any).attributes) {\n const asHTMLElement = node as HTMLElement;\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\n const nodeId = this.nextNodeId++;\n const virtualElement: LiveVirtualDomElement = {\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\n private getFirstNonIgnoredPreviousSibling(node: Element | Text): Element | Text | null {\n let currentNode = node;\n if (!this.isIgnoredElement(currentNode)) {\n return currentNode;\n }\n while (currentNode && currentNode.previousSibling) {\n currentNode = currentNode.previousSibling as Element | Text;\n if (!this.isIgnoredElement(currentNode)) {\n return currentNode;\n }\n }\n return null;\n }\n\n private getVirtualDomElementForRealElementOrThrow(\n realElement: Element | Text,\n ): LiveVirtualDomElement {\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\n private isIgnoredElement(node: Element | Text): boolean {\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\n private isIgnoredAttribute(node: Element | Text, attributeName: string): boolean {\n return attributeName.startsWith(\"on\");\n }\n\n public dispatchRemoteEventFromConnectionId(connectionId: number, remoteEvent: RemoteEvent): void {\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\n if (domNode instanceof this.domRunner.getWindow().Text) {\n console.warn(\"Cannot dispatch remote event to text node\");\n return;\n }\n\n this.domRunner.dispatchRemoteEventFromConnectionId(\n connectionId,\n domNode.realElement as Element,\n remoteEvent,\n );\n }\n\n public dispose() {\n clearInterval(this.documentTimeIntervalTimer);\n this.domRunner.dispose();\n }\n\n private getDocumentTime() {\n return this.domRunner.getDocumentTime();\n }\n}\n", "import vm from \"vm\";\n\nimport { LogMessage, RemoteEvent } from \"@mml-io/observable-dom-common\";\nimport { AbortablePromise, DOMWindow, JSDOM, ResourceLoader, VirtualConsole } from \"jsdom\";\nimport * as nodeFetch from \"node-fetch\";\nimport nodeFetchFn from \"node-fetch\";\n\nimport { DOMRunnerFactory, DOMRunnerInterface, DOMRunnerMessage } from \"./ObservableDom\";\n\nconst ErrDomWindowNotInitialized = \"DOMWindow not initialized\";\n\n// TODO - remove this monkeypatching if it's possible to avoid the race conditions in naive MutationObserver usage\nconst monkeyPatchedMutationRecordCallbacks = new Set<() => void>();\nfunction installMutationObserverMonkeyPatch() {\n /*\n This monkey patch replaces the `createImpl` exported function implementation in the `MutationRecord` class in JSDOM\n to insert an iteration through callbacks that are therefore fired before a subsequent MutationRecord is created.\n This provides an opportunity to invoke the MutationObservers with a single MutationRecord rather than multiple.\n\n This is necessary as (at least intuitive) usage of the MutationObserver API does not enable creating accurate\n incremental diffs as the handling of all-but-the-last MutationRecord in a list requires collecting state from the\n DOM that has been since been mutated further. (e.g. if an attribute is changed twice in a row the first event cannot\n discover the intermediate value of the attribute as it can only query the latest DOM state). Whilst this simple case\n is solvable by walking backwards through the list of MutationRecords and using `oldValue` there are cases where adding\n child elements with the correct attributes is not possible when handling intermediate diffs.\n */\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const MutationRecordExports = require(\"jsdom/lib/jsdom/living/generated/MutationRecord\");\n const originalCreateImpl = MutationRecordExports.createImpl;\n // This overwrites the function property on the exports that mutation-observers.js uses to create MutationRecords.\n MutationRecordExports.createImpl = (...args: any[]) => {\n for (const callback of monkeyPatchedMutationRecordCallbacks) {\n callback();\n }\n return originalCreateImpl.call(null, ...args);\n };\n}\nlet monkeyPatchInstalled = false;\n\nexport const JSDOMRunnerFactory: DOMRunnerFactory = (\n htmlPath: string,\n htmlContents: string,\n params: object,\n callback: (mutationList: DOMRunnerMessage) => void,\n): DOMRunnerInterface => {\n return new JSDOMRunner(htmlPath, htmlContents, params, callback);\n};\n\n// This is used to stop JSDOM trying to load resources\nclass RejectionResourceLoader extends ResourceLoader {\n public fetch(url: string): AbortablePromise<Buffer> | null {\n console.error(\"RejectionResourceLoader.fetch\", url);\n return null;\n }\n}\n\nexport class JSDOMRunner {\n private monkeyPatchMutationRecordCallback: () => void;\n\n private ipcWebsockets = new Set<WebSocket>();\n\n public domWindow: DOMWindow | null = null;\n private jsDom: JSDOM;\n\n private callback: (message: DOMRunnerMessage) => void;\n private mutationObserver: MutationObserver | null = null;\n private htmlPath: string;\n private ipcListeners = new Set<(event: any) => void>();\n\n private documentStartTime = Date.now();\n\n private isLoaded = false;\n private logBuffer: LogMessage[] = [];\n\n constructor(\n htmlPath: string,\n htmlContents: string,\n params: object,\n callback: (domRunnerMessage: DOMRunnerMessage) => void,\n ) {\n this.htmlPath = htmlPath;\n this.callback = callback;\n\n if (!monkeyPatchInstalled) {\n installMutationObserverMonkeyPatch();\n monkeyPatchInstalled = true;\n }\n\n this.monkeyPatchMutationRecordCallback = () => {\n /*\n This is called before every creation of a MutationRecord so that it can be used to process an existing record to\n avoid handling multiple MutationRecords at a time (see comment at the top of this file).\n */\n const records = this.mutationObserver?.takeRecords();\n if (records && records.length > 1) {\n throw new Error(\n \"The monkey patching should have prevented more than one record being handled at a time\",\n );\n } else if (records && records.length > 0) {\n this.callback({\n mutationList: records,\n });\n }\n };\n monkeyPatchedMutationRecordCallbacks.add(this.monkeyPatchMutationRecordCallback);\n\n this.jsDom = new JSDOM(htmlContents, {\n runScripts: \"dangerously\",\n resources: new RejectionResourceLoader(),\n url: this.htmlPath,\n virtualConsole: this.createVirtualConsole(),\n beforeParse: (window) => {\n this.domWindow = window;\n\n this.domWindow.fetch = nodeFetchFn as unknown as typeof fetch;\n this.domWindow.Headers = nodeFetch.Headers as unknown as typeof Headers;\n this.domWindow.Request = nodeFetch.Request as unknown as typeof Request;\n this.domWindow.Response = nodeFetch.Response as unknown as typeof Response;\n\n // This is a polyfill for https://developer.mozilla.org/en-US/docs/Web/API/Document/timeline\n const timeline = {};\n Object.defineProperty(timeline, \"currentTime\", {\n get: () => {\n return this.getDocumentTime();\n },\n });\n (window.document as any).timeline = timeline;\n\n // JSON stringify and parse to avoid potential reference leaks from the params object\n window.params = JSON.parse(JSON.stringify(params));\n\n const oldDocumentAddEventListener = window.document.addEventListener;\n window.document.addEventListener = (...args: [string, EventListener, ...any[]]) => {\n const [eventName, listener] = args;\n if (eventName === \"ipc\") {\n this.ipcListeners.add(listener);\n }\n return oldDocumentAddEventListener.call(window.document, ...args);\n };\n\n const oldDocumentRemoveEventListener = window.document.addEventListener;\n window.document.removeEventListener = (...args: [string, EventListener, ...any[]]) => {\n const [eventName, listener] = args;\n if (eventName === \"ipc\") {\n this.ipcListeners.delete(listener);\n }\n return oldDocumentRemoveEventListener.call(window.document, ...args);\n };\n\n this.mutationObserver = new window.MutationObserver((mutationList) => {\n this.callback({\n mutationList,\n });\n });\n\n window.addEventListener(\"load\", () => {\n this.mutationObserver?.observe(window.document, {\n attributes: true,\n childList: true,\n subtree: true,\n characterData: true,\n });\n\n this.isLoaded = true;\n\n this.callback({\n loaded: true,\n });\n\n this.flushLogBuffer();\n });\n },\n });\n }\n\n private flushLogBuffer() {\n for (const logMessage of this.logBuffer) {\n this.callback({\n logMessage,\n });\n }\n\n this.logBuffer = [];\n }\n\n private log(message: LogMessage) {\n if (!this.isLoaded) {\n this.logBuffer.push(message);\n return;\n }\n\n this.callback({\n logMessage: message,\n });\n }\n\n public getDocument(): Document {\n if (!this.domWindow) {\n throw new Error(ErrDomWindowNotInitialized);\n }\n\n return this.domWindow.document;\n }\n\n public getWindow(): any {\n return this.domWindow;\n }\n\n public addIPCWebsocket(webSocket: WebSocket) {\n if (!this.domWindow) {\n throw new Error(ErrDomWindowNotInitialized);\n }\n if (this.ipcListeners.size === 0) {\n console.error(\"ipc requested, but no ipc listeners registered on document:\", this.htmlPath);\n webSocket.close();\n return;\n }\n this.ipcWebsockets.add(webSocket);\n const event = new this.domWindow.CustomEvent(\"ipc\", {\n detail: {\n webSocket,\n },\n });\n for (const ipcListener of this.ipcListeners) {\n ipcListener(event);\n }\n webSocket.addEventListener(\"close\", () => {\n this.ipcWebsockets.delete(webSocket);\n });\n return;\n }\n\n public dispose() {\n const records = this.mutationObserver?.takeRecords();\n this.callback({\n mutationList: records,\n });\n monkeyPatchedMutationRecordCallbacks.delete(this.monkeyPatchMutationRecordCallback);\n this.mutationObserver?.disconnect();\n this.jsDom.window.close();\n }\n\n public getDocumentTime() {\n return Date.now() - this.documentStartTime;\n }\n\n public dispatchRemoteEventFromConnectionId(\n connectionId: number,\n domNode: Element,\n remoteEvent: RemoteEvent,\n ) {\n if (!this.domWindow) {\n throw new Error(ErrDomWindowNotInitialized);\n }\n\n const bubbles = remoteEvent.bubbles || false;\n const remoteEventObject = new this.domWindow.CustomEvent(remoteEvent.name, {\n bubbles,\n detail: { ...remoteEvent.params, connectionId },\n });\n\n const eventTypeLowerCase = remoteEvent.name.toLowerCase();\n\n // TODO - check if there are other events that automatically wire up similarly to click->onclick and avoid those too\n if (eventTypeLowerCase !== \"click\") {\n const handlerAttributeName = \"on\" + eventTypeLowerCase;\n const handlerAttributeValue = domNode.getAttribute(handlerAttributeName);\n if (handlerAttributeValue) {\n // This event is defined as an HTML event attribute.\n const script = handlerAttributeValue;\n const vmContext = this.jsDom.getInternalVMContext();\n try {\n const invoke = vm.runInContext(`(function(event){ ${script} })`, vmContext);\n Reflect.apply(invoke, domNode, [remoteEventObject]);\n } catch (e) {\n console.error(\"Error running event handler:\", e);\n }\n }\n }\n\n // Dispatch the event via JavaScript.\n domNode.dispatchEvent(remoteEventObject);\n }\n\n private createVirtualConsole(): VirtualConsole {\n const virtualConsole = new VirtualConsole();\n virtualConsole.on(\"jsdomError\", (...args) => {\n this.log({\n level: \"system\",\n content: args,\n });\n });\n virtualConsole.on(\"error\", (...args) => {\n this.log({\n level: \"error\",\n content: args,\n });\n });\n virtualConsole.on(\"warn\", (...args) => {\n this.log({\n level: \"warn\",\n content: args,\n });\n });\n virtualConsole.on(\"log\", (...args) => {\n this.log({\n level: \"log\",\n content: args,\n });\n });\n virtualConsole.on(\"info\", (...args) => {\n this.log({\n level: \"info\",\n content: args,\n });\n });\n return virtualConsole;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["nodeFetchFn", "vm"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mml-io/observable-dom",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "main": "./build/index.js",
5
5
  "types": "./build/index.d.ts",
6
6
  "files": [
@@ -16,8 +16,12 @@
16
16
  "lint-fix": "eslint \"./src/**/*.{js,jsx,ts,tsx}\" --fix"
17
17
  },
18
18
  "dependencies": {
19
- "@mml-io/observable-dom-common": "^0.1.0",
19
+ "@mml-io/observable-dom-common": "^0.1.3",
20
+ "jsdom": "22.1.0",
21
+ "node-fetch": "2.6.11"
22
+ },
23
+ "devDependencies": {
20
24
  "@types/jsdom": "21.1.1",
21
- "jsdom": "22.1.0"
25
+ "@types/node-fetch": "^2.6.4"
22
26
  }
23
27
  }
@@ -2,9 +2,13 @@ import vm from "vm";
2
2
 
3
3
  import { LogMessage, RemoteEvent } from "@mml-io/observable-dom-common";
4
4
  import { AbortablePromise, DOMWindow, JSDOM, ResourceLoader, VirtualConsole } from "jsdom";
5
+ import * as nodeFetch from "node-fetch";
6
+ import nodeFetchFn from "node-fetch";
5
7
 
6
8
  import { DOMRunnerFactory, DOMRunnerInterface, DOMRunnerMessage } from "./ObservableDom";
7
9
 
10
+ const ErrDomWindowNotInitialized = "DOMWindow not initialized";
11
+
8
12
  // TODO - remove this monkeypatching if it's possible to avoid the race conditions in naive MutationObserver usage
9
13
  const monkeyPatchedMutationRecordCallbacks = new Set<() => void>();
10
14
  function installMutationObserverMonkeyPatch() {
@@ -55,11 +59,11 @@ export class JSDOMRunner {
55
59
 
56
60
  private ipcWebsockets = new Set<WebSocket>();
57
61
 
58
- public domWindow: DOMWindow;
62
+ public domWindow: DOMWindow | null = null;
59
63
  private jsDom: JSDOM;
60
64
 
61
65
  private callback: (message: DOMRunnerMessage) => void;
62
- private mutationObserver: MutationObserver;
66
+ private mutationObserver: MutationObserver | null = null;
63
67
  private htmlPath: string;
64
68
  private ipcListeners = new Set<(event: any) => void>();
65
69
 
@@ -87,12 +91,12 @@ export class JSDOMRunner {
87
91
  This is called before every creation of a MutationRecord so that it can be used to process an existing record to
88
92
  avoid handling multiple MutationRecords at a time (see comment at the top of this file).
89
93
  */
90
- const records = this.mutationObserver.takeRecords();
91
- if (records.length > 1) {
94
+ const records = this.mutationObserver?.takeRecords();
95
+ if (records && records.length > 1) {
92
96
  throw new Error(
93
97
  "The monkey patching should have prevented more than one record being handled at a time",
94
98
  );
95
- } else if (records.length > 0) {
99
+ } else if (records && records.length > 0) {
96
100
  this.callback({
97
101
  mutationList: records,
98
102
  });
@@ -108,10 +112,10 @@ export class JSDOMRunner {
108
112
  beforeParse: (window) => {
109
113
  this.domWindow = window;
110
114
 
111
- this.domWindow.fetch = fetch;
112
- this.domWindow.Headers = Headers;
113
- this.domWindow.Request = Request;
114
- this.domWindow.Response = Response;
115
+ this.domWindow.fetch = nodeFetchFn as unknown as typeof fetch;
116
+ this.domWindow.Headers = nodeFetch.Headers as unknown as typeof Headers;
117
+ this.domWindow.Request = nodeFetch.Request as unknown as typeof Request;
118
+ this.domWindow.Response = nodeFetch.Response as unknown as typeof Response;
115
119
 
116
120
  // This is a polyfill for https://developer.mozilla.org/en-US/docs/Web/API/Document/timeline
117
121
  const timeline = {};
@@ -126,7 +130,7 @@ export class JSDOMRunner {
126
130
  window.params = JSON.parse(JSON.stringify(params));
127
131
 
128
132
  const oldDocumentAddEventListener = window.document.addEventListener;
129
- window.document.addEventListener = (...args: Array<any>) => {
133
+ window.document.addEventListener = (...args: [string, EventListener, ...any[]]) => {
130
134
  const [eventName, listener] = args;
131
135
  if (eventName === "ipc") {
132
136
  this.ipcListeners.add(listener);
@@ -135,7 +139,7 @@ export class JSDOMRunner {
135
139
  };
136
140
 
137
141
  const oldDocumentRemoveEventListener = window.document.addEventListener;
138
- window.document.removeEventListener = (...args: Array<any>) => {
142
+ window.document.removeEventListener = (...args: [string, EventListener, ...any[]]) => {
139
143
  const [eventName, listener] = args;
140
144
  if (eventName === "ipc") {
141
145
  this.ipcListeners.delete(listener);
@@ -150,7 +154,7 @@ export class JSDOMRunner {
150
154
  });
151
155
 
152
156
  window.addEventListener("load", () => {
153
- this.mutationObserver.observe(window.document, {
157
+ this.mutationObserver?.observe(window.document, {
154
158
  attributes: true,
155
159
  childList: true,
156
160
  subtree: true,
@@ -191,6 +195,10 @@ export class JSDOMRunner {
191
195
  }
192
196
 
193
197
  public getDocument(): Document {
198
+ if (!this.domWindow) {
199
+ throw new Error(ErrDomWindowNotInitialized);
200
+ }
201
+
194
202
  return this.domWindow.document;
195
203
  }
196
204
 
@@ -199,6 +207,9 @@ export class JSDOMRunner {
199
207
  }
200
208
 
201
209
  public addIPCWebsocket(webSocket: WebSocket) {
210
+ if (!this.domWindow) {
211
+ throw new Error(ErrDomWindowNotInitialized);
212
+ }
202
213
  if (this.ipcListeners.size === 0) {
203
214
  console.error("ipc requested, but no ipc listeners registered on document:", this.htmlPath);
204
215
  webSocket.close();
@@ -220,12 +231,12 @@ export class JSDOMRunner {
220
231
  }
221
232
 
222
233
  public dispose() {
223
- const records = this.mutationObserver.takeRecords();
234
+ const records = this.mutationObserver?.takeRecords();
224
235
  this.callback({
225
236
  mutationList: records,
226
237
  });
227
238
  monkeyPatchedMutationRecordCallbacks.delete(this.monkeyPatchMutationRecordCallback);
228
- this.mutationObserver.disconnect();
239
+ this.mutationObserver?.disconnect();
229
240
  this.jsDom.window.close();
230
241
  }
231
242
 
@@ -238,6 +249,10 @@ export class JSDOMRunner {
238
249
  domNode: Element,
239
250
  remoteEvent: RemoteEvent,
240
251
  ) {
252
+ if (!this.domWindow) {
253
+ throw new Error(ErrDomWindowNotInitialized);
254
+ }
255
+
241
256
  const bubbles = remoteEvent.bubbles || false;
242
257
  const remoteEventObject = new this.domWindow.CustomEvent(remoteEvent.name, {
243
258
  bubbles,