diff --git a/package-lock.json b/package-lock.json index 3c541db1e..d32938e0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2152,9 +2152,9 @@ } }, "node_modules/@lit/reactive-element": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.3.2.tgz", - "integrity": "sha512-A2e18XzPMrIh35nhIdE4uoqRzoIpEU5vZYuQN4S3Ee1zkGdYC27DP12pewbw/RLgPHzaE4kx/YqxMzebOpm0dA==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.4.1.tgz", + "integrity": "sha512-qDv4851VFSaBWzpS02cXHclo40jsbAjRXnebNXpm0uVg32kCneZPo9RYVQtrTNICtZ+1wAYHu1ZtxWSWMbKrBw==" }, "node_modules/@mrmlnc/readdir-enhanced": { "version": "2.2.1", @@ -2634,9 +2634,9 @@ "integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==" }, "node_modules/@types/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", "dependencies": { "@types/node": "*", "form-data": "^3.0.0" @@ -2873,36 +2873,36 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.37.tgz", - "integrity": "sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.40.tgz", + "integrity": "sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==", "dependencies": { "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.37", + "@vue/shared": "3.2.40", "estree-walker": "^2.0.2", "source-map": "^0.6.1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz", - "integrity": "sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.40.tgz", + "integrity": "sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==", "dependencies": { - "@vue/compiler-core": "3.2.37", - "@vue/shared": "3.2.37" + "@vue/compiler-core": "3.2.40", + "@vue/shared": "3.2.40" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz", - "integrity": "sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.40.tgz", + "integrity": "sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==", "dependencies": { "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.37", - "@vue/compiler-dom": "3.2.37", - "@vue/compiler-ssr": "3.2.37", - "@vue/reactivity-transform": "3.2.37", - "@vue/shared": "3.2.37", + "@vue/compiler-core": "3.2.40", + "@vue/compiler-dom": "3.2.40", + "@vue/compiler-ssr": "3.2.40", + "@vue/reactivity-transform": "3.2.40", + "@vue/shared": "3.2.40", "estree-walker": "^2.0.2", "magic-string": "^0.25.7", "postcss": "^8.1.10", @@ -2910,69 +2910,69 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz", - "integrity": "sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.40.tgz", + "integrity": "sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==", "dependencies": { - "@vue/compiler-dom": "3.2.37", - "@vue/shared": "3.2.37" + "@vue/compiler-dom": "3.2.40", + "@vue/shared": "3.2.40" } }, "node_modules/@vue/reactivity": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz", - "integrity": "sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.40.tgz", + "integrity": "sha512-N9qgGLlZmtUBMHF9xDT4EkD9RdXde1Xbveb+niWMXuHVWQP5BzgRmE3SFyUBBcyayG4y1lhoz+lphGRRxxK4RA==", "dependencies": { - "@vue/shared": "3.2.37" + "@vue/shared": "3.2.40" } }, "node_modules/@vue/reactivity-transform": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz", - "integrity": "sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.40.tgz", + "integrity": "sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==", "dependencies": { "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.37", - "@vue/shared": "3.2.37", + "@vue/compiler-core": "3.2.40", + "@vue/shared": "3.2.40", "estree-walker": "^2.0.2", "magic-string": "^0.25.7" } }, "node_modules/@vue/runtime-core": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.37.tgz", - "integrity": "sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.40.tgz", + "integrity": "sha512-U1+rWf0H8xK8aBUZhnrN97yoZfHbjgw/bGUzfgKPJl69/mXDuSg8CbdBYBn6VVQdR947vWneQBFzdhasyzMUKg==", "dependencies": { - "@vue/reactivity": "3.2.37", - "@vue/shared": "3.2.37" + "@vue/reactivity": "3.2.40", + "@vue/shared": "3.2.40" } }, "node_modules/@vue/runtime-dom": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz", - "integrity": "sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.40.tgz", + "integrity": "sha512-AO2HMQ+0s2+MCec8hXAhxMgWhFhOPJ/CyRXnmTJ6XIOnJFLrH5Iq3TNwvVcODGR295jy77I6dWPj+wvFoSYaww==", "dependencies": { - "@vue/runtime-core": "3.2.37", - "@vue/shared": "3.2.37", + "@vue/runtime-core": "3.2.40", + "@vue/shared": "3.2.40", "csstype": "^2.6.8" } }, "node_modules/@vue/server-renderer": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.37.tgz", - "integrity": "sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.40.tgz", + "integrity": "sha512-gtUcpRwrXOJPJ4qyBpU3EyxQa4EkV8I4f8VrDePcGCPe4O/hd0BPS7v9OgjIQob6Ap8VDz9G+mGTKazE45/95w==", "dependencies": { - "@vue/compiler-ssr": "3.2.37", - "@vue/shared": "3.2.37" + "@vue/compiler-ssr": "3.2.40", + "@vue/shared": "3.2.40" }, "peerDependencies": { - "vue": "3.2.37" + "vue": "3.2.40" } }, "node_modules/@vue/shared": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.37.tgz", - "integrity": "sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw==" + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.40.tgz", + "integrity": "sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ==" }, "node_modules/abab": { "version": "2.0.5", @@ -4651,7 +4651,7 @@ "node_modules/cpy/node_modules/to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", "dependencies": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" @@ -4704,9 +4704,9 @@ "dev": true }, "node_modules/csstype": { - "version": "2.6.20", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", - "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" }, "node_modules/dargs": { "version": "7.0.0", @@ -8584,9 +8584,9 @@ } }, "node_modules/jquery": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", - "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", + "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==" }, "node_modules/js-tokens": { "version": "4.0.0", @@ -9041,28 +9041,28 @@ "dev": true }, "node_modules/lit": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/lit/-/lit-2.2.5.tgz", - "integrity": "sha512-Ln463c0xJZfzVxBcHddNvFQQ8Z22NK7KgNmrzwFF1iESHUud412RRExzepj18wpTbusgwoTnOYuoTpo9uyNBaQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-2.4.0.tgz", + "integrity": "sha512-fdgzxEtLrZFQU/BqTtxFQCLwlZd9bdat+ltzSFjvWkZrs7eBmeX0L5MHUMb3kYIkuS8Xlfnii/iI5klirF8/Xg==", "dependencies": { - "@lit/reactive-element": "^1.3.0", + "@lit/reactive-element": "^1.4.0", "lit-element": "^3.2.0", - "lit-html": "^2.2.0" + "lit-html": "^2.4.0" } }, "node_modules/lit-element": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.0.tgz", - "integrity": "sha512-HbE7yt2SnUtg5DCrWt028oaU4D5F4k/1cntAFHTkzY8ZIa8N0Wmu92PxSxucsQSOXlODFrICkQ5x/tEshKi13g==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.2.tgz", + "integrity": "sha512-6ZgxBR9KNroqKb6+htkyBwD90XGRiqKDHVrW/Eh0EZ+l+iC+u+v+w3/BA5NGi4nizAVHGYvQBHUDuSmLjPp7NQ==", "dependencies": { "@lit/reactive-element": "^1.3.0", "lit-html": "^2.2.0" } }, "node_modules/lit-html": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.2.5.tgz", - "integrity": "sha512-e56Y9V+RNA+SGYsWP2DGb/wad5Ccd3xUZYjmcmbeZcnc0wP4zFQRXeXn7W3bbfBekmHDK2dOnuYNYkg0bQjh/w==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.4.0.tgz", + "integrity": "sha512-G6qXu4JNUpY6aaF2VMfaszhO9hlWw0hOTRFDmuMheg/nDYGB+2RztUSOyrzALAbr8Nh0Y7qjhYkReh3rPnplVg==", "dependencies": { "@types/trusted-types": "^2.0.2" } @@ -11013,9 +11013,9 @@ } }, "node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "version": "8.4.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.17.tgz", + "integrity": "sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==", "funding": [ { "type": "opencollective", @@ -11090,9 +11090,9 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "node_modules/promise": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", - "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.2.0.tgz", + "integrity": "sha512-+CMAlLHqwRYwBMXKCP+o8ns7DN+xHDUiI+0nArsiJ9y+kJVPLFxEaSw6Ha9s9H0tftxg2Yzl25wqj9G7m5wLZg==", "dependencies": { "asap": "~2.0.6" } @@ -12192,7 +12192,7 @@ "node_modules/snapdragon/node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "engines": { "node": ">=0.10.0" } @@ -12437,7 +12437,7 @@ "node_modules/static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", "dependencies": { "define-property": "^0.2.5", "object-copy": "^0.1.0" @@ -12977,7 +12977,7 @@ "node_modules/to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", "dependencies": { "kind-of": "^3.0.2" }, @@ -13340,7 +13340,7 @@ "node_modules/unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", "dependencies": { "has-value": "^0.3.1", "isobject": "^3.0.0" @@ -13403,7 +13403,7 @@ "node_modules/urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", "deprecated": "Please see https://github.com/lydell/urix#deprecated" }, "node_modules/use": { @@ -13538,15 +13538,15 @@ "dev": true }, "node_modules/vue": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.37.tgz", - "integrity": "sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.40.tgz", + "integrity": "sha512-1mGHulzUbl2Nk3pfvI5aXYYyJUs1nm4kyvuz38u4xlQkLUn1i2R7nDbI4TufECmY8v1qNBHYy62bCaM+3cHP2A==", "dependencies": { - "@vue/compiler-dom": "3.2.37", - "@vue/compiler-sfc": "3.2.37", - "@vue/runtime-dom": "3.2.37", - "@vue/server-renderer": "3.2.37", - "@vue/shared": "3.2.37" + "@vue/compiler-dom": "3.2.40", + "@vue/compiler-sfc": "3.2.40", + "@vue/runtime-dom": "3.2.40", + "@vue/server-renderer": "3.2.40", + "@vue/shared": "3.2.40" } }, "node_modules/w3c-hr-time": { @@ -15664,9 +15664,9 @@ } }, "@lit/reactive-element": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.3.2.tgz", - "integrity": "sha512-A2e18XzPMrIh35nhIdE4uoqRzoIpEU5vZYuQN4S3Ee1zkGdYC27DP12pewbw/RLgPHzaE4kx/YqxMzebOpm0dA==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.4.1.tgz", + "integrity": "sha512-qDv4851VFSaBWzpS02cXHclo40jsbAjRXnebNXpm0uVg32kCneZPo9RYVQtrTNICtZ+1wAYHu1ZtxWSWMbKrBw==" }, "@mrmlnc/readdir-enhanced": { "version": "2.2.1", @@ -16104,9 +16104,9 @@ "integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==" }, "@types/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", "requires": { "@types/node": "*", "form-data": "^3.0.0" @@ -16254,36 +16254,36 @@ } }, "@vue/compiler-core": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.37.tgz", - "integrity": "sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.40.tgz", + "integrity": "sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==", "requires": { "@babel/parser": "^7.16.4", - "@vue/shared": "3.2.37", + "@vue/shared": "3.2.40", "estree-walker": "^2.0.2", "source-map": "^0.6.1" } }, "@vue/compiler-dom": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz", - "integrity": "sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.40.tgz", + "integrity": "sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==", "requires": { - "@vue/compiler-core": "3.2.37", - "@vue/shared": "3.2.37" + "@vue/compiler-core": "3.2.40", + "@vue/shared": "3.2.40" } }, "@vue/compiler-sfc": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz", - "integrity": "sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.40.tgz", + "integrity": "sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==", "requires": { "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.37", - "@vue/compiler-dom": "3.2.37", - "@vue/compiler-ssr": "3.2.37", - "@vue/reactivity-transform": "3.2.37", - "@vue/shared": "3.2.37", + "@vue/compiler-core": "3.2.40", + "@vue/compiler-dom": "3.2.40", + "@vue/compiler-ssr": "3.2.40", + "@vue/reactivity-transform": "3.2.40", + "@vue/shared": "3.2.40", "estree-walker": "^2.0.2", "magic-string": "^0.25.7", "postcss": "^8.1.10", @@ -16291,66 +16291,66 @@ } }, "@vue/compiler-ssr": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz", - "integrity": "sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.40.tgz", + "integrity": "sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==", "requires": { - "@vue/compiler-dom": "3.2.37", - "@vue/shared": "3.2.37" + "@vue/compiler-dom": "3.2.40", + "@vue/shared": "3.2.40" } }, "@vue/reactivity": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz", - "integrity": "sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.40.tgz", + "integrity": "sha512-N9qgGLlZmtUBMHF9xDT4EkD9RdXde1Xbveb+niWMXuHVWQP5BzgRmE3SFyUBBcyayG4y1lhoz+lphGRRxxK4RA==", "requires": { - "@vue/shared": "3.2.37" + "@vue/shared": "3.2.40" } }, "@vue/reactivity-transform": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz", - "integrity": "sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.40.tgz", + "integrity": "sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==", "requires": { "@babel/parser": "^7.16.4", - "@vue/compiler-core": "3.2.37", - "@vue/shared": "3.2.37", + "@vue/compiler-core": "3.2.40", + "@vue/shared": "3.2.40", "estree-walker": "^2.0.2", "magic-string": "^0.25.7" } }, "@vue/runtime-core": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.37.tgz", - "integrity": "sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.40.tgz", + "integrity": "sha512-U1+rWf0H8xK8aBUZhnrN97yoZfHbjgw/bGUzfgKPJl69/mXDuSg8CbdBYBn6VVQdR947vWneQBFzdhasyzMUKg==", "requires": { - "@vue/reactivity": "3.2.37", - "@vue/shared": "3.2.37" + "@vue/reactivity": "3.2.40", + "@vue/shared": "3.2.40" } }, "@vue/runtime-dom": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz", - "integrity": "sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.40.tgz", + "integrity": "sha512-AO2HMQ+0s2+MCec8hXAhxMgWhFhOPJ/CyRXnmTJ6XIOnJFLrH5Iq3TNwvVcODGR295jy77I6dWPj+wvFoSYaww==", "requires": { - "@vue/runtime-core": "3.2.37", - "@vue/shared": "3.2.37", + "@vue/runtime-core": "3.2.40", + "@vue/shared": "3.2.40", "csstype": "^2.6.8" } }, "@vue/server-renderer": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.37.tgz", - "integrity": "sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==", + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.40.tgz", + "integrity": "sha512-gtUcpRwrXOJPJ4qyBpU3EyxQa4EkV8I4f8VrDePcGCPe4O/hd0BPS7v9OgjIQob6Ap8VDz9G+mGTKazE45/95w==", "requires": { - "@vue/compiler-ssr": "3.2.37", - "@vue/shared": "3.2.37" + "@vue/compiler-ssr": "3.2.40", + "@vue/shared": "3.2.40" } }, "@vue/shared": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.37.tgz", - "integrity": "sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw==" + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.40.tgz", + "integrity": "sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ==" }, "abab": { "version": "2.0.5", @@ -17658,7 +17658,7 @@ "to-regex-range": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", "requires": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" @@ -17706,9 +17706,9 @@ } }, "csstype": { - "version": "2.6.20", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", - "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" }, "dargs": { "version": "7.0.0", @@ -20705,9 +20705,9 @@ } }, "jquery": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", - "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", + "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==" }, "js-tokens": { "version": "4.0.0", @@ -21077,28 +21077,28 @@ "dev": true }, "lit": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/lit/-/lit-2.2.5.tgz", - "integrity": "sha512-Ln463c0xJZfzVxBcHddNvFQQ8Z22NK7KgNmrzwFF1iESHUud412RRExzepj18wpTbusgwoTnOYuoTpo9uyNBaQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-2.4.0.tgz", + "integrity": "sha512-fdgzxEtLrZFQU/BqTtxFQCLwlZd9bdat+ltzSFjvWkZrs7eBmeX0L5MHUMb3kYIkuS8Xlfnii/iI5klirF8/Xg==", "requires": { - "@lit/reactive-element": "^1.3.0", + "@lit/reactive-element": "^1.4.0", "lit-element": "^3.2.0", - "lit-html": "^2.2.0" + "lit-html": "^2.4.0" } }, "lit-element": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.0.tgz", - "integrity": "sha512-HbE7yt2SnUtg5DCrWt028oaU4D5F4k/1cntAFHTkzY8ZIa8N0Wmu92PxSxucsQSOXlODFrICkQ5x/tEshKi13g==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.2.tgz", + "integrity": "sha512-6ZgxBR9KNroqKb6+htkyBwD90XGRiqKDHVrW/Eh0EZ+l+iC+u+v+w3/BA5NGi4nizAVHGYvQBHUDuSmLjPp7NQ==", "requires": { "@lit/reactive-element": "^1.3.0", "lit-html": "^2.2.0" } }, "lit-html": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.2.5.tgz", - "integrity": "sha512-e56Y9V+RNA+SGYsWP2DGb/wad5Ccd3xUZYjmcmbeZcnc0wP4zFQRXeXn7W3bbfBekmHDK2dOnuYNYkg0bQjh/w==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.4.0.tgz", + "integrity": "sha512-G6qXu4JNUpY6aaF2VMfaszhO9hlWw0hOTRFDmuMheg/nDYGB+2RztUSOyrzALAbr8Nh0Y7qjhYkReh3rPnplVg==", "requires": { "@types/trusted-types": "^2.0.2" } @@ -22610,9 +22610,9 @@ "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==" }, "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "version": "8.4.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.17.tgz", + "integrity": "sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==", "requires": { "nanoid": "^3.3.4", "picocolors": "^1.0.0", @@ -22656,9 +22656,9 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "promise": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", - "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.2.0.tgz", + "integrity": "sha512-+CMAlLHqwRYwBMXKCP+o8ns7DN+xHDUiI+0nArsiJ9y+kJVPLFxEaSw6Ha9s9H0tftxg2Yzl25wqj9G7m5wLZg==", "requires": { "asap": "~2.0.6" } @@ -23464,7 +23464,7 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" } } }, @@ -23700,7 +23700,7 @@ "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", "requires": { "define-property": "^0.2.5", "object-copy": "^0.1.0" @@ -24118,7 +24118,7 @@ "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", "requires": { "kind-of": "^3.0.2" }, @@ -24382,7 +24382,7 @@ "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", "requires": { "has-value": "^0.3.1", "isobject": "^3.0.0" @@ -24433,7 +24433,7 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==" }, "use": { "version": "3.1.1", @@ -24553,15 +24553,15 @@ "dev": true }, "vue": { - "version": "3.2.37", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.37.tgz", - "integrity": "sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ==", - "requires": { - "@vue/compiler-dom": "3.2.37", - "@vue/compiler-sfc": "3.2.37", - "@vue/runtime-dom": "3.2.37", - "@vue/server-renderer": "3.2.37", - "@vue/shared": "3.2.37" + "version": "3.2.40", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.40.tgz", + "integrity": "sha512-1mGHulzUbl2Nk3pfvI5aXYYyJUs1nm4kyvuz38u4xlQkLUn1i2R7nDbI4TufECmY8v1qNBHYy62bCaM+3cHP2A==", + "requires": { + "@vue/compiler-dom": "3.2.40", + "@vue/compiler-sfc": "3.2.40", + "@vue/runtime-dom": "3.2.40", + "@vue/server-renderer": "3.2.40", + "@vue/shared": "3.2.40" } }, "w3c-hr-time": { diff --git a/packages/happy-dom/package.json b/packages/happy-dom/package.json index 8c6c8e077..d69160d59 100644 --- a/packages/happy-dom/package.json +++ b/packages/happy-dom/package.json @@ -53,7 +53,8 @@ "whatwg-encoding": "^2.0.0", "whatwg-mimetype": "^3.0.0", "webidl-conversions": "^7.0.0", - "css.escape": "^1.5.1" + "css.escape": "^1.5.1", + "xmlhttprequest-ssl": "^1.6.3" }, "devDependencies": { "@types/he": "^1.1.2", diff --git a/packages/happy-dom/src/event/IEventListener.ts b/packages/happy-dom/src/event/IEventListener.ts index 0c9e5cbee..0423fec60 100644 --- a/packages/happy-dom/src/event/IEventListener.ts +++ b/packages/happy-dom/src/event/IEventListener.ts @@ -7,7 +7,7 @@ export default interface IEventListener { /** * Handles event. * - * @param event + * @param event Event. */ handleEvent(event: Event): void; } diff --git a/packages/happy-dom/src/fetch/FetchHandler.ts b/packages/happy-dom/src/fetch/FetchHandler.ts index f4da6a206..3b4e1e4c4 100644 --- a/packages/happy-dom/src/fetch/FetchHandler.ts +++ b/packages/happy-dom/src/fetch/FetchHandler.ts @@ -20,17 +20,19 @@ export default class FetchHandler { public static fetch(document: IDocument, url: string, init?: IRequestInit): Promise { // We want to only load NodeFetch when it is needed to improve performance and not have direct dependencies to server side packages. const taskManager = document.defaultView.happyDOM.asyncTaskManager; + const requestInit = { ...init, headers: { ...init?.headers } }; + // We need set referer to solve anti-hotlinking. // And the browser will set the referer to the origin of the page. - if (init) { - if (!init.headers['referer']) { - init.headers['referer'] = document.defaultView.location.origin; - } - } + requestInit.headers['referer'] = document.defaultView.location.origin; + + requestInit.headers['user-agent'] = document.defaultView.navigator.userAgent; + requestInit.headers['cookie'] = document.defaultView.document.cookie; + return new Promise((resolve, reject) => { const taskID = taskManager.startTask(); - NodeFetch(RelativeURL.getAbsoluteURL(document.defaultView.location, url).href, init) + NodeFetch(RelativeURL.getAbsoluteURL(document.defaultView.location, url).href, requestInit) .then((response) => { if (taskManager.getTaskCount() === 0) { reject(new Error('Failed to complete fetch request. Task was canceled.')); diff --git a/packages/happy-dom/src/location/Location.ts b/packages/happy-dom/src/location/Location.ts index 0e16a3671..05898745b 100644 --- a/packages/happy-dom/src/location/Location.ts +++ b/packages/happy-dom/src/location/Location.ts @@ -1,4 +1,4 @@ -import URL from './URL'; +import { URL } from 'url'; /** * diff --git a/packages/happy-dom/src/location/RelativeURL.ts b/packages/happy-dom/src/location/RelativeURL.ts index 53a04ab5f..3308685c2 100644 --- a/packages/happy-dom/src/location/RelativeURL.ts +++ b/packages/happy-dom/src/location/RelativeURL.ts @@ -1,5 +1,5 @@ import Location from './Location'; -import URL from './URL'; +import { URL } from 'url'; /** * Helper class for getting the URL relative to a Location object. diff --git a/packages/happy-dom/src/location/URL.ts b/packages/happy-dom/src/location/URL.ts deleted file mode 100644 index 2d52a7c47..000000000 --- a/packages/happy-dom/src/location/URL.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { URL as Url } from 'url'; - -/** - * - */ -export default class URL extends Url {} diff --git a/packages/happy-dom/src/location/URLSearchParams.ts b/packages/happy-dom/src/location/URLSearchParams.ts deleted file mode 100644 index fbfd6dbba..000000000 --- a/packages/happy-dom/src/location/URLSearchParams.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { URLSearchParams as UrlSearchParams } from 'url'; - -/** - * - */ -export default class URLSearchParams extends UrlSearchParams {} diff --git a/packages/happy-dom/src/window/IWindow.ts b/packages/happy-dom/src/window/IWindow.ts index 88fa0b7aa..4dd4c9898 100644 --- a/packages/happy-dom/src/window/IWindow.ts +++ b/packages/happy-dom/src/window/IWindow.ts @@ -36,8 +36,7 @@ import AnimationEvent from '../event/events/AnimationEvent'; import KeyboardEvent from '../event/events/KeyboardEvent'; import ProgressEvent from '../event/events/ProgressEvent'; import EventTarget from '../event/EventTarget'; -import URL from '../location/URL'; -import URLSearchParams from '../location/URLSearchParams'; +import { URL, URLSearchParams } from 'url'; import Location from '../location/Location'; import MutationObserver from '../mutation-observer/MutationObserver'; import DOMParser from '../dom-parser/DOMParser'; @@ -83,6 +82,9 @@ import IRequestInit from '../fetch/IRequestInit'; import IResponse from '../fetch/IResponse'; import Range from '../range/Range'; import MediaQueryList from '../match-media/MediaQueryList'; +import XMLHttpRequest from '../xml-http-request/XMLHttpRequest'; +import XMLHttpRequestUpload from '../xml-http-request/XMLHttpRequestUpload'; +import XMLHttpRequestEventTarget from '../xml-http-request/XMLHttpRequestEventTarget'; import DOMRect from '../nodes/element/DOMRect'; import Window from './Window'; import { Performance } from 'perf_hooks'; @@ -179,6 +181,9 @@ export default interface IWindow extends IEventTarget, NodeJS.Global { readonly Response: { new (body?: unknown | null, init?: IResponseInit): IResponse }; readonly Range: typeof Range; readonly DOMRect: typeof DOMRect; + readonly XMLHttpRequest: typeof XMLHttpRequest; + readonly XMLHttpRequestUpload: typeof XMLHttpRequestUpload; + readonly XMLHttpRequestEventTarget: typeof XMLHttpRequestEventTarget; // Events onload: (event: Event) => void; diff --git a/packages/happy-dom/src/window/Window.ts b/packages/happy-dom/src/window/Window.ts index 46aa1fc4f..28366b2d0 100644 --- a/packages/happy-dom/src/window/Window.ts +++ b/packages/happy-dom/src/window/Window.ts @@ -36,8 +36,7 @@ import AnimationEvent from '../event/events/AnimationEvent'; import KeyboardEvent from '../event/events/KeyboardEvent'; import ProgressEvent from '../event/events/ProgressEvent'; import EventTarget from '../event/EventTarget'; -import URL from '../location/URL'; -import URLSearchParams from '../location/URLSearchParams'; +import { URL, URLSearchParams } from 'url'; import Location from '../location/Location'; import NonImplementedEventTypes from '../event/NonImplementedEventTypes'; import MutationObserver from '../mutation-observer/MutationObserver'; @@ -94,9 +93,9 @@ import VMGlobalPropertyScript from './VMGlobalPropertyScript'; import * as PerfHooks from 'perf_hooks'; import VM from 'vm'; import { Buffer } from 'buffer'; -import XMLHttpRequest from '../xml-http-request/XMLHttpRequest'; +import XMLHttpRequestImplementation from '../xml-http-request/XMLHttpRequest'; import XMLHttpRequestUpload from '../xml-http-request/XMLHttpRequestUpload'; -import { XMLHttpRequestEventTarget } from '../xml-http-request/XMLHttpRequestEventTarget'; +import XMLHttpRequestEventTarget from '../xml-http-request/XMLHttpRequestEventTarget'; import Base64 from '../base64/Base64'; import IDocument from '../nodes/document/IDocument'; @@ -204,6 +203,9 @@ export default class Window extends EventTarget implements IWindow { public readonly Response: { new (body?: NodeJS.ReadableStream | null, init?: IResponseInit): IResponse; }; + public readonly XMLHttpRequestUpload = XMLHttpRequestUpload; + public readonly XMLHttpRequestEventTarget = XMLHttpRequestEventTarget; + public readonly XMLHttpRequest; public readonly DOMParser; public readonly Range; public readonly FileReader; @@ -233,10 +235,6 @@ export default class Window extends EventTarget implements IWindow { public readonly localStorage = new Storage(); public readonly performance = PerfHooks.performance; - public XMLHttpRequest = XMLHttpRequest; - public XMLHttpRequestUpload = XMLHttpRequestUpload; - public XMLHttpRequestEventTarget = XMLHttpRequestEventTarget; - // Node.js Globals public ArrayBuffer; public Boolean; @@ -345,7 +343,6 @@ export default class Window extends EventTarget implements IWindow { } } - XMLHttpRequest._defaultView = this; HTMLDocument._defaultView = this; const document = new HTMLDocument(); @@ -361,6 +358,7 @@ export default class Window extends EventTarget implements IWindow { FileReaderImplementation._ownerDocument = document; DOMParserImplementation._ownerDocument = document; RangeImplementation._ownerDocument = document; + XMLHttpRequestImplementation._ownerDocument = document; /* eslint-disable jsdoc/require-jsdoc */ class Response extends ResponseImplementation { @@ -378,10 +376,9 @@ export default class Window extends EventTarget implements IWindow { class DOMParser extends DOMParserImplementation { public static _ownerDocument: IDocument = document; } - class Range extends RangeImplementation { + class XMLHttpRequest extends XMLHttpRequestImplementation { public static _ownerDocument: IDocument = document; } - /* eslint-enable jsdoc/require-jsdoc */ this.Response = Response; @@ -389,7 +386,7 @@ export default class Window extends EventTarget implements IWindow { this.Image = Image; this.FileReader = FileReader; this.DOMParser = DOMParser; - this.Range = Range; + this.XMLHttpRequest = XMLHttpRequest; this._setupVMContext(); diff --git a/packages/happy-dom/src/xml-http-request/XMLHttpReqeustUtility.ts b/packages/happy-dom/src/xml-http-request/XMLHttpReqeustUtility.ts deleted file mode 100644 index 17ee0ac44..000000000 --- a/packages/happy-dom/src/xml-http-request/XMLHttpReqeustUtility.ts +++ /dev/null @@ -1,13 +0,0 @@ -const NodeVersion = process.version.replace('v', '').split('.'); - -export interface IXMLHttpRequestOptions { - anon?: boolean; -} - -export const MajorNodeVersion = Number.parseInt(NodeVersion[0]); -export const copyToArrayBuffer = (buffer: Buffer, offset?: number): ArrayBuffer => { - const arrayBuffer = new ArrayBuffer(buffer.length); - const view = new Uint8Array(arrayBuffer); - view.set(buffer, offset || 0); - return arrayBuffer; -}; diff --git a/packages/happy-dom/src/xml-http-request/XMLHttpRequest.ts b/packages/happy-dom/src/xml-http-request/XMLHttpRequest.ts index 8e40e19c8..ad88299e8 100644 --- a/packages/happy-dom/src/xml-http-request/XMLHttpRequest.ts +++ b/packages/happy-dom/src/xml-http-request/XMLHttpRequest.ts @@ -1,696 +1,653 @@ -import * as http from 'http'; -import { - Agent as HttpAgent, - ClientRequest, - IncomingMessage, - RequestOptions as RequestOptionsHttp -} from 'http'; -import * as https from 'https'; -import { Agent as HttpsAgent } from 'https'; -import ProgressEvent from '../event/events/ProgressEvent'; -import DOMExceptionNameEnum from '../exception/DOMExceptionNameEnum'; -import { ProgressEventListener, XMLHttpRequestEventTarget } from './XMLHttpRequestEventTarget'; -import XMLHttpRequestUpload from './XMLHttpRequestUpload'; -import DOMException from '../exception/DOMException'; -import IWindow from '../window/IWindow'; -import URL from '../location/URL'; +import FS from 'fs'; +import { URL } from 'url'; +import ChildProcess from 'child_process'; +import HTTP from 'http'; +import HTTPS from 'https'; +import XMLHttpRequestEventTarget from './XMLHttpRequestEventTarget'; +import XMLHttpRequestReadyStateEnum from './XMLHttpRequestReadyStateEnum'; +import Event from '../event/Event'; +import IDocument from '../nodes/document/IDocument'; import RelativeURL from '../location/RelativeURL'; -import Blob from '../file/Blob'; -import { - copyToArrayBuffer, - MajorNodeVersion, - IXMLHttpRequestOptions -} from './XMLHttpReqeustUtility'; -import { spawnSync } from "child_process"; -const SyncWorkerFile = require.resolve ? require.resolve('./XMLHttpRequestSyncWorker') : null; +import XMLHttpRequestUpload from './XMLHttpRequestUpload'; + +const SSL_CERT = `-----BEGIN CERTIFICATE----- +MIIDYzCCAkugAwIBAgIUJRKB/H66hpet1VfUlm0CiXqePA4wDQYJKoZIhvcNAQEL +BQAwQTELMAkGA1UEBhMCU0UxDjAMBgNVBAgMBVNrYW5lMQ4wDAYDVQQHDAVNYWxt +bzESMBAGA1UECgwJSGFwcHkgRE9NMB4XDTIyMTAxMTIyMDM0OVoXDTMyMTAwODIy +MDM0OVowQTELMAkGA1UEBhMCU0UxDjAMBgNVBAgMBVNrYW5lMQ4wDAYDVQQHDAVN +YWxtbzESMBAGA1UECgwJSGFwcHkgRE9NMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAqerQSQEg/SxVxRiwlItithr5e5EMZo1nsqt/xOxagbmpW3IEmt0j +bpbH7iEF4DDEo7KAOwUCOwVWeFxRoag8lG2ax48wrgjlCna45XDn0Xeg1ARajL04 +gs46HZ0VrzIloVGfln0zgt/Vum5BNqs9Oc5fQoBmoP3cAn3dn4ZVcP0AKthtcyPl +q2DuNRN0PV0D2RtMSiAy9l1Ko6N5x+sAeClDyOL+sTDLngZBVeZyOKt9Id15S8Zt +XtA6VMgHnnF3jChn7pag77rsd/y5iANAVNZYqRl+Eg7xaDcsvbgH46UBOrBcB39Q +tTh5Mtjoxep5e3ZDFG+kQ1HUE+iz5O5n0wIDAQABo1MwUTAdBgNVHQ4EFgQU69s9 +YSobG/m2SN4L/7zTaF7iDbwwHwYDVR0jBBgwFoAU69s9YSobG/m2SN4L/7zTaF7i +DbwwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAi/WUXx2oal8L +YnPlIuKfh49n/K18wXSYG//oFYwxfVxqpYH8hUiXVm/GUcXCxS++hUkaKLqXmH9q +MKJiCrZr3vS+2nsBKopkICu/TLdROl0sAI9lByfnEbfSAzjxe1IWJdK8NdY0y5m5 +9pEr/URVIAp/CxrneyASb4q0Jg5To3FR7vYc+2X6wZn0MundKMg6Dp9/A37jiF3l +Tt/EJp299YZcsUzh+LnRuggRjnoOVu1aLcLFlaUiwZfy9m8mLG6B/mdW/qNzNMh9 +Oqvg1zfGdpz/4D/2UUUBn6pq1vbsoAaF3OesoA3mfDcegDf/H9woJlpT0Wql+e68 +Y3FblSokcA== +-----END CERTIFICATE-----`; + +const SSL_KEY = `-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCp6tBJASD9LFXF +GLCUi2K2Gvl7kQxmjWeyq3/E7FqBualbcgSa3SNulsfuIQXgMMSjsoA7BQI7BVZ4 +XFGhqDyUbZrHjzCuCOUKdrjlcOfRd6DUBFqMvTiCzjodnRWvMiWhUZ+WfTOC39W6 +bkE2qz05zl9CgGag/dwCfd2fhlVw/QAq2G1zI+WrYO41E3Q9XQPZG0xKIDL2XUqj +o3nH6wB4KUPI4v6xMMueBkFV5nI4q30h3XlLxm1e0DpUyAeecXeMKGfulqDvuux3 +/LmIA0BU1lipGX4SDvFoNyy9uAfjpQE6sFwHf1C1OHky2OjF6nl7dkMUb6RDUdQT +6LPk7mfTAgMBAAECggEAKkwTkTjAt4UjzK56tl+EMQTB+ep/hb/JgoaChci4Nva6 +m9LkJpDJ0yuhlTuPNOGu8XjrxsVWas7HWarRf0Zb3i7yip6wZYI9Ub+AA015x4DZ +/i0fRU2NFbK0cM67qSL4jxG8gj+kZP3HPGNZxHwX/53JxMolwgmvjMc8NgvAlSFd +NnV9h4xtbhUh1NGS5zmP3iU2rwnE8JrIEzwy6axLom7nekAgkdcbAr0UoBs8gcgH +aYNhU4Gz3tGcZZ0IXAfT/bJIH1Ko8AGv4pssWc3BXcmmNdm/+kzvHIxEIV7Qegmo +XG1ZyZCyD/0b4/3e8ySDBEDqwR+HeyTW2isWG2agAQKBgQDp44aTwr3dkIXY30xv +FPfUOipg/B49dWnffYJ9MWc1FT9ijNPAngWSk0EIiEQIazICcUBI4Yji6/KeyqLJ +GdLpDi1CkKqtyh73mjELinYp3EUQgEa77aQogGa2+nMOVfu+O5CtloUrv/A18jX3 ++VEyaEASK0fWmnSI0OdlxQHIAQKBgQC5+xOls2F3MlKASvWRLlnW1wHqlDTtVoYg +5Nh8syZH4Ci2UH8tON3A5/7SWNM0t1cgV6Cw4zW8Z2spgIT/W0iYYrQ4hHL1xdCu ++CxL1km4Gy8Uwpsd+KdFahFqF/XTmLzW0HXLxWSK0fTwmdV0SFrKF3MXfTCU2AeZ +jJoMFb6P0wKBgQC3Odw6s0vkYAzLGhuZxfZkVvDOK5RRF0NKpttr0iEFL9EJFkPo +2KKK8jr3QTDy229BBJGUxsJi6u6VwS8HlehpVQbV59kd7oKV/EBBx0XMg1fDlopT +PNbmN7i/zbIG4AsoOyebJZjL7kBzMn1e9vzKHWtcEHXlw/hZGja8vjooAQKBgAeg +xK2HLfg1mCyq5meN/yFQsENu0LzrT5UJzddPgcJw7zqLEqxIKNBAs7Ls8by3yFsL +PQwERa/0jfCl1M6kb9XQNpQa2pw6ANUsWKTDpUJn2wZ+9N3F1RaDwzMWyH5lRVmK +M0qoTfdjpSg5Jwgd75taWt4bxGJWeflSSv8z5R0BAoGAWL8c527AbeBvx2tOYKkD +2TFranvANNcoMrbeviZSkkGvMNDP3p8b6juJwXOIeWNr8q4vFgCzLmq6d1/9gYm2 +3XJwwyD0LKlqzkBrrKU47qrnmMosUrIRlrAzd3HbShOptxc6Iz2apSaUDKGKXkaw +gl5OpEjeliU7Mus0BVS858g= +-----END PRIVATE KEY-----`; + +// These headers are not user setable. +// The following are allowed but banned in the spec: +// * User-agent +const FORBIDDEN_REQUEST_HEADERS = [ + 'accept-charset', + 'accept-encoding', + 'access-control-request-headers', + 'access-control-request-method', + 'connection', + 'content-length', + 'content-transfer-encoding', + 'cookie', + 'cookie2', + 'date', + 'expect', + 'host', + 'keep-alive', + 'origin', + 'referer', + 'te', + 'trailer', + 'transfer-encoding', + 'upgrade', + 'via' +]; + +// These request methods are not allowed +const FORBIDDEN_REQUEST_METHODS = ['TRACE', 'TRACK', 'CONNECT']; /** - * References: https://github.com/souldreamer/xhr2-cookies. + * XMLHttpRequest. + * + * Based on: + * https://github.com/mjwwit/node-XMLHttpRequest/blob/master/lib/XMLHttpRequest.js */ export default class XMLHttpRequest extends XMLHttpRequestEventTarget { - public static readonly UNSENT = 0; - public static readonly OPENED = 1; - public static readonly HEADERS_RECEIVED = 2; - public static readonly LOADING = 3; - public static readonly DONE = 4; - - public static _defaultView: IWindow; - - public onreadystatechange: ProgressEventListener | null = null; - public readyState: number = XMLHttpRequest.UNSENT; - - public response: string | ArrayBuffer | Buffer | object | null = null; + // Owner document is set by a sub-class in the Window constructor + public static _ownerDocument: IDocument = null; + + // Constants + public static UNSENT = XMLHttpRequestReadyStateEnum.unsent; + public static OPENED = XMLHttpRequestReadyStateEnum.opened; + public static HEADERS_RECEIVED = XMLHttpRequestReadyStateEnum.headersRecieved; + public static LOADING = XMLHttpRequestReadyStateEnum.loading; + public static DONE = XMLHttpRequestReadyStateEnum.done; + + // Public properties + public readyState: XMLHttpRequestReadyStateEnum = XMLHttpRequestReadyStateEnum.unsent; public responseText = ''; - public responseType = ''; - public status = 0; - public statusText = ''; - public timeout = 0; - public upload = new XMLHttpRequestUpload(); - public responseUrl = ''; - public withCredentials = false; - - public nodejsHttpAgent: HttpAgent = http.globalAgent; - public nodejsHttpsAgent: HttpsAgent = https.globalAgent; - - private readonly anonymous: boolean; - private method: string | null = null; - private url: URL | null = null; - private auth: string | null = null; - private body: string | Buffer | ArrayBuffer | ArrayBufferView; - private sync = false; - private headers: { [header: string]: string } = {}; - private loweredHeaders: { [lowercaseHeader: string]: string } = {}; - private mimeOverride: string | null = null; // TODO: is type right? - private _request: ClientRequest | null = null; - private _response: IncomingMessage | null = null; - // @ts-ignore - private _error: Error | string | null = null; - private responseParts: Buffer[] | null = null; - private responseHeaders: { [lowercaseHeader: string]: string } | null = null; - private loadedBytes = 0; - private totalBytes = 0; - private lengthComputable = false; - - private restrictedMethods = { CONNECT: true, TRACE: true, TRACK: true }; - private restrictedHeaders = { - 'accept-charset': true, - 'accept-encoding': true, - 'access-control-request-headers': true, - 'access-control-request-method': true, - connection: true, - 'content-length': true, - cookie: true, - cookie2: true, - date: true, - dnt: true, - expect: true, - host: true, - 'keep-alive': true, - origin: true, - referer: true, - te: true, - trailer: true, - 'transfer-encoding': true, - upgrade: true, - 'user-agent': true, - via: true + public responseXML = ''; + public status: number = null; + public statusText: string = null; + public upload: XMLHttpRequestUpload = new XMLHttpRequestUpload(); + + // Private properties + private readonly _ownerDocument: IDocument = null; + private _request = null; + private _response = null; + private _requestHeaders = {}; + private _sendFlag = false; + private _errorFlag = false; + private _abortedFlag = false; + private _settings: { + method: string; + url: string; + async: boolean; + user: string; + password: string; + } = { + method: null, + url: null, + async: true, + user: null, + password: null }; - private privateHeaders = { 'set-cookie': true, 'set-cookie2': true }; /** - * News a request. - * - * @param options + * Constructor. */ - constructor(options: IXMLHttpRequestOptions = {}) { + constructor() { super(); - this.anonymous = options.anon || false; + this._ownerDocument = XMLHttpRequest._ownerDocument; } /** - * Initializes a newly-created request, or re-initializes an existing one. + * Opens the connection. * - * @param method The HTTP request method to use. - * @param url The URL to request. - * @param async Whether the request is synchronous or asynchronous. - * @param user The username to use for authentication purposes. - * @param password The password to use for authentication purposes. + * @param method Connection method (eg GET, POST). + * @param url URL for the connection. + * @param [async=true] Asynchronous connection. + * @param [user] Username for basic authentication (optional). + * @param [password] Password for basic authentication (optional). */ public open(method: string, url: string, async = true, user?: string, password?: string): void { - const { _defaultView } = XMLHttpRequest; - // If _defaultView is not defined, then we can't set the URL. - if (!_defaultView) { - throw new Error('need set defaultView'); - } - method = method.toUpperCase(); - if (this.restrictedMethods[method]) { - throw new DOMException( - `HTTP method ${method} is not allowed in XHR`, - DOMExceptionNameEnum.securityError - ); - } + this.abort(); + this._errorFlag = false; + this._abortedFlag = false; - // Get and Parse the URL relative to the given Location object. - const xhrUrl = RelativeURL.getAbsoluteURL(XMLHttpRequest._defaultView.location, url); - // Set username and password if given. - xhrUrl.username = user ? user : xhrUrl.username; - xhrUrl.password = password ? password : xhrUrl.password; + const upperMethod = method.toUpperCase(); - if ( - this.readyState === XMLHttpRequest.HEADERS_RECEIVED || - this.readyState === XMLHttpRequest.LOADING - ) { - // TODO: terminate abort(), terminate send() + // Check for valid request method + if (FORBIDDEN_REQUEST_METHODS.includes(upperMethod)) { + throw new Error('SecurityError: Request method not allowed'); } - this.method = method; - this.url = xhrUrl; - this.auth = `${this.url.username || ''}:${this.url.password || ''}` - this.sync = !async; - // this.headers = {}; - this.loweredHeaders = {}; - // this.mimeOverride = null; - this.setReadyState(XMLHttpRequest.OPENED); - this._request = null; - this._response = null; - this.status = 0; - this.statusText = ''; - this.responseParts = []; - this.responseHeaders = null; - this.loadedBytes = 0; - this.totalBytes = 0; - this.lengthComputable = false; + this._settings = { + method: upperMethod, + url: url.toString(), + async: async, + user: user || null, + password: password || null + }; + this._setState(XMLHttpRequestReadyStateEnum.opened); } /** - * Sets the value of an HTTP request header. + * Sets a header for the request. * - * @param name The name of the header whose value is to be set. - * @param value The value to set as the body of the header. + * @param header Header name + * @param value Header value + * @returns Header added. */ - public setRequestHeader(name: string, value: unknown): void { - const { _defaultView } = XMLHttpRequest; - if (this.readyState !== XMLHttpRequest.OPENED) { - throw new DOMException( - 'XHR readyState must be OPENED', - DOMExceptionNameEnum.invalidStateError - ); + public setRequestHeader(header: string, value: string): boolean { + if (this.readyState != XMLHttpRequestReadyStateEnum.opened) { + throw new Error('INVALID_STATE_ERR: setRequestHeader can only be called when state is OPEN'); } + const lowerHeader = header.toLowerCase(); - const loweredName = name.toLowerCase(); - if ( - this.restrictedHeaders[loweredName] || - /^sec-/.test(loweredName) || - /^proxy-/.test(loweredName) - ) { - _defaultView.console.warn(`Refused to set unsafe header "${name}"`); - return; + if (FORBIDDEN_REQUEST_HEADERS.includes(lowerHeader)) { + return false; } - - const headerBody = value.toString(); - if (this.loweredHeaders[loweredName] != null) { - name = this.loweredHeaders[loweredName]; - this.headers[name] = `${this.headers[name]}, ${headerBody}`; - } else { - this.loweredHeaders[loweredName] = name; - this.headers[name] = headerBody; + if (this._sendFlag) { + throw new Error('INVALID_STATE_ERR: send flag is true'); } + this._requestHeaders[lowerHeader] = value; + return true; } /** - * Sends the request. If the request is asynchronous (which is the default), this method returns as soon as the request is sent. + * Gets a header from the server response. * - * @param data The data to send with the request. - */ - public send(data?: string | Buffer | ArrayBuffer | ArrayBufferView): void { - const { invalidStateError, networkError } = DOMExceptionNameEnum; - if (this.readyState !== XMLHttpRequest.OPENED) { - throw new DOMException('XHR readyState must be OPENED', invalidStateError); - } - if (this._request) { - throw new DOMException('send() already called', invalidStateError); - } - switch (this.url.protocol) { - case 'file:': - return this.sendFile(data); - case 'http:': - case 'https:': - return this.sendHttp(data); - default: - throw new DOMException(`Unsupported protocol ${this.url.protocol}`, networkError); - } - } - - /** - * Aborts the request if it has already been sent. + * @param string header Name of header to get. + * @param header + * @returns string Text of the header or null if it doesn't exist. */ - public abort(): void { - if (this._request == null) { - return; - } - // ClientRequest.destroy breaks the test suite for versions 10 and 12, - // Hence the version check - if (MajorNodeVersion > 13) { - this._request.destroy(); - } else { - this._request.abort(); - } - this.setReadyState(XMLHttpRequest.DONE); - this.setError(); - - this.dispatchProgress('abort'); - this.dispatchProgress('loadend'); - } + public getResponseHeader(header: string): string { + const lowerHeader = header.toLowerCase(); - /** - * Returns the string containing the text of the specified header, or null if either the response has not yet been received or the header doesn't exist in the response. - * - * @param name The name of the header. - */ - public getResponseHeader(name: string): string { if ( - this.responseHeaders == null || - name == null || - this.readyState === XMLHttpRequest.OPENED || - this.readyState === XMLHttpRequest.UNSENT + typeof header === 'string' && + this.readyState > XMLHttpRequestReadyStateEnum.opened && + this._response.headers[lowerHeader] && + !this._errorFlag ) { - return null; + return this._response.headers[lowerHeader]; } - const loweredName = name.toLowerCase(); - return this.responseHeaders.hasOwnProperty(loweredName) - ? this.responseHeaders[name.toLowerCase()] - : null; + + return null; } /** - * Returns all the response headers, separated by CRLF, as a string, or null if no response has been received. + * Gets all the response headers. * + * @returns A string with all response headers separated by CR+LF. */ public getAllResponseHeaders(): string { - if ( - this.responseHeaders == null || - this.readyState === XMLHttpRequest.OPENED || - this.readyState === XMLHttpRequest.UNSENT - ) { + if (this.readyState < XMLHttpRequestReadyStateEnum.headersRecieved || this._errorFlag) { return ''; } - return Object.keys(this.responseHeaders) - .map((key) => `${key}: ${this.responseHeaders[key]}`) - .join('\r\n'); - } + let result = ''; - /** - * Overrides the MIME type returned by the server. - * - * @param mimeType The MIME type to use. - */ - public overrideMimeType(mimeType: string): void { - if (this.readyState === XMLHttpRequest.LOADING || this.readyState === XMLHttpRequest.DONE) { - throw new DOMException( - 'overrideMimeType() not allowed in LOADING or DONE', - DOMExceptionNameEnum.invalidStateError - ); + for (const name of Object.keys(this._response.headers)) { + // Cookie headers are excluded + if (name !== 'set-cookie' && name !== 'set-cookie2') { + result += `${name}: ${this._response.headers[name]}\r\n`; + } } - this.mimeOverride = mimeType.toLowerCase(); - } - /** - * Sets the value of the ReadyState. - * - * @param readyState The new value of the ReadyState. - */ - private setReadyState(readyState: number): void { - this.readyState = readyState; - this.dispatchEvent(new ProgressEvent('readystatechange')); - } - - /** - * Send request with file. - * - * @todo Not implemented. - * @param _data File body to send. - */ - private sendFile(_data: unknown): void { - // TODO: sendFile() not implemented. - throw new Error('Protocol file: not implemented'); + return result.substr(0, result.length - 2); } /** - * Send request with http. + * Gets a request header * - * @param data Data to send. + * @param name Name of header to get. + * @returns Returns the request header or empty string if not set. */ - private sendHttp(data?: string | Buffer | ArrayBuffer | ArrayBufferView): void { - const { _defaultView } = XMLHttpRequest; - this.body = data; - - if (this.sync) { - const params = this._serialParams(); - const res = spawnSync(process.execPath, [SyncWorkerFile], { input: params, maxBuffer: Infinity }); - res; - // TODO: sync not implemented. - throw new Error('Synchronous XHR processing not implemented'); - } - if (data && (this.method === 'GET' || this.method === 'HEAD')) { - _defaultView.console.warn(`Discarding entity body for ${this.method} requests`); - data = null; - } else { - data = data || ''; + public getRequestHeader(name: string): string { + const lowerName = name.toLowerCase(); + if (typeof name === 'string' && this._requestHeaders[lowerName]) { + return this._requestHeaders[lowerName]; } - this.upload.setData(data); - this.finalizeHeaders(); - this.sendHxxpRequest(); + return ''; } /** - * SendHxxpRequest sends the actual request. + * Sends the request to the server. * + * @param data Optional data to send as request body. */ - private sendHxxpRequest(): void { - const { _defaultView } = XMLHttpRequest; - if (this.withCredentials) { - // Set cookie if URL is same-origin. - if (_defaultView.location.origin === this.url.origin) { - this.headers.cookie = _defaultView.document.cookie; - } + public send(data: string): void { + if (this.readyState != XMLHttpRequestReadyStateEnum.opened) { + throw new Error('INVALID_STATE_ERR: connection must be opened before send() is called'); } - const [hxxp, agent] = - this.url.protocol === 'http:' ? [http, this.nodejsHttpAgent] : [https, this.nodejsHttpsAgent]; - const requestMethod: (options: RequestOptionsHttp) => ClientRequest = hxxp.request.bind(hxxp); - const request = requestMethod({ - hostname: this.url.hostname, - port: +this.url.port, - path: this.url.pathname, - auth: this.auth, - method: this.method, - headers: this.headers, - agent - }); - this._request = request; - - if (this.timeout) { - request.setTimeout(this.timeout, () => this.onHttpTimeout(request)); + if (this._sendFlag) { + throw new Error('INVALID_STATE_ERR: send has already been called'); } - request.on('response', (response) => this.onHttpResponse(request, response)); - request.on('error', (error) => this.onHttpRequestError(request, error)); - this.upload.startUpload(request); - if (this._request === request) { - this.dispatchProgress('loadstart'); - } - } + let ssl = false; + let local = false; + const url = RelativeURL.getAbsoluteURL( + this._ownerDocument.defaultView.location, + this._settings.url + ); + let host; - /** - * Finalize headers. - * - */ - private finalizeHeaders(): void { - const { _defaultView } = XMLHttpRequest; - this.headers = { - ...this.headers, - Connection: 'keep-alive', - Host: this.url.host, - 'User-Agent': _defaultView.navigator.userAgent, - ...(this.anonymous ? { Referer: 'about:blank' } : { Referer: _defaultView.location.origin }) - }; - this.upload.finalizeHeaders(this.headers, this.loweredHeaders); - } + // Determine the server + switch (url.protocol) { + case 'https:': + ssl = true; + // SSL & non-SSL both need host, no break here. + case 'http:': + host = url.hostname; + break; - /** - * OnHttpResponse handles the response. - * - * @param request The request. - * @param response The response. - */ - private onHttpResponse(request: ClientRequest, response: IncomingMessage): void { - if (this._request !== request) { - return; - } - const { _defaultView } = XMLHttpRequest; + case 'file:': + local = true; + break; + + case undefined: + case '': + host = 'localhost'; + break; - if (this.withCredentials && response.headers['set-cookie']) { - _defaultView.document.cookie = response.headers['set-cookie'].join('; '); + default: + throw new Error('Protocol not supported.'); } - if ([301, 302, 303, 307, 308].indexOf(response.statusCode) >= 0) { - this.url = new _defaultView.URL(response.headers.location); - this.method = 'GET'; - if (this.loweredHeaders['content-type']) { - delete this.headers[this.loweredHeaders['content-type']]; - delete this.loweredHeaders['content-type']; + // Load files off the local filesystem (file://) + if (local) { + if (this._settings.method !== 'GET') { + throw new Error('XMLHttpRequest: Only GET method is supported'); } - if (this.headers['Content-Type'] != null) { - delete this.headers['Content-Type']; + + if (this._settings.async) { + FS.readFile(unescape(url.pathname), 'utf8', (error: Error, data: Buffer) => { + if (error) { + this._handleError(error); + } else { + this.status = 200; + this.responseText = data.toString(); + this._setState(XMLHttpRequestReadyStateEnum.done); + } + }); + } else { + try { + this.responseText = FS.readFileSync(unescape(url.pathname), 'utf8'); + this.status = 200; + this._setState(XMLHttpRequestReadyStateEnum.done); + } catch (error) { + this._handleError(error); + } } - delete this.headers['Content-Length']; - this.upload.reset(); - this.finalizeHeaders(); - this.sendHxxpRequest(); return; } - this._response = response; - this._response.on('data', (data) => this.onHttpResponseData(response, data)); - this._response.on('end', () => this.onHttpResponseEnd(response)); - this._response.on('close', () => this.onHttpResponseClose(response)); - - this.responseUrl = this.url.href.split('#')[0]; - this.status = response.statusCode; - this.statusText = http.STATUS_CODES[this.status]; - this.parseResponseHeaders(response); + // Default to port 80. If accessing localhost on another port be sure + // To use http://localhost:port/path + const port = url.port || (ssl ? 443 : 80); + // Add query string if one is used + const uri = url.pathname + (url.search ? url.search : ''); - const lengthString = this.responseHeaders['content-length'] || ''; - this.totalBytes = +lengthString; - this.lengthComputable = !!lengthString; - - this.setReadyState(XMLHttpRequest.HEADERS_RECEIVED); - } + // Set the Host header or the server may reject the request + this._requestHeaders['host'] = host; + if (!((ssl && port === 443) || port === 80)) { + this._requestHeaders['host'] += ':' + url.port; + } - /** - * OnHttpResponseData handles the response data. - * - * @param response The response. - * @param data The data. - */ - private onHttpResponseData(response: IncomingMessage, data: string | Buffer): void { - if (this._response !== response) { - return; + // Set Basic Auth if necessary + if (this._settings.user) { + if (typeof this._settings.password == 'undefined') { + this._settings.password = ''; + } + const authBuffer = Buffer.from(this._settings.user + ':' + this._settings.password); + this._requestHeaders['authorization'] = 'Basic ' + authBuffer.toString('base64'); } - this.responseParts.push(Buffer.from(data)); - this.loadedBytes += data.length; + // Set content length header + if (this._settings.method === 'GET' || this._settings.method === 'HEAD') { + data = null; + } else if (data) { + this._requestHeaders['content-length'] = Buffer.isBuffer(data) + ? data.length + : Buffer.byteLength(data); - if (this.readyState !== XMLHttpRequest.LOADING) { - this.setReadyState(XMLHttpRequest.LOADING); - } + if (!this._requestHeaders['content-type']) { + this._requestHeaders['content-type'] = 'text/plain;charset=UTF-8'; + } + } else if (this._settings.method === 'POST') { + // For a post with no data set Content-Length: 0. + // This is required by buggy servers that don't meet the specs. + this._requestHeaders['content-length'] = 0; + } + + const options = { + host: host, + port: port, + path: uri, + method: this._settings.method, + headers: Object.assign(this._getDefaultRequestHeaders(), this._requestHeaders), + agent: false, + rejectUnauthorized: true, + key: null, + cert: null + }; - this.dispatchProgress('progress'); - } + if (ssl) { + options.key = SSL_KEY; + options.cert = SSL_CERT; + } + + // Reset error flag + this._errorFlag = false; + // Handle async requests + if (this._settings.async) { + // Use the proper protocol + const sendRequest = ssl ? HTTPS.request : HTTP.request; + + // Request is being sent, set send flag + this._sendFlag = true; + + // As per spec, this is called here for historical reasons. + this.dispatchEvent(new Event('readystatechange')); + + // Handler for the response + const responseHandler = (resp): void => { + // Set response var to the response we got back + // This is so it remains accessable outside this scope + this._response = resp; + + // Check for redirect + // @TODO Prevent looped redirects + if ( + this._response.statusCode === 302 || + this._response.statusCode === 303 || + this._response.statusCode === 307 + ) { + // Change URL to the redirect location + this._settings.url = this._response.headers.location; + const url = new URL(this._settings.url); + // Set host var in case it's used later + host = url.hostname; + // Options for the new request + const newOptions = { + hostname: url.hostname, + port: url.port, + path: url.pathname, + method: this._response.statusCode === 303 ? 'GET' : this._settings.method, + headers: Object.assign(this._getDefaultRequestHeaders(), this._requestHeaders), + rejectUnauthorized: true, + key: null, + cert: null + }; + + if (ssl) { + newOptions.key = SSL_KEY; + newOptions.cert = SSL_CERT; + } + + // Issue the new request + this._request = sendRequest(newOptions, responseHandler).on('error', errorHandler); + this._request.end(); + // @TODO Check if an XHR event needs to be fired here + return; + } - /** - * OnHttpResponseEnd handles the response end. - * - * @param response The response. - */ - private onHttpResponseEnd(response: IncomingMessage): void { - if (this._response !== response) { - return; - } + if (this._response && this._response.setEncoding) { + this._response.setEncoding('utf8'); + } - this.parseResponse(); - this._request = null; - this._response = null; - this.setReadyState(XMLHttpRequest.DONE); + this._setState(XMLHttpRequestReadyStateEnum.headersRecieved); + this.status = this._response.statusCode; + + this._response.on('data', (chunk) => { + // Make sure there's some data + if (chunk) { + this.responseText += chunk; + } + // Don't emit state changes if the connection has been aborted. + if (this._sendFlag) { + this._setState(XMLHttpRequestReadyStateEnum.loading); + } + }); - this.dispatchProgress('load'); - this.dispatchProgress('loadend'); - } + this._response.on('end', () => { + if (this._sendFlag) { + // The sendFlag needs to be set before setState is called. Otherwise if we are chaining callbacks + // There can be a timing issue (the callback is called and a new call is made before the flag is reset). + this._sendFlag = false; + // Discard the 'end' event if the connection has been aborted + this._setState(XMLHttpRequestReadyStateEnum.done); + } + }); - /** - * OnHttpResponseClose handles the response close. - * - * @param response The response. - */ - private onHttpResponseClose(response: IncomingMessage): void { - if (this._response !== response) { - return; - } + this._response.on('error', (error) => { + this._handleError(error); + }); + }; - const request = this._request; - this.setError(); - // ClientRequest.destroy breaks the test suite for versions 10 and 12, - // Hence the version check - if (MajorNodeVersion > 13) { - request.destroy(); - } else { - request.abort(); - } - this.setReadyState(XMLHttpRequest.DONE); + // Error handler for the request + const errorHandler = (error): void => { + this._handleError(error); + }; - this.dispatchProgress('error'); - this.dispatchProgress('loadend'); - } + // Create the request + this._request = sendRequest(options, responseHandler).on('error', errorHandler); - /** - * OnHttpTimeout handles the timeout. - * - * @param request The request. - */ - private onHttpTimeout(request: ClientRequest): void { - if (this._request !== request) { - return; - } + // Node 0.4 and later won't accept empty data. Make sure it's needed. + if (data) { + this._request.write(data); + } - this.setError(); - // ClientRequest.destroy breaks the test suite for versions 10 and 12, - // Hence the version check - if (MajorNodeVersion > 13) { - request.destroy(); + this._request.end(); + + this.dispatchEvent(new Event('loadstart')); } else { - request.abort(); - } - this.setReadyState(XMLHttpRequest.DONE); + // Synchronous + // Create a temporary file for communication with the other Node process + const contentFile = '.node-xmlhttprequest-content-' + process.pid; + const syncFile = '.node-xmlhttprequest-sync-' + process.pid; + FS.writeFileSync(syncFile, '', 'utf8'); + + // The async request the other Node process executes + const execString = ` + const HTTP = require('http') + const HTTPS = require('https') + const FS = require('FS'); + const sendRequest = HTTP${ssl ? 'S' : ''}.request; + const options = ${JSON.stringify(options)}; + const responseText = ''; + const request = sendRequest(options, (response) => { + response.setEncoding('utf8'); + response.on('data', (chunk) => { + responseText += chunk; + }); + response.on('end', () => { + FS.writeFileSync('${contentFile}', 'NODE-XMLHTTPREQUEST-STATUS:' + response.statusCode + ',' + responseText, 'utf8'); + FS.unlinkSync('${syncFile}'); + }); + }); + response.on('error', (error) => { + FS.writeFileSync('${contentFile}', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8').on('error', (error) => { + FS.writeFileSync('${contentFile}', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8'); + FS.unlinkSync('${syncFile}')); + }); + }); + request.write(\`${JSON.stringify(data).slice(1, -1)}\`); + request.end(); + `.trim(); + + // Start the other Node Process, executing this string + const syncProc = ChildProcess.spawn(process.argv[0], ['-e', execString]); + + while (FS.existsSync(syncFile)) { + // Wait while the sync file is empty + } - this.dispatchProgress('timeout'); - this.dispatchProgress('loadend'); + this.responseText = FS.readFileSync(contentFile, 'utf8'); + + // Kill the child process once the file has data + syncProc.stdin.end(); + + // Remove the temporary file + FS.unlinkSync(contentFile); + + if (this.responseText.match(/^NODE-XMLHTTPREQUEST-ERROR:/)) { + // If the file returned an error, handle it + const errorObj = this.responseText.replace(/^NODE-XMLHTTPREQUEST-ERROR:/, ''); + this._handleError(errorObj, 503); + } else { + // If the file returned okay, parse its data and move to the DONE state + this.status = Number( + this.responseText.replace(/^NODE-XMLHTTPREQUEST-STATUS:([0-9]*),.*/, '$1') + ); + this.responseText = this.responseText.replace( + /^NODE-XMLHTTPREQUEST-STATUS:[0-9]*,(.*)/, + '$1' + ); + this._setState(XMLHttpRequestReadyStateEnum.done); + } + } } /** - * OnHttpRequestError handles the request error. - * - * @param request The request. - * @param error The error. + * Aborts a request. */ - private onHttpRequestError(request: ClientRequest, error: Error): void { - this._error = error; - if (this._request !== request) { - return; - } - - this.setError(); - // ClientRequest.destroy breaks the test suite for versions 10 and 12, - // Hence the version check - if (MajorNodeVersion > 13) { - request.destroy(); - } else { - request.abort(); + public abort(): void { + if (this._request) { + this._request.abort(); + this._request = null; } - this.setReadyState(XMLHttpRequest.DONE); - this.dispatchProgress('error'); - this.dispatchProgress('loadend'); - } + this._requestHeaders = {}; + this.responseText = ''; + this.responseXML = ''; - /** - * Dispatches the progress event. - * - * @param eventType The event type. - */ - private dispatchProgress(eventType: string): void { - const event = new ProgressEvent(eventType, { - lengthComputable: this.lengthComputable, - loaded: this.loadedBytes, - total: this.totalBytes - }); - this.dispatchEvent(event); + this._errorFlag = this._abortedFlag = true; + if ( + this.readyState !== XMLHttpRequestReadyStateEnum.unsent && + (this.readyState !== XMLHttpRequestReadyStateEnum.opened || this._sendFlag) && + this.readyState !== XMLHttpRequestReadyStateEnum.done + ) { + this._sendFlag = false; + this._setState(XMLHttpRequestReadyStateEnum.done); + } + this.readyState = XMLHttpRequestReadyStateEnum.unsent; } /** - * Sets the error. + * Called when an error is encountered to deal with it. * + * @param error Error object. + * @param status HTTP status code to use rather than the default (0) for XHR errors. */ - private setError(): void { - this._request = null; - this._response = null; - this.responseHeaders = null; - this.responseParts = null; + private _handleError(error: Error | string, status = 0): void { + this.status = status; + this.statusText = error.toString(); + this.responseText = error instanceof Error ? error.stack : ''; + this._errorFlag = true; + this._setState(XMLHttpRequestReadyStateEnum.done); } /** - * Parses the response headers. + * Changes readyState and calls onreadystatechange. * - * @param response The response. + * @param int state New state + * @param state */ - private parseResponseHeaders(response: IncomingMessage): void { - this.responseHeaders = {}; - for (const name in response.headers) { - const loweredName = name.toLowerCase(); - if (this.privateHeaders[loweredName]) { - continue; - } - this.responseHeaders[loweredName] = response.headers[name]; + private _setState(state: XMLHttpRequestReadyStateEnum): void { + if ( + this.readyState === state || + (this.readyState === XMLHttpRequestReadyStateEnum.unsent && this._abortedFlag) + ) { + return; } - if (this.mimeOverride != null) { - this.responseHeaders['content-type'] = this.mimeOverride; + + this.readyState = state; + + if ( + this._settings.async || + this.readyState < XMLHttpRequestReadyStateEnum.opened || + this.readyState === XMLHttpRequestReadyStateEnum.done + ) { + this.dispatchEvent(new Event('readystatechange')); } - } - /** - * Parses the response. - * - */ - private parseResponse(): void { - const buffer = Buffer.concat(this.responseParts); - this.responseParts = null; + if (this.readyState === XMLHttpRequestReadyStateEnum.done) { + let fire: Event; - switch (this.responseType) { - case 'json': - this.responseText = null; - try { - this.response = JSON.parse(buffer.toString('utf-8')); - } catch { - this.response = null; - } - break; - case 'buffer': - this.responseText = null; - this.response = buffer; - break; - case 'arraybuffer': - this.responseText = null; - this.response = copyToArrayBuffer(buffer); - break; - case 'blob': - this.responseText = null; - this.response = new Blob([new Uint8Array(buffer)], { - type: this.mimeOverride || this.responseHeaders['content-type'] || '' - }); - break; - case 'document': - // TODO: MimeType parse not yet supported. - break; - case 'text': - default: - try { - this.responseText = buffer.toString(this.parseResponseEncoding()); - } catch { - this.responseText = buffer.toString('binary'); - } - this.response = this.responseText; - break; + if (this._abortedFlag) { + fire = new Event('abort'); + } else if (this._errorFlag) { + fire = new Event('error'); + } else { + fire = new Event('load'); + } + + this.dispatchEvent(fire); + this.dispatchEvent(new Event('loadend')); } - return; } /** - * Parses the response encoding. + * Default request headers. * + * @returns Default request headers. */ - private parseResponseEncoding(): string { - const charset = /;\s*charset=(.*)$/.exec(this.responseHeaders['content-type'] || ''); - return Array.isArray(charset) ? charset[1] : 'utf-8'; - } - - public _syncGetError(): Error { - return this._error; - } - public _syncSetErrorString(error: string): void { - this._error = error; - } - - private _serialParams(): string { - const { _defaultView } = XMLHttpRequest; - const serials = { - sync: this.sync, - withCredentials: this.withCredentials, - mimeType: this.mimeOverride, - username: this.url.username, - password: this.url.password, - auth: this.auth, - method: this.method, - responseType: this.responseType, - headers: this.headers, - uri: this.url.href, - timeout: this.timeout, - body: this.body, - - cookie: _defaultView.document.cookie, - origin: _defaultView.location.href + private _getDefaultRequestHeaders(): { [key: string]: string } { + return { + accept: '*/*', + refered: this._ownerDocument.defaultView.location.origin, + 'user-agent': this._ownerDocument.defaultView.navigator.userAgent, + cookie: this._ownerDocument.defaultView.document.cookie }; - return JSON.stringify(serials); } } diff --git a/packages/happy-dom/src/xml-http-request/XMLHttpRequestEventTarget.ts b/packages/happy-dom/src/xml-http-request/XMLHttpRequestEventTarget.ts index c04e4f4b3..90cb15e1e 100644 --- a/packages/happy-dom/src/xml-http-request/XMLHttpRequestEventTarget.ts +++ b/packages/happy-dom/src/xml-http-request/XMLHttpRequestEventTarget.ts @@ -6,12 +6,12 @@ export type ProgressEventListener = (event: ProgressEvent) => void; /** * References: https://xhr.spec.whatwg.org/#xmlhttprequesteventtarget. */ -export class XMLHttpRequestEventTarget extends EventTarget { - public onloadstart: ProgressEventListener | null; - public onprogress: ProgressEventListener | null; - public onabort: ProgressEventListener | null; - public onerror: ProgressEventListener | null; - public onload: ProgressEventListener | null; - public ontimeout: ProgressEventListener | null; - public onloadend: ProgressEventListener | null; +export default class XMLHttpRequestEventTarget extends EventTarget { + public onloadstart: ProgressEventListener | null = null; + public onprogress: ProgressEventListener | null = null; + public onabort: (event: ProgressEvent) => void | null = null; + public onerror: ProgressEventListener | null = null; + public onload: ProgressEventListener | null = null; + public ontimeout: ProgressEventListener | null = null; + public onloadend: ProgressEventListener | null = null; } diff --git a/packages/happy-dom/src/xml-http-request/XMLHttpRequestReadyStateEnum.ts b/packages/happy-dom/src/xml-http-request/XMLHttpRequestReadyStateEnum.ts new file mode 100644 index 000000000..3996ceb6b --- /dev/null +++ b/packages/happy-dom/src/xml-http-request/XMLHttpRequestReadyStateEnum.ts @@ -0,0 +1,9 @@ +enum XMLHttpRequestReadyStateEnum { + unsent = 0, + opened = 1, + headersRecieved = 2, + loading = 3, + done = 4 +} + +export default XMLHttpRequestReadyStateEnum; diff --git a/packages/happy-dom/src/xml-http-request/XMLHttpRequestSyncWorker.ts b/packages/happy-dom/src/xml-http-request/XMLHttpRequestSyncWorker.ts deleted file mode 100644 index ed97d8eaa..000000000 --- a/packages/happy-dom/src/xml-http-request/XMLHttpRequestSyncWorker.ts +++ /dev/null @@ -1,73 +0,0 @@ -import Window from '../window/Window'; -import * as util from 'util'; - -const window = new Window(); -const xhr = new window.XMLHttpRequest(); - -const chunks = []; - -process.stdin.on('data', (chunk) => { - chunks.push(chunk); -}); - -process.stdin.on('end', () => { - const buffer = Buffer.concat(chunks); - - const serials = JSON.parse(buffer.toString()); - if (serials.body && serials.body.type === 'Buffer' && serials.body.data) { - serials.body = Buffer.from(serials.body.data); - } - if (serials.origin) { - window.location.href = serials.origin; - } - - if (serials.cookie) { - window.document.cookie = serials.cookie; - } - - xhr.overrideMimeType(serials.mimeType); - xhr.open(serials.method, serials.uri, true, serials.user, serials.password); - if (serials.headers) { - Object.keys(serials.headers).forEach((key) => { - xhr.setRequestHeader(key, serials.headers[key]); - }); - } - - - xhr.timeout = serials.timeout; - - try { - xhr.addEventListener('loadend', () => { - if (xhr._syncGetError()) { - const err = xhr._syncGetError(); - xhr._syncSetErrorString(err.stack || util.inspect(err)); - } - - process.stdout.write( - JSON.stringify({ - responseURL: xhr.responseUrl, - responseText: xhr.responseText, - status: xhr.status, - statusText: xhr.statusText, - }), - () => { - process.exit(0); - } - ); - }); - - xhr.send(serials.body); - } catch (error) { - // Properties.error += error.stack || util.inspect(error); - process.stdout.write( - JSON.stringify({ - responseURL: xhr.responseUrl, - status: xhr.status, - statusText: xhr.statusText - }), - () => { - process.exit(0); - } - ); - } -}); diff --git a/packages/happy-dom/src/xml-http-request/XMLHttpRequestUpload.ts b/packages/happy-dom/src/xml-http-request/XMLHttpRequestUpload.ts index da9c8c710..d0d4c5dcc 100644 --- a/packages/happy-dom/src/xml-http-request/XMLHttpRequestUpload.ts +++ b/packages/happy-dom/src/xml-http-request/XMLHttpRequestUpload.ts @@ -1,86 +1,6 @@ -import { XMLHttpRequestEventTarget } from './XMLHttpRequestEventTarget'; -import { ClientRequest } from 'http'; +import XMLHttpRequestEventTarget from './XMLHttpRequestEventTarget'; /** * References: https://xhr.spec.whatwg.org/#xmlhttprequestupload. */ -export default class XMLHttpRequestUpload extends XMLHttpRequestEventTarget { - private _contentType: string | null = null; - private _body = null; - - /** - * Create a new XMLHttpRequestUpload object. - */ - constructor() { - super(); - this.reset(); - } - - /** - * Reset the upload. - */ - public reset(): void { - this._contentType = null; - this._body = null; - } - - /** - * Set data to be sent. - * - * @param data The data to be sent. - */ - public setData(data?: string | Buffer | ArrayBuffer | ArrayBufferView): void { - if (data == null) { - return; - } - - if (typeof data === 'string') { - if (data.length !== 0) { - this._contentType = 'text/plain;charset=UTF-8'; - } - this._body = Buffer.from(data, 'utf-8'); - } else if (Buffer.isBuffer(data)) { - this._body = data; - } else if (data instanceof ArrayBuffer) { - const body = Buffer.alloc(data.byteLength); - const view = new Uint8Array(data); - body.set(view); - this._body = body; - } else if (data.buffer && data.buffer instanceof ArrayBuffer) { - const body = Buffer.alloc(data.byteLength); - const offset = data.byteOffset; - const view = new Uint8Array(data.buffer); - body.set(view, offset); - this._body = body; - } else { - throw new Error(`Unsupported send() data ${data}`); - } - } - - /** - * Finalize headers. - * - * @param headers The headers to be finalized. - * @param loweredHeaders The lowered headers to be finalized. - */ - public finalizeHeaders(headers: object, loweredHeaders: object): void { - if (this._contentType && !loweredHeaders['content-type']) { - headers['Content-Type'] = this._contentType; - } - if (this._body) { - headers['Content-Length'] = this._body.length.toString(); - } - } - - /** - * Start upload. - * - * @param request The request. - */ - public startUpload(request: ClientRequest): void { - if (this._body) { - request.write(this._body); - } - request.end(); - } -} +export default class XMLHttpRequestUpload extends XMLHttpRequestEventTarget {}