diff --git a/.dprint.json b/.dprint.json
index d20b1673ba..1decc7863e 100644
--- a/.dprint.json
+++ b/.dprint.json
@@ -13,7 +13,9 @@
"associations": "**/*.rs",
"rustfmt": "rustfmt --config imports_granularity=item"
},
- "includes": ["**/*.{ts,tsx,js,jsx,json,md,toml,rs}"],
+ "includes": [
+ "**/*.{ts,tsx,js,jsx,json,md,toml,rs}"
+ ],
"excludes": [
".cargo_home",
".git",
@@ -33,6 +35,8 @@
"cli/tests/testdata/byte_order_mark.ts",
"cli/tests/testdata/encoding",
"cli/tests/testdata/fmt/*",
+ "cli/tests/testdata/lint/glob/*",
+ "cli/tests/testdata/test/glob/*",
"cli/tests/testdata/import_assertions/json_with_shebang.json",
"cli/tests/testdata/run/inline_js_source_map*",
"cli/tests/testdata/malformed_config/*",
@@ -46,14 +50,15 @@
"test_util/wpt",
"third_party",
"tools/node_compat/TODO.md",
- "tools/node_compat/versions",
+ "tools/node_compat/node",
"tools/wpt/expectation.json",
- "tools/wpt/manifest.json"
+ "tools/wpt/manifest.json",
+ "ext/websocket/autobahn/reports"
],
"plugins": [
- "https://plugins.dprint.dev/typescript-0.84.0.wasm",
- "https://plugins.dprint.dev/json-0.17.0.wasm",
- "https://plugins.dprint.dev/markdown-0.15.2.wasm",
+ "https://plugins.dprint.dev/typescript-0.85.0.wasm",
+ "https://plugins.dprint.dev/json-0.17.3.wasm",
+ "https://plugins.dprint.dev/markdown-0.15.3.wasm",
"https://plugins.dprint.dev/toml-0.5.4.wasm",
"https://plugins.dprint.dev/exec-0.3.5.json@d687dda57be0fe9a0088ccdaefa5147649ff24127d8b3ea227536c68ee7abeab"
]
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 44c670d275..e6e5a41f03 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,5 +1,5 @@
\n\n\n\nImplementation of function.prototype.bind\n\n## Example\n\nI mainly do this for unit tests I run on phantomjs.\nPhantomJS does not have Function.prototype.bind :(\n\n```js\nFunction.prototype.bind = require(\"function-bind\")\n```\n\n## Installation\n\n`npm install function-bind`\n\n## Contributors\n\n - Raynos\n\n## MIT Licenced\n\n [travis-svg]: https://travis-ci.org/Raynos/function-bind.svg\n [travis-url]: https://travis-ci.org/Raynos/function-bind\n [npm-badge-svg]: https://badge.fury.io/js/function-bind.svg\n [npm-url]: https://npmjs.org/package/function-bind\n [5]: https://coveralls.io/repos/Raynos/function-bind/badge.png\n [6]: https://coveralls.io/r/Raynos/function-bind\n [7]: https://gemnasium.com/Raynos/function-bind.png\n [8]: https://gemnasium.com/Raynos/function-bind\n [deps-svg]: https://david-dm.org/Raynos/function-bind.svg\n [deps-url]: https://david-dm.org/Raynos/function-bind\n [dev-deps-svg]: https://david-dm.org/Raynos/function-bind/dev-status.svg\n [dev-deps-url]: https://david-dm.org/Raynos/function-bind#info=devDependencies\n [11]: https://ci.testling.com/Raynos/function-bind.png\n [12]: https://ci.testling.com/Raynos/function-bind\n","maintainers":[{"name":"raynos","email":"raynos2@gmail.com"},{"name":"ljharb","email":"ljharb@gmail.com"}],"time":{"modified":"2022-06-18T04:14:28.973Z","created":"2013-06-16T23:25:41.232Z","0.1.0":"2013-06-16T23:25:42.888Z","1.0.0":"2014-08-09T17:02:51.069Z","1.0.1":"2014-10-03T07:38:13.045Z","1.0.2":"2014-10-05T07:23:52.930Z","1.1.0":"2016-02-14T08:28:42.411Z","1.1.1":"2017-08-28T07:51:35.937Z"},"author":{"name":"Raynos","email":"raynos2@gmail.com"},"repository":{"type":"git","url":"git://github.com/Raynos/function-bind.git"},"homepage":"https://github.com/Raynos/function-bind","keywords":["function","bind","shim","es5"],"contributors":[{"name":"Raynos"},{"name":"Jordan Harband","url":"https://github.com/ljharb"}],"bugs":{"url":"https://github.com/Raynos/function-bind/issues","email":"raynos2@gmail.com"},"readmeFilename":"README.md","users":{},"license":"MIT"}
\ No newline at end of file
diff --git a/cli/tests/testdata/npm/registry/get-intrinsic/get-intrinsic-1.2.0.tgz b/cli/tests/testdata/npm/registry/get-intrinsic/get-intrinsic-1.2.0.tgz
new file mode 100644
index 0000000000..b55e814a0a
Binary files /dev/null and b/cli/tests/testdata/npm/registry/get-intrinsic/get-intrinsic-1.2.0.tgz differ
diff --git a/cli/tests/testdata/npm/registry/get-intrinsic/registry.json b/cli/tests/testdata/npm/registry/get-intrinsic/registry.json
new file mode 100644
index 0000000000..b09a472f65
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/get-intrinsic/registry.json
@@ -0,0 +1 @@
+{"_id":"get-intrinsic","_rev":"8-56a236fdf4f8cc6cb2833aa6d5ee81d7","name":"get-intrinsic","dist-tags":{"latest":"1.2.0"},"versions":{"1.0.0":{"name":"get-intrinsic","version":"1.0.0","description":"Get and robustly cache all JS language-level intrinsics at first require time","main":"index.js","exports":{".":[{"default":"./index.js"},"./index.js"]},"scripts":{"lint":"eslint --ext=.js,.mjs .","pretest":"npm run lint","tests-only":"tape 'test/*'","coverage":"nyc npm run tests-only","test":"npm run tests-only","posttest":"aud --production","version":"auto-changelog && git add CHANGELOG.md","postversion":"auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""},"repository":{"type":"git","url":"git+https://github.com/ljharb/get-intrinsic.git"},"keywords":["javascript","ecmascript","es","js","intrinsic","getintrinsic","es-abstract"],"author":{"name":"Jordan Harband","email":"ljharb@gmail.com"},"funding":{"url":"https://github.com/sponsors/ljharb"},"license":"MIT","bugs":{"url":"https://github.com/ljharb/get-intrinsic/issues"},"homepage":"https://github.com/ljharb/get-intrinsic#readme","devDependencies":{"@ljharb/eslint-config":"^17.2.0","aud":"^1.1.2","auto-changelog":"^2.2.1","es-abstract":"^1.18.0-next.1","eslint":"^7.12.1","foreach":"^2.0.5","has-bigints":"^1.0.0","make-async-function":"^1.0.0","make-async-generator-function":"^1.0.0","make-generator-function":"^2.0.0","nyc":"^10.3.2","object-inspect":"^1.8.0","tape":"^5.0.1"},"auto-changelog":{"output":"CHANGELOG.md","template":"keepachangelog","unreleased":false,"commitLimit":false,"backfillLimit":false,"hideCredit":true},"gitHead":"516f403fe75287a2a80a8d48c2061f6b3238ec0c","_id":"get-intrinsic@1.0.0","_nodeVersion":"14.15.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-EMuu0ud8uAP4Zs6tQqMeHiY1PbIBDcZ92QVxqeLfqTMbyvqcDbrtHjfu0RWh8QaUNJ3lP1DSX3J2okgj9JE47g==","shasum":"035ccf14a00ae2eb3d110a00fcd10e74706a8fe7","tarball":"http://localhost:4545/npm/registry/get-intrinsic/get-intrinsic-1.0.0.tgz","fileCount":11,"unpackedSize":25104,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfnCtMCRA9TVsSAnZWagAAwm0P/0h8E1pcOVKKP6XQ6No4\n9tdWqfwRUlG8RTYs8sXW8g2qL3PxQdM1ql5GztOTUSstrtEE2sux290V6w1B\n829I8YHJbw667RuqIOuUBnXjaFm3Eb6S1Tvhvlbff0MtEoP9dZwgvqHn6yLx\niIBIRDCEJhuqrfVmjbpy6hLDEsxhaWsSxPj81gm+aHY6xVb4f/dZvrDp8R9j\nlaEwsE7EK+cEn3ifTQYYHlv8an9QkPFTHDLjeZ+wdWBnut+tepMeFM+ZjG+d\ngdTg2IeNfXFw/QSU5eDQtjqHZ2Fv2T4fFn2blhkrIbEMmwxczzM6QuQiOGc8\n1suIs9vDdt8qq6h8ESs9hr5I2hgE3M4Xxt5ziZ95TifSDRNyyQGbMy5vj3CY\n0z2e5M6zr5b2mkiWm0A5tZI4Mdy/2XrpJxTE6/opYgvA5mQ0GIYzO7r1Zt+G\nmHD/MDeTe2WxBWizo3nv0IGRvZeHZ/JjcRHdHeRAq+rqJ6o4hvYanxfoGlGA\njCUXYsZzR2XLfxBiTeSUO9VQ5YSBtsfU+egeRNwOw5PwxpGwfW4VUVOPHwHJ\n5dHlRGuWHDOn+4uF+09o5B70By6rcGZsHV62jX5ci5JclHswBdrvcftucfyG\nyR2qyuEnxq7O+S2D/uMylQLqTdCdJ6Bf58TKGSzpsp45oWrSmIsSTdiVWIsG\nz7pB\r\n=DQ3f\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEehDACke//ohQCAy5pJo/R/9J5UGrufkNBiQJqe3y2DAiBC7txNPrBmQB4PjK/Ydow1627eRDEIl0wz1IkhWkBAkw=="}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/get-intrinsic_1.0.0_1604070219549_0.3039159077605891"},"_hasShrinkwrap":false},"1.0.1":{"name":"get-intrinsic","version":"1.0.1","description":"Get and robustly cache all JS language-level intrinsics at first require time","main":"index.js","exports":{".":[{"default":"./index.js"},"./index.js"]},"scripts":{"lint":"eslint --ext=.js,.mjs .","pretest":"npm run lint","tests-only":"nyc tape 'test/*'","test":"npm run tests-only","posttest":"aud --production","version":"auto-changelog && git add CHANGELOG.md","postversion":"auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""},"repository":{"type":"git","url":"git+https://github.com/ljharb/get-intrinsic.git"},"keywords":["javascript","ecmascript","es","js","intrinsic","getintrinsic","es-abstract"],"author":{"name":"Jordan Harband","email":"ljharb@gmail.com"},"funding":{"url":"https://github.com/sponsors/ljharb"},"license":"MIT","bugs":{"url":"https://github.com/ljharb/get-intrinsic/issues"},"homepage":"https://github.com/ljharb/get-intrinsic#readme","devDependencies":{"@ljharb/eslint-config":"^17.2.0","aud":"^1.1.2","auto-changelog":"^2.2.1","es-abstract":"^1.18.0-next.1","es-value-fixtures":"^1.0.0","eslint":"^7.12.1","foreach":"^2.0.5","has-bigints":"^1.0.0","make-async-function":"^1.0.0","make-async-generator-function":"^1.0.0","make-generator-function":"^2.0.0","nyc":"^10.3.2","object-inspect":"^1.8.0","tape":"^5.0.1"},"auto-changelog":{"output":"CHANGELOG.md","template":"keepachangelog","unreleased":false,"commitLimit":false,"backfillLimit":false,"hideCredit":true},"dependencies":{"function-bind":"^1.1.1","has":"^1.0.3","has-symbols":"^1.0.1"},"gitHead":"1802957d1ff6a04965505f54c3d354ad7fa31034","_id":"get-intrinsic@1.0.1","_nodeVersion":"14.15.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==","shasum":"94a9768fcbdd0595a1c9273aacf4c89d075631be","tarball":"http://localhost:4545/npm/registry/get-intrinsic/get-intrinsic-1.0.1.tgz","fileCount":12,"unpackedSize":26012,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfnPbUCRA9TVsSAnZWagAAO2UP/37xSp1p1f50Pt6yyL4k\n1BKcgA+OfPCEhFnJA1AKqYeL8rVBLr7VoSvMzTQ9JonFIIXLlnlVe8P91KWE\n1AXoYJr/dW8ZG7vHs37jK7aiEweyYlgLebWPOM2T2bU0WFoaaIws1fa+TwTS\neCqY8Q7XysXV3syWXX1El/2TIXzSVa8g8gOVJy/j8j+fthSAPD0H6ZTCvYQ6\nPSWIFAYhRIWXLGel3T/TE1p61AWZuEtf8B+e6K8hPiMuzhNjODCBqJQV246D\nPznhAbJV81wNIdM0ohuT19+t7GqjjKbKKMpU0LZzSCjZF3Q+zLI4H+qMY0Bl\nHFiqspAfS0r/wHWoBkzODoHWMduJ/JPtE/uee8ae92iC9fR9Y8fSOWXTt07W\nFWSGyLyJ6CQS7d+dJwFb+2cQNckV/9VKu+y58z+i6x6/FExmHNBdYt4ps3ju\nH89DQEmfq5wyLcceng9K0a7A6vfLM6MvEk8FugXVhGORioFOkscE3f8gS7Sc\nIzbl739iiG3oGvNzRgF229t2xwUZXVNqGJ4Sg3AQM/RX75+Mu1Jlx52z0ECY\nLZGX16A+J3N955DxJktRA1l7RA+zihIs1fZKHm+fErP547biV5p+TNocKrrn\nwghypBweNbcNkzNds6qczoB/3Vsc2OxHaVUBWNleqmlrQ6Qk1AA4ZRIdhQZ8\njWu7\r\n=BhVO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFzb1R9CMnRu3GNwT893R3yms0wnrxROjmNn7s1aWEdLAiEAgP3VkUFew7/H+j05N3mW3XntPRU+Smw2z+q8kZl3CC4="}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/get-intrinsic_1.0.1_1604122323843_0.022947285149020447"},"_hasShrinkwrap":false},"1.0.2":{"name":"get-intrinsic","version":"1.0.2","description":"Get and robustly cache all JS language-level intrinsics at first require time","main":"index.js","exports":{".":[{"default":"./index.js"},"./index.js"]},"scripts":{"lint":"eslint --ext=.js,.mjs .","pretest":"npm run lint","tests-only":"nyc tape 'test/*'","test":"npm run tests-only","posttest":"aud --production","version":"auto-changelog && git add CHANGELOG.md","postversion":"auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""},"repository":{"type":"git","url":"git+https://github.com/ljharb/get-intrinsic.git"},"keywords":["javascript","ecmascript","es","js","intrinsic","getintrinsic","es-abstract"],"author":{"name":"Jordan Harband","email":"ljharb@gmail.com"},"funding":{"url":"https://github.com/sponsors/ljharb"},"license":"MIT","bugs":{"url":"https://github.com/ljharb/get-intrinsic/issues"},"homepage":"https://github.com/ljharb/get-intrinsic#readme","devDependencies":{"@ljharb/eslint-config":"^17.3.0","aud":"^1.1.3","auto-changelog":"^2.2.1","es-abstract":"^1.18.0-next.1","es-value-fixtures":"^1.0.0","eslint":"^7.15.0","foreach":"^2.0.5","has-bigints":"^1.0.1","make-async-function":"^1.0.0","make-async-generator-function":"^1.0.0","make-generator-function":"^2.0.0","nyc":"^10.3.2","object-inspect":"^1.9.0","tape":"^5.0.1"},"auto-changelog":{"output":"CHANGELOG.md","template":"keepachangelog","unreleased":false,"commitLimit":false,"backfillLimit":false,"hideCredit":true},"dependencies":{"function-bind":"^1.1.1","has":"^1.0.3","has-symbols":"^1.0.1"},"gitHead":"eec980691af2fafb4e0d9207e473c9e1eb7995e6","_id":"get-intrinsic@1.0.2","_nodeVersion":"14.15.1","_npmVersion":"6.14.8","dist":{"integrity":"sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==","shasum":"6820da226e50b24894e08859469dc68361545d49","tarball":"http://localhost:4545/npm/registry/get-intrinsic/get-intrinsic-1.0.2.tgz","fileCount":13,"unpackedSize":92891,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf3FiZCRA9TVsSAnZWagAAhqwP/ApVcuwN67ClrOqU4sXI\nq1LZjlVXwkM54mRbfqChOsZUZsxW1V8xCpeaaZE0h2JKH+PnzS/GUvAcd0iA\nXbyjsIfvc66lu0bBIMKrP/zLMQ7LIm3q9Vr7iLIWi7LXXCGNqhtNx0rGgPVi\npk6c0o6MUK6Tr1RGtdpQZVnJqF9veFC6RVApu+xFYt8QWXuYDTGKrS093aRU\noI3SmbrjLSlskjXSVREGFja/L5JsiHbds7meSHPWdF57AhatrEb9X8h93fdy\n4Pz1yUKjd1QFXoAg4Pw+TLRPO0VN4JYeHWwaQ+mmOl5RViz+Yiq6joR+Fo1r\nsdSWHdijgx3XzGH4nbiP9mjR/TcypqZQeEP1H5TDZfDSSRSg9Eus0BQuHwOa\n9kNLDQywTsBBsB8S5tlJ4QSrTSn6Y8q5RsQIl9IIHwAUW/0GyiAUfCJMYCiE\n9A13GnS6ZPJEdJu960P7ZlbvnfpPbiQaMOMyC6kXOfACBkcxhhc4SofQkMZw\n5v7Xjg3Nz6inEnpbXuuU3Tj3WmDMMWoyX06sDbv50X/gzciNSy6ptcJgultt\n8aGrP+i/QWfHzGdguIVlz+2wf5kYG1jRyvbKsVZKJ2wvnnxUC8Ji0yjFO6j/\nKuwcsva60yDyaAjpy4Sbw7WSE1etufVa0rXf96788xqPhAhvFcGzzVKiOigr\nUP2R\r\n=SifI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDLO/WsMu1yogrwHBInw7hC3MUpX9G+E/a0CiaubHBZGAIhAOSWmWgEOV39qmGn7YkvbHgzc+IxhjCPiTch0LAig1NA"}]},"_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"directories":{},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/get-intrinsic_1.0.2_1608276120877_0.84071357918607"},"_hasShrinkwrap":false},"1.1.0":{"name":"get-intrinsic","version":"1.1.0","description":"Get and robustly cache all JS language-level intrinsics at first require time","main":"index.js","exports":{".":[{"default":"./index.js"},"./index.js"]},"scripts":{"lint":"eslint --ext=.js,.mjs .","pretest":"npm run lint","tests-only":"nyc tape 'test/**/*.js'","test":"npm run tests-only","posttest":"aud --production","version":"auto-changelog && git add CHANGELOG.md","postversion":"auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""},"repository":{"type":"git","url":"git+https://github.com/ljharb/get-intrinsic.git"},"keywords":["javascript","ecmascript","es","js","intrinsic","getintrinsic","es-abstract"],"author":{"name":"Jordan Harband","email":"ljharb@gmail.com"},"funding":{"url":"https://github.com/sponsors/ljharb"},"license":"MIT","bugs":{"url":"https://github.com/ljharb/get-intrinsic/issues"},"homepage":"https://github.com/ljharb/get-intrinsic#readme","devDependencies":{"@ljharb/eslint-config":"^17.5.0","aud":"^1.1.3","auto-changelog":"^2.2.1","call-bind":"^1.0.2","es-abstract":"^1.18.0-next.2","es-value-fixtures":"^1.0.0","eslint":"^7.18.0","foreach":"^2.0.5","has-bigints":"^1.0.1","make-async-function":"^1.0.0","make-async-generator-function":"^1.0.0","make-generator-function":"^2.0.0","nyc":"^10.3.2","object-inspect":"^1.9.0","tape":"^5.1.1"},"auto-changelog":{"output":"CHANGELOG.md","template":"keepachangelog","unreleased":false,"commitLimit":false,"backfillLimit":false,"hideCredit":true},"dependencies":{"function-bind":"^1.1.1","has":"^1.0.3","has-symbols":"^1.0.1"},"gitHead":"aaaaa0d5cd17d4b0b274cdaa1f7f3e6007fc9e59","_id":"get-intrinsic@1.1.0","_nodeVersion":"14.15.4","_npmVersion":"6.14.10","dist":{"integrity":"sha512-M11rgtQp5GZMZzDL7jLTNxbDfurpzuau5uqRWDPvlHjfvg3TdScAZo96GLvhMjImrmR8uAt0FS2RLoMrfWGKlg==","shasum":"892e62931e6938c8a23ea5aaebcfb67bd97da97e","tarball":"http://localhost:4545/npm/registry/get-intrinsic/get-intrinsic-1.1.0.tgz","fileCount":10,"unpackedSize":29482,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgD0SqCRA9TVsSAnZWagAAVjQP/276R/hJLC3r7gUvPhUj\n8mwJCVpdCjIzaf4fBVvu0mB4cXe4G+t+N1w3JG9wkBqTpoHjNRzUUxhcFYeX\nnos9b4CeNftDFVgwixFHcRS0Nk0A6SUSj7jdmLiyrM3Lc0KVrMfe7G7ECeSV\nKGWViXtP8oEZJ6FZURMS9yMraQzeh5ChjcGKXsX0Jf0IpUXlDaib0ElChkLr\nN6iXsGveM9tYf15JjBW/gyJXhMPQLGE37jdCBkoW6WeOT7twWr0KDcmn6QHg\n775CZxPl1VJpEiXIoSk0PnAxRN95MIRZvdQ9k1ctSuE5kpErrTZk7j7i4i2T\n5bbOOcLvxX+StCvNtOh7M52RyDxPaagFSoKaNHxmW4e2muDDuvWRPA3n/FI4\nuXw1J1Lb1lvbhx/L9wLNN9SdPFcFOA2+t23SJE/F8abLHNsdhoBlCyoCmULL\nIKdrVXWxFbopnQF3n18ajCIDJ9E4J1vr6XU7+xYc7Pl1Nuel9AfQU5PuLAFy\nj0ziiUntUOuYWC0xHuhnYVHDWmU+1UB5IoxlQi9uAYp0/RBWg4mmAcQ2dK9B\nJDxOaa/Rmkp1F/5htSqD6hvfAH8Pv/SpEglGRUPH4mmHF183iLEwls2GfOAJ\nh2Baw9u2yX7COfPYqDG2MVbwB6wafDPgUKNNCZ+FjMRgLCt9VrzwVjPu9QF/\n5V7l\r\n=QaDr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA+e/mSh+QgkqBLYqQTWcVvq5FQ05WbdMfaLjOMgM3N5AiB29JZnuXxh8SsBV17yu9nizQr5iwkWwK5HPPpe9Dx8Vg=="}]},"_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"directories":{},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/get-intrinsic_1.1.0_1611613354056_0.8648044903277086"},"_hasShrinkwrap":false},"1.1.1":{"name":"get-intrinsic","version":"1.1.1","description":"Get and robustly cache all JS language-level intrinsics at first require time","main":"index.js","exports":{".":[{"default":"./index.js"},"./index.js"],"./package.json":"./package.json"},"scripts":{"prelint":"evalmd README.md","lint":"eslint --ext=.js,.mjs .","pretest":"npm run lint","tests-only":"nyc tape 'test/**/*.js'","test":"npm run tests-only","posttest":"aud --production","version":"auto-changelog && git add CHANGELOG.md","postversion":"auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""},"repository":{"type":"git","url":"git+https://github.com/ljharb/get-intrinsic.git"},"keywords":["javascript","ecmascript","es","js","intrinsic","getintrinsic","es-abstract"],"author":{"name":"Jordan Harband","email":"ljharb@gmail.com"},"funding":{"url":"https://github.com/sponsors/ljharb"},"license":"MIT","bugs":{"url":"https://github.com/ljharb/get-intrinsic/issues"},"homepage":"https://github.com/ljharb/get-intrinsic#readme","devDependencies":{"@ljharb/eslint-config":"^17.5.0","aud":"^1.1.3","auto-changelog":"^2.2.1","call-bind":"^1.0.2","es-abstract":"^1.18.0-next.2","es-value-fixtures":"^1.0.0","eslint":"^7.19.0","evalmd":"^0.0.19","foreach":"^2.0.5","has-bigints":"^1.0.1","make-async-function":"^1.0.0","make-async-generator-function":"^1.0.0","make-generator-function":"^2.0.0","nyc":"^10.3.2","object-inspect":"^1.9.0","tape":"^5.1.1"},"auto-changelog":{"output":"CHANGELOG.md","template":"keepachangelog","unreleased":false,"commitLimit":false,"backfillLimit":false,"hideCredit":true},"dependencies":{"function-bind":"^1.1.1","has":"^1.0.3","has-symbols":"^1.0.1"},"gitHead":"efa0daa5166f1a06658001e34f49b5f1185786eb","_id":"get-intrinsic@1.1.1","_nodeVersion":"14.15.4","_npmVersion":"6.14.10","dist":{"integrity":"sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==","shasum":"15f59f376f855c446963948f0d24cd3637b4abc6","tarball":"http://localhost:4545/npm/registry/get-intrinsic/get-intrinsic-1.1.1.tgz","fileCount":10,"unpackedSize":32513,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgGroRCRA9TVsSAnZWagAAAaMP/1kYGifz/BvcSYhnlVk+\nSnCwbyuOaTefaIpH15yyWb9sjo+1fgUw4Ej3GmVdpmyW45Tj0WePwRWhbpok\n1aKIx3P8/q8m95HymXcR50VRByFyxpNFxtWuo674yTzvYxN1+QqXVSO7xeLI\nL+bRYOScvb+f5DI8t5LqhZlvQgfiqyWXZI4L+gbwfIIrE7EUg5DZJZrzIBOY\n5SExvgueChcIptQgu8ppE5kADlGqmTHUBt3P68EU5HRc5Z/LN5csgTu63VkJ\nxx3pTXa/Q672C9qj1CqedmughzgkfBjSuKOhbQWgILCbNy0A6TKKVirpc2fB\nuI0f4vWTf1ImGrspsfIH2IR4SQqMmVy8qpgwG/YtU3q9Si9pOcXQ1q+JnyD6\nDoLaiTEVPC8ks/bKGjtNBDUmlnEuyluaaFuK3cfJQMGp2n+FNLXI5LBz9uoR\nkpqUHNJBFJ9HbbMfBUmTS3K3duAkgOR+izFQgAJJWzYbuAvM7GGAoy1eQUrY\nuD1tAQglMbB0YwsjnDxvGcV32iFoMttrcXb5xKUOlVaFMD2D9PDryeO/gu0N\nm3wDWCKhmMjGNWV6WA9q0mD6YRCPHZUwmb4xSFdz/i1MP4iVjVKc1tz6RAiT\nLqxKnm4uPjTsPPGrXWYdRs5EEF2/QHcmCex2kwk5Ul4fsVayaNOAzB3F+iSp\nbWf1\r\n=5aSN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCNo13JEcIXzNTEsjtVSMSsBL9CAqU56ZzTh56ilFAwmgIgCFy2IWS7fXyDYWF1/aSqiRCTW9wVIONaN0YUk7J0diM="}]},"_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"directories":{},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/get-intrinsic_1.1.1_1612364304893_0.18784978138621788"},"_hasShrinkwrap":false},"1.1.2":{"name":"get-intrinsic","version":"1.1.2","description":"Get and robustly cache all JS language-level intrinsics at first require time","main":"index.js","exports":{".":[{"default":"./index.js"},"./index.js"],"./package.json":"./package.json"},"scripts":{"prepack":"npmignore --auto --commentLines=autogenerated","prepublish":"not-in-publish || npm run prepublishOnly","prepublishOnly":"safe-publish-latest","prelint":"evalmd README.md","lint":"eslint --ext=.js,.mjs .","pretest":"npm run lint","tests-only":"nyc tape 'test/**/*.js'","test":"npm run tests-only","posttest":"aud --production","version":"auto-changelog && git add CHANGELOG.md","postversion":"auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""},"repository":{"type":"git","url":"git+https://github.com/ljharb/get-intrinsic.git"},"keywords":["javascript","ecmascript","es","js","intrinsic","getintrinsic","es-abstract"],"author":{"name":"Jordan Harband","email":"ljharb@gmail.com"},"funding":{"url":"https://github.com/sponsors/ljharb"},"license":"MIT","bugs":{"url":"https://github.com/ljharb/get-intrinsic/issues"},"homepage":"https://github.com/ljharb/get-intrinsic#readme","devDependencies":{"@ljharb/eslint-config":"^21.0.0","aud":"^2.0.0","auto-changelog":"^2.4.0","call-bind":"^1.0.2","es-abstract":"^1.20.1","es-value-fixtures":"^1.4.1","eslint":"=8.8.0","evalmd":"^0.0.19","for-each":"^0.3.3","make-async-function":"^1.0.0","make-async-generator-function":"^1.0.0","make-generator-function":"^2.0.0","mock-property":"^1.0.0","npmignore":"^0.3.0","nyc":"^10.3.2","object-inspect":"^1.12.2","safe-publish-latest":"^2.0.0","tape":"^5.5.3"},"auto-changelog":{"output":"CHANGELOG.md","template":"keepachangelog","unreleased":false,"commitLimit":false,"backfillLimit":false,"hideCredit":true},"dependencies":{"function-bind":"^1.1.1","has":"^1.0.3","has-symbols":"^1.0.3"},"publishConfig":{"ignore":[".github/workflows"]},"gitHead":"1692762305146cdee0bd0a31cb0a57ffd9240c8c","_id":"get-intrinsic@1.1.2","_nodeVersion":"18.3.0","_npmVersion":"8.11.0","dist":{"integrity":"sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==","shasum":"336975123e05ad0b7ba41f152ee4aadbea6cf598","tarball":"http://localhost:4545/npm/registry/get-intrinsic/get-intrinsic-1.1.2.tgz","fileCount":9,"unpackedSize":36671,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGk690kzjXZ7zcAng4wWyvMEdoQ4xPaEtBm2SQIm48nMAiBm4P1A9nW2MVt9ngQfwiaKLc6wAZZBcdlpzlq8Br1v2Q=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJioLxOACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmqs3A//RY5KjVotePADUXDnTk/obYp33AmFt7aXnVRafoQNIhvDC7Ya\r\nMj9g+8NGAG2D1xBgD+Q/dhvvfZQlpuLRzkfQg4V92liFmgpoEB0ue6BP0TD8\r\n37S9yioBWG6LTJkqbvjc68V3gi3t5jWTqHJeYPW4mxJF6MMCx7m9EYWGtqUR\r\n0AnVNqH9j4SJ/X3qHach0vgsI8hnb8iXeTg8X7465MmQke+tygQbT3rYLN/L\r\nSni+uwm6EMybJ8Lh5GKq5U6aKr+inAYm/h47js4D7/A+tvfzYfWvLjr1l4J5\r\n+cMKLskFEP6g/Xz9jaYCCRxe7YGaiTmH/sUgT+kTzo2oJaYh6xd/6bgvGCut\r\nPFBBxh0lknSR1wbiQz3hcdHu42D0a9jiOmtc3DlkiRzrez6pEJMDnKu+Pbck\r\nkqhrBMLYyLYkLHJzeB07aN+KuspIZgjMJ/rSsgqla8JHv6TqWx0BbaoZ53VA\r\nPzf3fs73zh7IhNLznCQVNHR9iM0w+dUMI2n6c0QlOaimFkJ+61cHA13zU20x\r\nllmejv9s5XEkvuVSU/ibuYEkbnqDg62sWcm0HgGuL6k+RKe7Mj/gOds/Zn9n\r\ngvMIl1y7zeaIrEmQHfj6ndXAB1Mv9eIySBA4//nd+oVZLibt4pkAJIIy8xY1\r\nnHBGLFN4jrtJBI6I36xrNfrC0DVumPnTh8A=\r\n=ZCcH\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"directories":{},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/get-intrinsic_1.1.2_1654701133878_0.7209001100988714"},"_hasShrinkwrap":false},"1.1.3":{"name":"get-intrinsic","version":"1.1.3","description":"Get and robustly cache all JS language-level intrinsics at first require time","main":"index.js","exports":{".":[{"default":"./index.js"},"./index.js"],"./package.json":"./package.json"},"scripts":{"prepack":"npmignore --auto --commentLines=autogenerated","prepublish":"not-in-publish || npm run prepublishOnly","prepublishOnly":"safe-publish-latest","prelint":"evalmd README.md","lint":"eslint --ext=.js,.mjs .","pretest":"npm run lint","tests-only":"nyc tape 'test/**/*.js'","test":"npm run tests-only","posttest":"aud --production","version":"auto-changelog && git add CHANGELOG.md","postversion":"auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""},"repository":{"type":"git","url":"git+https://github.com/ljharb/get-intrinsic.git"},"keywords":["javascript","ecmascript","es","js","intrinsic","getintrinsic","es-abstract"],"author":{"name":"Jordan Harband","email":"ljharb@gmail.com"},"funding":{"url":"https://github.com/sponsors/ljharb"},"license":"MIT","bugs":{"url":"https://github.com/ljharb/get-intrinsic/issues"},"homepage":"https://github.com/ljharb/get-intrinsic#readme","devDependencies":{"@ljharb/eslint-config":"^21.0.0","aud":"^2.0.0","auto-changelog":"^2.4.0","call-bind":"^1.0.2","es-abstract":"^1.20.2","es-value-fixtures":"^1.4.2","eslint":"=8.8.0","evalmd":"^0.0.19","for-each":"^0.3.3","make-async-function":"^1.0.0","make-async-generator-function":"^1.0.0","make-generator-function":"^2.0.0","mock-property":"^1.0.0","npmignore":"^0.3.0","nyc":"^10.3.2","object-inspect":"^1.12.2","safe-publish-latest":"^2.0.0","tape":"^5.6.0"},"auto-changelog":{"output":"CHANGELOG.md","template":"keepachangelog","unreleased":false,"commitLimit":false,"backfillLimit":false,"hideCredit":true},"dependencies":{"function-bind":"^1.1.1","has":"^1.0.3","has-symbols":"^1.0.3"},"testling":{"files":"test/GetIntrinsic.js"},"publishConfig":{"ignore":[".github/workflows"]},"gitHead":"65cac0bca7cf7db4d1594bd1f7c68e921adedb5b","_id":"get-intrinsic@1.1.3","_nodeVersion":"18.9.0","_npmVersion":"8.19.1","dist":{"integrity":"sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==","shasum":"063c84329ad93e83893c7f4f243ef63ffa351385","tarball":"http://localhost:4545/npm/registry/get-intrinsic/get-intrinsic-1.1.3.tgz","fileCount":9,"unpackedSize":37128,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFx7EpcX7UchnW1MjTW4LY/IDpL1jl3H+M29ezR+WSHQAiEA7sXR/8EoSjeBOAK0Z3he//k1OtgvYgkt6hGAGtrHojM="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjIACSACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpAuA//URHhCHAEO247jjMd3BcpIpNWgbXTtnIEpHqbGulxZuzwd8hD\r\n7a9fqywLrEQq10reRxRNeS6Zk7BSv9QqwtZx7dTmi95ZxPETZvF2khJ6ggyj\r\nhAMonDjaP79Ki5Dwz/JH2WxsefDcAAPRRftEmm73oSJgt9EEdssmmAXgG5JS\r\n5OU3tCLGb4ricSaPNv2g2QDDLuLh/j6axKGn5bsQZFCvK87PV1vR/9Q6EVUz\r\nNDgWOxcgQTXgpVJYPsd6j8FiB3PiuFmd7/aLiqUMncStQDzklRHd8zUcxay3\r\n+0NplukrzPQPRDjMLuLeIX6WX+145sPZcThc7s9nrfmk2ODpDmLUYPZdki6U\r\nUBBa9aK3kDBIocvwVrleIzyY53SKvmmZ6jqmP5wS9pEWPa1gdD+VugZGazEK\r\noYK1MH77WG9fJb/2n27AWhJ/Tm9m177G+9rYQKIA+Q9JmZom+qNQviXkSkHL\r\n9MhOdjGzH0hnhX25ml81l6I2a/spKuN6RsHKNruUEUUxAyQYxIm6ZJs6D2Hy\r\nDjd+LklfZnCUsJUIJarqkB8XnRYsrKR+zrcTjxuRS0vQMBs+t/DYyXaS1k73\r\n4SR/biyt43/SOVtwZ25ThMxfGBZ+gwIqsoih3Rovs18QsrZNDyeU3fzfcTCM\r\nRioeF4ejfq26VnL5JSIEvGDWBNihLZhCw5U=\r\n=Yum9\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"directories":{},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/get-intrinsic_1.1.3_1663041682205_0.23362607287463288"},"_hasShrinkwrap":false},"1.2.0":{"name":"get-intrinsic","version":"1.2.0","description":"Get and robustly cache all JS language-level intrinsics at first require time","main":"index.js","exports":{".":[{"default":"./index.js"},"./index.js"],"./package.json":"./package.json"},"scripts":{"prepack":"npmignore --auto --commentLines=autogenerated","prepublish":"not-in-publish || npm run prepublishOnly","prepublishOnly":"safe-publish-latest","prelint":"evalmd README.md","lint":"eslint --ext=.js,.mjs .","pretest":"npm run lint","tests-only":"nyc tape 'test/**/*.js'","test":"npm run tests-only","posttest":"aud --production","version":"auto-changelog && git add CHANGELOG.md","postversion":"auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""},"repository":{"type":"git","url":"git+https://github.com/ljharb/get-intrinsic.git"},"keywords":["javascript","ecmascript","es","js","intrinsic","getintrinsic","es-abstract"],"author":{"name":"Jordan Harband","email":"ljharb@gmail.com"},"funding":{"url":"https://github.com/sponsors/ljharb"},"license":"MIT","bugs":{"url":"https://github.com/ljharb/get-intrinsic/issues"},"homepage":"https://github.com/ljharb/get-intrinsic#readme","devDependencies":{"@ljharb/eslint-config":"^21.0.1","aud":"^2.0.2","auto-changelog":"^2.4.0","call-bind":"^1.0.2","es-abstract":"^1.21.1","es-value-fixtures":"^1.4.2","eslint":"=8.8.0","evalmd":"^0.0.19","for-each":"^0.3.3","gopd":"^1.0.1","make-async-function":"^1.0.0","make-async-generator-function":"^1.0.0","make-generator-function":"^2.0.0","mock-property":"^1.0.0","npmignore":"^0.3.0","nyc":"^10.3.2","object-inspect":"^1.12.3","safe-publish-latest":"^2.0.0","tape":"^5.6.3"},"auto-changelog":{"output":"CHANGELOG.md","template":"keepachangelog","unreleased":false,"commitLimit":false,"backfillLimit":false,"hideCredit":true},"dependencies":{"function-bind":"^1.1.1","has":"^1.0.3","has-symbols":"^1.0.3"},"testling":{"files":"test/GetIntrinsic.js"},"publishConfig":{"ignore":[".github/workflows"]},"gitHead":"0b60d7ac9d93e8824a36ddd52635be1fc13758d1","_id":"get-intrinsic@1.2.0","_nodeVersion":"19.4.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==","shasum":"7ad1dc0535f3a2904bba075772763e5051f6d05f","tarball":"http://localhost:4545/npm/registry/get-intrinsic/get-intrinsic-1.2.0.tgz","fileCount":9,"unpackedSize":38691,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDb29OYVbJKfex+ljyYg1fRxZiHvAcbeMgBRIcq6cP6MgIhAMPAotqdPrJxkwnAeSq+RDK//aoFWESiSJuvWBmlhUAH"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjykKtACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoUEQ/+PfWdGnewUZa86B0H4haSxBRBlwuFGg7GpdoEhJ3Ll1A9p3Jb\r\nvU2+9RyeNp1p2LNrktOAenAcs7I3dLl0dAspHjjL3uLNhPkrlpeVqOktXs+H\r\n7l0VaAOrLZVi1f+akY117IO0OO6FwRLV42VdM7QIH2BcfXuCyDDke41rq5oS\r\nR9I+8C2SCW2/OxXcMG9nYOpW494hmHRRYh9mpovJUOpAerMUgy334rK72ArR\r\nNsgnAu4luu/7RmC5BNPS26Q7NVCVf7THdx2v3OSkgFvTrdS+wu0NhqkakppS\r\nfGTYkR1m+7vX9YLHIokoIDjHtHaNPMUb7e51OxegjtPEh7FBacfRs0bxfx7Z\r\nJLhYAbjSanGci/gfC2gT1YIPUgydWbx1Ejmol9j7QmA9BQuHSxHu+SiaRA46\r\n+F/Fzbkp1sC0gqo4qGN04Lw8+2g2DHGfBygd6vcUtnaHMz2coCF4rlvcW2fN\r\nz6tT4pcE/AWtC6l9yCWzAWDjEZjF2kBycuiY36IlhhPjtj3qiGQqnTPLL10d\r\nUWA9ZTqFH2k+o4tKhz8g1kQeBApgpRgr9FfukaNq/TZi2tguQ2MlHQ+0R0ZC\r\nZZRtnin4nEpjZ+GkAcfnm9QCrripiWwDtSgXsKvgSICOdp9urrSgfcEAuEvM\r\nrTjOosJAUVohG06+klaUIe6mIssavg3AgjU=\r\n=CxS4\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"directories":{},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/get-intrinsic_1.2.0_1674199725115_0.9427568240984563"},"_hasShrinkwrap":false}},"time":{"created":"2020-10-30T15:03:39.549Z","1.0.0":"2020-10-30T15:03:39.692Z","modified":"2023-01-20T07:28:45.383Z","1.0.1":"2020-10-31T05:32:03.992Z","1.0.2":"2020-12-18T07:22:01.056Z","1.1.0":"2021-01-25T22:22:34.211Z","1.1.1":"2021-02-03T14:58:25.007Z","1.1.2":"2022-06-08T15:12:14.076Z","1.1.3":"2022-09-13T04:01:22.362Z","1.2.0":"2023-01-20T07:28:45.291Z"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"description":"Get and robustly cache all JS language-level intrinsics at first require time","homepage":"https://github.com/ljharb/get-intrinsic#readme","keywords":["javascript","ecmascript","es","js","intrinsic","getintrinsic","es-abstract"],"repository":{"type":"git","url":"git+https://github.com/ljharb/get-intrinsic.git"},"author":{"name":"Jordan Harband","email":"ljharb@gmail.com"},"bugs":{"url":"https://github.com/ljharb/get-intrinsic/issues"},"license":"MIT","readme":"# get-intrinsic [![Version Badge][npm-version-svg]][package-url]\n\n[![github actions][actions-image]][actions-url]\n[![coverage][codecov-image]][codecov-url]\n[![dependency status][deps-svg]][deps-url]\n[![dev dependency status][dev-deps-svg]][dev-deps-url]\n[![License][license-image]][license-url]\n[![Downloads][downloads-image]][downloads-url]\n\n[![npm badge][npm-badge-png]][package-url]\n\nGet and robustly cache all JS language-level intrinsics at first require time.\n\nSee the syntax described [in the JS spec](https://tc39.es/ecma262/#sec-well-known-intrinsic-objects) for reference.\n\n## Example\n\n```js\nvar GetIntrinsic = require('get-intrinsic');\nvar assert = require('assert');\n\n// static methods\nassert.equal(GetIntrinsic('%Math.pow%'), Math.pow);\nassert.equal(Math.pow(2, 3), 8);\nassert.equal(GetIntrinsic('%Math.pow%')(2, 3), 8);\ndelete Math.pow;\nassert.equal(GetIntrinsic('%Math.pow%')(2, 3), 8);\n\n// instance methods\nvar arr = [1];\nassert.equal(GetIntrinsic('%Array.prototype.push%'), Array.prototype.push);\nassert.deepEqual(arr, [1]);\n\narr.push(2);\nassert.deepEqual(arr, [1, 2]);\n\nGetIntrinsic('%Array.prototype.push%').call(arr, 3);\nassert.deepEqual(arr, [1, 2, 3]);\n\ndelete Array.prototype.push;\nGetIntrinsic('%Array.prototype.push%').call(arr, 4);\nassert.deepEqual(arr, [1, 2, 3, 4]);\n\n// missing features\ndelete JSON.parse; // to simulate a real intrinsic that is missing in the environment\nassert.throws(() => GetIntrinsic('%JSON.parse%'));\nassert.equal(undefined, GetIntrinsic('%JSON.parse%', true));\n```\n\n## Tests\nSimply clone the repo, `npm install`, and run `npm test`\n\n## Security\n\nPlease email [@ljharb](https://github.com/ljharb) or see https://tidelift.com/security if you have a potential security vulnerability to report.\n\n[package-url]: https://npmjs.org/package/get-intrinsic\n[npm-version-svg]: https://versionbadg.es/ljharb/get-intrinsic.svg\n[deps-svg]: https://david-dm.org/ljharb/get-intrinsic.svg\n[deps-url]: https://david-dm.org/ljharb/get-intrinsic\n[dev-deps-svg]: https://david-dm.org/ljharb/get-intrinsic/dev-status.svg\n[dev-deps-url]: https://david-dm.org/ljharb/get-intrinsic#info=devDependencies\n[npm-badge-png]: https://nodei.co/npm/get-intrinsic.png?downloads=true&stars=true\n[license-image]: https://img.shields.io/npm/l/get-intrinsic.svg\n[license-url]: LICENSE\n[downloads-image]: https://img.shields.io/npm/dm/get-intrinsic.svg\n[downloads-url]: https://npm-stat.com/charts.html?package=get-intrinsic\n[codecov-image]: https://codecov.io/gh/ljharb/get-intrinsic/branch/main/graphs/badge.svg\n[codecov-url]: https://app.codecov.io/gh/ljharb/get-intrinsic/\n[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/get-intrinsic\n[actions-url]: https://github.com/ljharb/get-intrinsic/actions\n","readmeFilename":"README.md"}
\ No newline at end of file
diff --git a/cli/tests/testdata/npm/registry/has-property-descriptors/has-property-descriptors-1.0.0.tgz b/cli/tests/testdata/npm/registry/has-property-descriptors/has-property-descriptors-1.0.0.tgz
new file mode 100644
index 0000000000..ee60a4f9e0
Binary files /dev/null and b/cli/tests/testdata/npm/registry/has-property-descriptors/has-property-descriptors-1.0.0.tgz differ
diff --git a/cli/tests/testdata/npm/registry/has-property-descriptors/registry.json b/cli/tests/testdata/npm/registry/has-property-descriptors/registry.json
new file mode 100644
index 0000000000..6cc2fae107
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/has-property-descriptors/registry.json
@@ -0,0 +1 @@
+{"_id":"has-property-descriptors","name":"has-property-descriptors","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"has-property-descriptors","version":"1.0.0","description":"Does the environment have full property descriptor support? Handles IE 8's broken defineProperty/gOPD.","main":"index.js","exports":{".":"./index.js","./package.json":"./package.json"},"sideEffects":false,"scripts":{"prepublishOnly":"safe-publish-latest","prepublish":"not-in-publish || npm run prepublishOnly","pretest":"npm run lint","prelint":"evalmd README.md","lint":"eslint --ext=js,mjs .","tests-only":"nyc tape 'test/**/*.js'","test":"npm run tests-only","posttest":"aud --production","version":"auto-changelog && git add CHANGELOG.md","postversion":"auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""},"repository":{"type":"git","url":"git+https://github.com/inspect-js/has-property-descriptors.git"},"keywords":["property","descriptors","has","environment","env","defineProperty","getOwnPropertyDescriptor"],"author":{"name":"Jordan Harband","email":"ljharb@gmail.com"},"funding":{"url":"https://github.com/sponsors/ljharb"},"license":"MIT","bugs":{"url":"https://github.com/inspect-js/has-property-descriptors/issues"},"homepage":"https://github.com/inspect-js/has-property-descriptors#readme","devDependencies":{"@ljharb/eslint-config":"^21.0.0","aud":"^2.0.0","auto-changelog":"^2.4.0","eslint":"=8.8.0","in-publish":"^2.0.1","evalmd":"^0.0.19","nyc":"^10.3.2","safe-publish-latest":"^2.0.0","tape":"^5.5.3"},"dependencies":{"get-intrinsic":"^1.1.1"},"testling":{"files":"test/index.js"},"auto-changelog":{"output":"CHANGELOG.md","template":"keepachangelog","unreleased":false,"commitLimit":false,"backfillLimit":false,"hideCredit":true},"gitHead":"3771c8b4f20e963d3a64b101b3233c20791c32ae","_id":"has-property-descriptors@1.0.0","_nodeVersion":"17.9.0","_npmVersion":"8.3.1","dist":{"integrity":"sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==","shasum":"610708600606d36961ed04c196193b6a607fa861","tarball":"http://localhost:4545/npm/registry/has-property-descriptors/has-property-descriptors-1.0.0.tgz","fileCount":9,"unpackedSize":9308,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGu4gOEZPx0AUfM6YuqldUOElOureYihKd6CDr1Dpv9gAiBYuTEkAw8K4moKvJ7BXTohQQAJNKNWCnAJlOEyg06yYg=="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiWQgoACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmpuEhAAlWpOnMjEOy8ZCmrOUmE82lejonXdgy/c0+t609UYz2VO0nKr\r\n0RqhNm3xSvSU/0wMa1LOKb0rcNnRNk9YFffxDMWi6xE84n7jjWbf1vcZ1xEb\r\nFLb5T7MGEveF6lNeeMLOZPJVyQ3WDEwio5meyayWVRzEBrJq5yT+e5/hgFwz\r\nLDxMfil2CosRkDeqr+YHJC5s57qcTOkM0SKLv7pfvtVymnFPuVjTkZfwb26g\r\nwRu7oVkZFjIBf6bG0wCxj9fMCMsHpKI27rU9O3K+U0DCJLtSG92bTyvDJ0ig\r\nNLBiX5zwelnLHUEGmvIwt3/V2ZxFvK5Soymnk4COvCI3QgJkGAKoBJDgsLmP\r\nDcvHe5NEidZqvh/8kfiqwHqQ0tAUImPGQoQ3j+Sx6oN3+q+6d9RWkUyfv69I\r\n0268s/Mf2Rf7Ow0PbgkQn3qq/dxR/PvPKDSTz53gpmiDd79Hqjv9KTNTawBG\r\nHF/Nga5rVOUZHQgvhaOoXrGDsIVLfKeda+UrFwKHN4zkbvO58LaBoIMjHKz3\r\nLB8Qddh4Cqm4QdK6fBgmrDyCI79AIICCeETfQCGU/gitLcS0mQTCIHFSdwtR\r\nwt0t85krp1lpjhKA8HfHgwj5Ky9A/KdFPI1DrbbqjJiRCAnsSJzdJuw0eXcn\r\nUai87G82D9Q2HmEpEgBhWKsa8PQU4pO18E0=\r\n=iq2o\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"directories":{},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/has-property-descriptors_1.0.0_1650001960160_0.19595316522875494"},"_hasShrinkwrap":false}},"time":{"created":"2022-04-15T05:52:40.160Z","1.0.0":"2022-04-15T05:52:40.310Z","modified":"2022-04-15T05:52:40.433Z"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"description":"Does the environment have full property descriptor support? Handles IE 8's broken defineProperty/gOPD.","homepage":"https://github.com/inspect-js/has-property-descriptors#readme","keywords":["property","descriptors","has","environment","env","defineProperty","getOwnPropertyDescriptor"],"repository":{"type":"git","url":"git+https://github.com/inspect-js/has-property-descriptors.git"},"author":{"name":"Jordan Harband","email":"ljharb@gmail.com"},"bugs":{"url":"https://github.com/inspect-js/has-property-descriptors/issues"},"license":"MIT","readme":"# has-property-descriptors [![Version Badge][npm-version-svg]][package-url]\n\n[![github actions][actions-image]][actions-url]\n[![coverage][codecov-image]][codecov-url]\n[![dependency status][deps-svg]][deps-url]\n[![dev dependency status][dev-deps-svg]][dev-deps-url]\n[![License][license-image]][license-url]\n[![Downloads][downloads-image]][downloads-url]\n\n[![npm badge][npm-badge-png]][package-url]\n\nDoes the environment have full property descriptor support? Handles IE 8's broken defineProperty/gOPD.\n\n## Example\n\n```js\nvar hasPropertyDescriptors = require('has-property-descriptors');\nvar assert = require('assert');\n\nassert.equal(hasPropertyDescriptors(), true); // will be `false` in IE 6-8, and ES5 engines\n\n// Arrays can not have their length `[[Defined]]` in some engines\nassert.equal(hasPropertyDescriptors.hasArrayLengthDefineBug(), false); // will be `true` in Firefox 4-22, and node v0.6\n```\n\n## Tests\nSimply clone the repo, `npm install`, and run `npm test`\n\n[package-url]: https://npmjs.org/package/has-property-descriptors\n[npm-version-svg]: https://versionbadg.es/inspect-js/has-property-descriptors.svg\n[deps-svg]: https://david-dm.org/inspect-js/has-property-descriptors.svg\n[deps-url]: https://david-dm.org/inspect-js/has-property-descriptors\n[dev-deps-svg]: https://david-dm.org/inspect-js/has-property-descriptors/dev-status.svg\n[dev-deps-url]: https://david-dm.org/inspect-js/has-property-descriptors#info=devDependencies\n[npm-badge-png]: https://nodei.co/npm/has-property-descriptors.png?downloads=true&stars=true\n[license-image]: https://img.shields.io/npm/l/has-property-descriptors.svg\n[license-url]: LICENSE\n[downloads-image]: https://img.shields.io/npm/dm/has-property-descriptors.svg\n[downloads-url]: https://npm-stat.com/charts.html?package=has-property-descriptors\n[codecov-image]: https://codecov.io/gh/inspect-js/has-property-descriptors/branch/main/graphs/badge.svg\n[codecov-url]: https://app.codecov.io/gh/inspect-js/has-property-descriptors/\n[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/inspect-js/has-property-descriptors\n[actions-url]: https://github.com/inspect-js/has-property-descriptors/actions\n","readmeFilename":"README.md"}
\ No newline at end of file
diff --git a/cli/tests/testdata/npm/registry/has-symbols/has-symbols-1.0.3.tgz b/cli/tests/testdata/npm/registry/has-symbols/has-symbols-1.0.3.tgz
new file mode 100644
index 0000000000..a5f34be724
Binary files /dev/null and b/cli/tests/testdata/npm/registry/has-symbols/has-symbols-1.0.3.tgz differ
diff --git a/cli/tests/testdata/npm/registry/has-symbols/registry.json b/cli/tests/testdata/npm/registry/has-symbols/registry.json
new file mode 100644
index 0000000000..c8602cac7c
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/has-symbols/registry.json
@@ -0,0 +1 @@
+{"_id":"has-symbols","_rev":"9-4d7f5f8fd9b1e0675ff88a8f88b1f511","name":"has-symbols","description":"Determine if the JS environment has Symbol support. Supports spec, or shams.","dist-tags":{"latest":"1.0.3"},"versions":{"1.0.0":{"name":"has-symbols","version":"1.0.0","author":{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},"contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"}],"description":"Determine if the JS environment has Symbol support. Supports spec, or shams.","license":"MIT","main":"index.js","scripts":{"prepublish":"safe-publish-latest","pretest":"npm run --silent lint","test":"npm run --silent tests-only","posttest":"npm run --silent security","tests-only":"npm run --silent test:stock && npm run --silent test:staging && npm run --silent test:shams","test:stock":"node test","test:staging":"node --harmony --es-staging test","test:shams":"npm run --silent test:shams:getownpropertysymbols && npm run --silent test:shams:corejs","test:shams:corejs":"node test/shams/core-js.js","test:shams:getownpropertysymbols":"node test/shams/get-own-property-symbols.js","lint":"eslint *.js","security":"nsp check"},"repository":{"type":"git","url":"git://github.com/ljharb/has-symbols.git"},"keywords":["Symbol","symbols","typeof","sham","polyfill","native","core-js","ES6"],"dependencies":{},"devDependencies":{"tape":"^4.6.0","nsp":"^2.6.1","safe-publish-latest":"^1.0.1","eslint":"^3.5.0","@ljharb/eslint-config":"^8.0.0","get-own-property-symbols":"^0.9.2","core-js":"^2.4.1"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"e4a5e7028c87d509902ff292f4da3ea45c7c50cf","bugs":{"url":"https://github.com/ljharb/has-symbols/issues"},"homepage":"https://github.com/ljharb/has-symbols#readme","_id":"has-symbols@1.0.0","_shasum":"ba1a8f1af2a0fc39650f5c850367704122063b44","_from":".","_npmVersion":"3.10.3","_nodeVersion":"6.6.0","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"dist":{"shasum":"ba1a8f1af2a0fc39650f5c850367704122063b44","tarball":"http://localhost:4545/npm/registry/has-symbols/has-symbols-1.0.0.tgz","integrity":"sha512-QfcgWpH8qn5qhNMg3wfXf2FD/rSA4TwNiDDthKqXe7v6oBW0YKWcnfwMAApgWq9Lh+Yu+fQWVhHPohlD/S6uoQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCraijP8TUgset3RP/0apBanH6US79uNIP6cuXhcXO2XAIgcC1SUirdAx9l8oZX/ALh1KkxopaC+SvsCad2NUwNfMs="}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmOperationalInternal":{"host":"packages-12-west.internal.npmjs.com","tmp":"tmp/has-symbols-1.0.0.tgz_1474328796481_0.2780582248233259"},"directories":{}},"1.0.1":{"name":"has-symbols","version":"1.0.1","author":{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},"funding":{"url":"https://github.com/sponsors/ljharb"},"contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"}],"description":"Determine if the JS environment has Symbol support. Supports spec, or shams.","license":"MIT","main":"index.js","scripts":{"prepublish":"safe-publish-latest","pretest":"npm run --silent lint","test":"npm run --silent tests-only","posttest":"npx aud","tests-only":"npm run --silent test:stock && npm run --silent test:staging && npm run --silent test:shams","test:stock":"node test","test:staging":"node --harmony --es-staging test","test:shams":"npm run --silent test:shams:getownpropertysymbols && npm run --silent test:shams:corejs","test:shams:corejs":"node test/shams/core-js.js","test:shams:getownpropertysymbols":"node test/shams/get-own-property-symbols.js","lint":"eslint *.js","version":"auto-changelog && git add CHANGELOG.md","postversion":"auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""},"repository":{"type":"git","url":"git://github.com/ljharb/has-symbols.git"},"keywords":["Symbol","symbols","typeof","sham","polyfill","native","core-js","ES6"],"dependencies":{},"devDependencies":{"@ljharb/eslint-config":"^15.0.1","auto-changelog":"^1.16.2","core-js":"^2.6.10","eslint":"^6.6.0","get-own-property-symbols":"^0.9.4","safe-publish-latest":"^1.1.4","tape":"^4.11.0"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"auto-changelog":{"output":"CHANGELOG.md","template":"keepachangelog","unreleased":false,"commitLimit":false,"backfillLimit":false},"gitHead":"132fe9ce5c2e443e0570606d4568a242eb86b5f5","bugs":{"url":"https://github.com/ljharb/has-symbols/issues"},"homepage":"https://github.com/ljharb/has-symbols#readme","_id":"has-symbols@1.0.1","_nodeVersion":"13.1.0","_npmVersion":"6.12.1","dist":{"integrity":"sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==","shasum":"9f5214758a44196c406d9bd76cebf81ec2dd31e8","tarball":"http://localhost:4545/npm/registry/has-symbols/has-symbols-1.0.1.tgz","fileCount":14,"unpackedSize":15474,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd0I28CRA9TVsSAnZWagAAt2AP/jan/+oerqF7TJJ1/7C0\nDib5YuePKj9dBimLNNxyNbDCo9+XGPPXoDd5OuGVQ8hePAe0pFxsenbtyT+Y\n+empPCZMrgUJfP7Umo6FYPE7EChp7ES7pPua2oeoKzMhK3xH+sBXj9MQ60Al\nunwIhq1k0idyeHT/9iJegP+wGF5pDe/EZUVbzt9r6JU4WhCNopdta+BZwIRP\nmE/NCdPjZ2jHbjYVJlT7b7uFrA4KXtRXtaJKhy97biek3xfgP4WLKvRaPTKo\nlTXtw/UXk8L42RYfJFlIJ2nyLeorwS6QObZPi9tB8BmIogvrSjthvcVL6DSN\nWJTjxpu43zbRS6mHK5nBAnXcshB/mvM9E8hTxrhG2jfSwAR3RexMLsOqgEsH\nTUTxRdB4Zox0nUD7rSahulvEtjl0bCRwo+oeuNfNDgf34sAjhIsaBMzH1rQY\nuTI8DKB4s1wKbth7YWUvacPay0+vvIbJnq4AMjoIDXezAKdDVIG6zB5rt5vN\nPxaAnRkB2htFP8MEHbqAvhk2ibqGdQvdVW2QPeTjDc7bXeymiXI93nrZyiw6\nq5alXDWN8ubB8A9A7HvKa+XU3dsOoYW/Ypx/h/ca17m0Gc0LwfJ2o0lC4sLQ\n3akrlFDjuBdlt2tWWzCRD5e7av8jJZ5C5ZKBeRkL2Xod4iyMFAD5wtefsXvR\nELxD\r\n=shCA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCCFSo29lhXirX0Rs+Cuj11qyhheYjpANIVYXAMEcNrhwIgGt53b4yRyWBuEHwcnk8h23iA8dpWARLe+Ojp9kyWxO8="}]},"maintainers":[{"email":"ljharb@gmail.com","name":"ljharb"}],"_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/has-symbols_1.0.1_1573948860346_0.7408930604026625"},"_hasShrinkwrap":false},"1.0.2":{"name":"has-symbols","version":"1.0.2","author":{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},"funding":{"url":"https://github.com/sponsors/ljharb"},"contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"}],"description":"Determine if the JS environment has Symbol support. Supports spec, or shams.","license":"MIT","main":"index.js","scripts":{"prepublish":"safe-publish-latest","pretest":"npm run --silent lint","test":"npm run tests-only","posttest":"aud --production","tests-only":"npm run test:stock && npm run test:staging && npm run test:shams","test:stock":"nyc node test","test:staging":"nyc node --harmony --es-staging test","test:shams":"npm run --silent test:shams:getownpropertysymbols && npm run --silent test:shams:corejs","test:shams:corejs":"nyc node test/shams/core-js.js","test:shams:getownpropertysymbols":"nyc node test/shams/get-own-property-symbols.js","lint":"eslint --ext=js,mjs .","version":"auto-changelog && git add CHANGELOG.md","postversion":"auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""},"repository":{"type":"git","url":"git://github.com/inspect-js/has-symbols.git"},"keywords":["Symbol","symbols","typeof","sham","polyfill","native","core-js","ES6"],"devDependencies":{"@ljharb/eslint-config":"^17.5.1","aud":"^1.1.4","auto-changelog":"^2.2.1","core-js":"^2.6.12","eslint":"^7.20.0","get-own-property-symbols":"^0.9.5","nyc":"^10.3.2","safe-publish-latest":"^1.1.4","tape":"^5.2.0"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"auto-changelog":{"output":"CHANGELOG.md","template":"keepachangelog","unreleased":false,"commitLimit":false,"backfillLimit":false,"hideCredit":true},"greenkeeper":{"ignore":["core-js"]},"gitHead":"32b16a3809db3bbb463df501c3984a333f1979f3","bugs":{"url":"https://github.com/inspect-js/has-symbols/issues"},"homepage":"https://github.com/inspect-js/has-symbols#readme","_id":"has-symbols@1.0.2","_nodeVersion":"15.10.0","_npmVersion":"7.5.6","dist":{"integrity":"sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==","shasum":"165d3070c00309752a1236a479331e3ac56f1423","tarball":"http://localhost:4545/npm/registry/has-symbols/has-symbols-1.0.2.tgz","fileCount":14,"unpackedSize":18056,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgOnPHCRA9TVsSAnZWagAA+tEP/296p4+KYc4qJLrX0uxY\nDA8r08WOCWwnIEHw6X1O12XSFB4d0bMTA1vIZEAl/GosbM4wvVdO4JWYaxL+\nAe8m2GxNjz3KXswq4SELhwf8c6xk3Q294qHiUeByfvxK4OwfyKyZ7iI4YnV/\n4jT/FE+AOlNqdAO3izGfm94UW8X1g/6S2X82JTxKngl1/YoAOraEjtD+XF2f\nPTcL4SmyoL7xTTMtPEOSXRAB73Y2KeNbFC8Ee1r/vU0C62MscsFD6Whc3lH4\naxT1ccSUO8YUftLzdMJY3R6jqF8ZKAx7rYdPPDEkm4fJ/MAsw9pQKKf71Dnf\nDAYBAGsFByUVGraRgWXx01w6NnVNbHqBBbLlTK1e0JpCvrSpkFpX/kfERpEB\nhaFUc8n7SDIeAFgBidagI5HYHwbUmlPkZ46NXZcL0xLMx8SNXyRvImsXOR5m\nnV6ReVLSJP/VmTFYjOVuFsteXU2Ot7ZtnHy/eOP8WXYuWn02CuqFC+i8xFxn\nEXCqT3wC1ObdFkV9E2WGVQpB6U2UoJCiRQJO0KjP+arqcS22zL+IXDgGxr/a\nw8f4erWtCTSJquDY9+P7VCSbjGTxfmkunAXUcwMCEiVfLc8wwrg+vHJF3Sgi\n2QqXRdNu1JKHGXfnpnJm1rj2oZV9/5ZjYQFB+CeWM70TyKrYeTgysG0pa8y5\nc8++\r\n=JCcn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICmk/GnP0bgLMLKwOsqmi85pgPZF8i7IN0E1zAWz8kQIAiEA7agW7tNwD7vJ3WADcThU+35O/hHKm2DzykwgXewNWdU="}]},"_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"directories":{},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/has-symbols_1.0.2_1614443462507_0.38046500905605685"},"_hasShrinkwrap":false},"1.0.3":{"name":"has-symbols","version":"1.0.3","description":"Determine if the JS environment has Symbol support. Supports spec, or shams.","main":"index.js","scripts":{"prepublishOnly":"safe-publish-latest","prepublish":"not-in-publish || npm run prepublishOnly","pretest":"npm run --silent lint","test":"npm run tests-only","posttest":"aud --production","tests-only":"npm run test:stock && npm run test:staging && npm run test:shams","test:stock":"nyc node test","test:staging":"nyc node --harmony --es-staging test","test:shams":"npm run --silent test:shams:getownpropertysymbols && npm run --silent test:shams:corejs","test:shams:corejs":"nyc node test/shams/core-js.js","test:shams:getownpropertysymbols":"nyc node test/shams/get-own-property-symbols.js","lint":"eslint --ext=js,mjs .","version":"auto-changelog && git add CHANGELOG.md","postversion":"auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""},"repository":{"type":"git","url":"git://github.com/inspect-js/has-symbols.git"},"keywords":["Symbol","symbols","typeof","sham","polyfill","native","core-js","ES6"],"author":{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},"contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"}],"funding":{"url":"https://github.com/sponsors/ljharb"},"license":"MIT","bugs":{"url":"https://github.com/ljharb/has-symbols/issues"},"homepage":"https://github.com/ljharb/has-symbols#readme","devDependencies":{"@ljharb/eslint-config":"^20.2.3","aud":"^2.0.0","auto-changelog":"^2.4.0","core-js":"^2.6.12","eslint":"=8.8.0","get-own-property-symbols":"^0.9.5","nyc":"^10.3.2","safe-publish-latest":"^2.0.0","tape":"^5.5.2"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"auto-changelog":{"output":"CHANGELOG.md","template":"keepachangelog","unreleased":false,"commitLimit":false,"backfillLimit":false,"hideCredit":true},"greenkeeper":{"ignore":["core-js"]},"gitHead":"444dc14d035df9891743a28cbc5d6ecdb0cb3b01","_id":"has-symbols@1.0.3","_nodeVersion":"17.6.0","_npmVersion":"8.5.2","dist":{"integrity":"sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==","shasum":"bb7b2c4349251dce87b125f7bdf874aa7c8b39f8","tarball":"http://localhost:4545/npm/registry/has-symbols/has-symbols-1.0.3.tgz","fileCount":13,"unpackedSize":20603,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJiHo7dACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoJTg//VKZyTT/GxVMPZNQFC6Q05AQ+zwFmm1ePSsyP3+hebhjz0KMZ\r\nZh8Z3oaFj53lk6p6hl6wJgJh8v+4H8tYi90zewuk2/sv/r4gS8KKOJkEU5hS\r\nExpiO/FlpW1EBW0kHcPOLiYkyvhm5iNX17o0qUXw62EVu9pFdzLuMLtoVch9\r\n0RC3armyFU5YXjpr4lQCbHCAK6okYFFh6BGQYB0k/to/o1YZ3QijFZ7cDlyl\r\nUSH33b6VFsD9gVT6pVYGmhwPfbxrUzvgpmMeJqdL940V3BgVDu9h/lXFDpvC\r\nyf9vmUEiVkcxeiIbJuusCQjMbPT31uYDaAYY+W+v4pbD552jb/7Gm2ttl1uV\r\n1yx9J3M5aKbjZWMVfRinlfGoyUIs0rpxhSsQTp84skwPLkXC1YfODYNhy4+o\r\nVR5GNTIDDOB4i4y7lGVvx7Vd4ySP+Tz9YpmFI9ZrCnEVXggUn9y+PU8R19UJ\r\nrOVAYikVzsyC5PT9PKr2lvITXDb8siGUNt8YmJhZupzv3K+I5sEojmpqCGvP\r\nW748lmzXQAFYUY/BL1/zChahtp6w5mBaX79uF/xO7h/owukFCK2Y1Seyz4HP\r\nFzn6kDQM+TcUD9GlOhy1OsSLVhuK+gbGupNtSG52OaR9JVtmxSzd83TujaTF\r\ncDmeevwmNQi4Gnt70AILlnaCxXanGrp0epk=\r\n=B5Gh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDwzczMy98ycY151XrPoURp8chFzfXRYegRhpOydLT8UgIgb/6c33xTl81h3biIUwEWPJAVPlOf6E2AEaWCvLmPOck="}]},"_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"directories":{},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/has-symbols_1.0.3_1646169820978_0.6668045837242529"},"_hasShrinkwrap":false}},"readme":"# has-symbols [![Version Badge][2]][1]\n\n[![github actions][actions-image]][actions-url]\n[![coverage][codecov-image]][codecov-url]\n[![dependency status][5]][6]\n[![dev dependency status][7]][8]\n[![License][license-image]][license-url]\n[![Downloads][downloads-image]][downloads-url]\n\n[![npm badge][11]][1]\n\nDetermine if the JS environment has Symbol support. Supports spec, or shams.\n\n## Example\n\n```js\nvar hasSymbols = require('has-symbols');\n\nhasSymbols() === true; // if the environment has native Symbol support. Not polyfillable, not forgeable.\n\nvar hasSymbolsKinda = require('has-symbols/shams');\nhasSymbolsKinda() === true; // if the environment has a Symbol sham that mostly follows the spec.\n```\n\n## Supported Symbol shams\n - get-own-property-symbols [npm](https://www.npmjs.com/package/get-own-property-symbols) | [github](https://github.com/WebReflection/get-own-property-symbols)\n - core-js [npm](https://www.npmjs.com/package/core-js) | [github](https://github.com/zloirock/core-js)\n\n## Tests\nSimply clone the repo, `npm install`, and run `npm test`\n\n[1]: https://npmjs.org/package/has-symbols\n[2]: https://versionbadg.es/inspect-js/has-symbols.svg\n[5]: https://david-dm.org/inspect-js/has-symbols.svg\n[6]: https://david-dm.org/inspect-js/has-symbols\n[7]: https://david-dm.org/inspect-js/has-symbols/dev-status.svg\n[8]: https://david-dm.org/inspect-js/has-symbols#info=devDependencies\n[11]: https://nodei.co/npm/has-symbols.png?downloads=true&stars=true\n[license-image]: https://img.shields.io/npm/l/has-symbols.svg\n[license-url]: LICENSE\n[downloads-image]: https://img.shields.io/npm/dm/has-symbols.svg\n[downloads-url]: https://npm-stat.com/charts.html?package=has-symbols\n[codecov-image]: https://codecov.io/gh/inspect-js/has-symbols/branch/main/graphs/badge.svg\n[codecov-url]: https://app.codecov.io/gh/inspect-js/has-symbols/\n[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/inspect-js/has-symbols\n[actions-url]: https://github.com/inspect-js/has-symbols/actions\n","maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"time":{"modified":"2022-06-18T19:26:17.135Z","created":"2016-09-19T23:46:36.740Z","1.0.0":"2016-09-19T23:46:36.740Z","1.0.1":"2019-11-17T00:01:00.460Z","1.0.2":"2021-02-27T16:31:02.668Z","1.0.3":"2022-03-01T21:23:41.133Z"},"homepage":"https://github.com/ljharb/has-symbols#readme","keywords":["Symbol","symbols","typeof","sham","polyfill","native","core-js","ES6"],"repository":{"type":"git","url":"git://github.com/inspect-js/has-symbols.git"},"contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"}],"author":{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},"bugs":{"url":"https://github.com/ljharb/has-symbols/issues"},"license":"MIT","readmeFilename":"README.md"}
\ No newline at end of file
diff --git a/cli/tests/testdata/npm/registry/has/has-1.0.3.tgz b/cli/tests/testdata/npm/registry/has/has-1.0.3.tgz
new file mode 100644
index 0000000000..90c33297e2
Binary files /dev/null and b/cli/tests/testdata/npm/registry/has/has-1.0.3.tgz differ
diff --git a/cli/tests/testdata/npm/registry/has/registry.json b/cli/tests/testdata/npm/registry/has/registry.json
new file mode 100644
index 0000000000..137122e4a7
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/has/registry.json
@@ -0,0 +1 @@
+{"_id":"has","_rev":"28-a7978fcc3beffd13dd518b65fdb20142","name":"has","description":"Object.prototype.hasOwnProperty.call shortcut","dist-tags":{"latest":"1.0.3"},"versions":{"0.0.1":{"name":"has","description":"Object.prototype.hasOwnProperty.call shortcut","version":"0.0.1","homepage":"https://github.com/tarruda/has","author":{"name":"Thiago de Arruda","email":"tpadilha84@gmail.com"},"repository":{"type":"git","url":"git://github.com/tarruda/has.git"},"bugs":{"url":"https://github.com/tarruda/has/issues"},"licenses":[{"type":"MIT","url":"https://github.com/tarruda/has/blob/master/LICENSE-MIT"}],"main":"./src/index","devDependencies":{"chai":"~1.7.2","grunt":"~0.4.1","grunt-contrib-watch":"~0.5.3","grunt-mocha-debug":"~0.0.6","grunt-exec-jshint":"~0.0.0","grunt-release":"~0.5.1","grunt-newer":"~0.5.4"},"engines":{"node":">= 0.8.0"},"_id":"has@0.0.1","dist":{"shasum":"66639c14eaf559f139da2be0e438910ef3fd5b1b","tarball":"http://localhost:4545/npm/registry/has/has-0.0.1.tgz","integrity":"sha512-Ulo9uG05SN7r55LqJxpU84yWzVPfJGv+GZSaEnm5mKO/jtwV5KODce9bPEDJh1uoYGJpsy5pKi4dQOdDSFzCvw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDfgu84CAfHIBVCPe26sam0TSBJ85TxQTylJ60gIU80NgIgf47PIcgnILXdcQJnGDT+j5EpCT2kN392mwWhJQz21gg="}]},"_from":".","_npmVersion":"1.3.8","_npmUser":{"name":"tarruda","email":"tpadilha84@gmail.com"},"maintainers":[{"name":"tarruda","email":"tpadilha84@gmail.com"}],"directories":{}},"1.0.0":{"name":"has","description":"Object.prototype.hasOwnProperty.call shortcut","version":"1.0.0","homepage":"https://github.com/tarruda/has","author":{"name":"Thiago de Arruda","email":"tpadilha84@gmail.com"},"repository":{"type":"git","url":"git://github.com/tarruda/has.git"},"bugs":{"url":"https://github.com/tarruda/has/issues"},"licenses":[{"type":"MIT","url":"https://github.com/tarruda/has/blob/master/LICENSE-MIT"}],"main":"./src/index","devDependencies":{"chai":"~1.7.2","mocha":"^1.21.4"},"engines":{"node":">= 0.8.0"},"scripts":{"test":"node_modules/mocha/bin/mocha"},"gitHead":"3113c5ff93ec8befffd9cf23c4dbab7a9d429c20","_id":"has@1.0.0","_shasum":"56c6582d23b40f3a5458f68ba79bc6c4bef203b3","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"tarruda","email":"tpadilha84@gmail.com"},"maintainers":[{"name":"tarruda","email":"tpadilha84@gmail.com"}],"dist":{"shasum":"56c6582d23b40f3a5458f68ba79bc6c4bef203b3","tarball":"http://localhost:4545/npm/registry/has/has-1.0.0.tgz","integrity":"sha512-pZW9uw/9635RZCMUO1nIiZ8Ue8fJP6GlegyXWsFmqp1asx44TMS+K+ffoKnhdFt/piqIpvHG1h6qXmyVEiXCfg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFCoV79Swa7ogDk0FPLZ8kwt4fygO2aUdChkivfxg5juAiAjZm1GE4R6dpMqLcAafe1QcBaMYvhuK5jkjQfoULHq/A=="}]},"directories":{}},"1.0.1":{"name":"has","description":"Object.prototype.hasOwnProperty.call shortcut","version":"1.0.1","homepage":"https://github.com/tarruda/has","author":{"name":"Thiago de Arruda","email":"tpadilha84@gmail.com"},"repository":{"type":"git","url":"git://github.com/tarruda/has.git"},"bugs":{"url":"https://github.com/tarruda/has/issues"},"licenses":[{"type":"MIT","url":"https://github.com/tarruda/has/blob/master/LICENSE-MIT"}],"main":"./src/index","dependencies":{"function-bind":"^1.0.2"},"devDependencies":{"chai":"~1.7.2","mocha":"^1.21.4"},"engines":{"node":">= 0.8.0"},"scripts":{"test":"node_modules/mocha/bin/mocha"},"gitHead":"535c5c8ed1dc255c9e223829e702548dd514d2a5","_id":"has@1.0.1","_shasum":"8461733f538b0837c9361e39a9ab9e9704dc2f28","_from":".","_npmVersion":"2.11.0","_nodeVersion":"2.2.1","_npmUser":{"name":"tarruda","email":"tpadilha84@gmail.com"},"dist":{"shasum":"8461733f538b0837c9361e39a9ab9e9704dc2f28","tarball":"http://localhost:4545/npm/registry/has/has-1.0.1.tgz","integrity":"sha512-8wpov6mGFPJ/SYWGQIFo6t0yuNWoO9MkSq3flX8LhiGmbIUhDETp9knPMcIm0Xig1ybWsw6gq2w0gCz1JHD+Qw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGwsArRhXrhj+qjKhTjYer8IcOloz5NSf90mKsSbjDTCAiEAikyzkXCsz1Xr2d+L0/QJVYD+vLNZUGu/gQpngIRstPM="}]},"maintainers":[{"name":"tarruda","email":"tpadilha84@gmail.com"}],"directories":{}},"1.0.2":{"name":"has","description":"Object.prototype.hasOwnProperty.call shortcut","version":"1.0.2","homepage":"https://github.com/tarruda/has","author":{"name":"Thiago de Arruda","email":"tpadilha84@gmail.com"},"contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"}],"repository":{"type":"git","url":"git://github.com/tarruda/has.git"},"bugs":{"url":"https://github.com/tarruda/has/issues"},"license":"MIT","licenses":[{"type":"MIT","url":"https://github.com/tarruda/has/blob/master/LICENSE-MIT"}],"main":"./src","dependencies":{"function-bind":"^1.1.1"},"devDependencies":{"@ljharb/eslint-config":"^12.2.1","eslint":"^4.19.1","tape":"^4.9.0"},"engines":{"node":">= 0.4.0"},"scripts":{"lint":"eslint .","pretest":"npm run lint","test":"tape test"},"gitHead":"5becaf997373b548e790e8c5ec0b718e20da6097","_id":"has@1.0.2","_npmVersion":"6.1.0","_nodeVersion":"10.3.0","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"dist":{"integrity":"sha512-D5/WxwX+SrGfs/fiQn34RAoIZkCLJBDEfBWS1kmTI6G/1mtjhxTBiIiJi8EsKhwaQqKqj7lpKOi3i69tg3P+OQ==","shasum":"1a64bfe4b52e67fb87b9822503d97c019fb6ba42","tarball":"http://localhost:4545/npm/registry/has/has-1.0.2.tgz","fileCount":6,"unpackedSize":2854,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEwZkCRA9TVsSAnZWagAALQAP+wSiRGrAWvCQnVLAKjxZ\nYQebEuC2gpCQX1p/eG0RGxdRC+rC1+LOG2CJR41wtMtXXIcnC6wrXb2TnHMw\nyHMqSyQIjRxVAmR2CPOlh36FXAp9efcgPKyTAfXEw0/3iZMjJ0jcZCbaDckQ\ngLFB0fkv1T0uzudvjUqXVbSXihMe17qi3/vXQbQNtnrFkHfGLstp+cdXN33+\nh0Hwv6FsE+tdfRM2q2xhYzIvDbDQ3mGCe0nsMYj3nRFoSOsAnftsOrZnIGr0\n4VuFxe66oYGaokH/GI/JR9AikEj9iEizknW4TB3d9KDSHtfVbsHeptDQ/CdA\nXUpyKlALK0VZvHGC+lKQsllCb4D1uJ6isnoqL3rV20/v1X1tjKIm9/P1tBsB\nKGS7mMlR+vSFzB8iGNhYVvE5p7Du5FXJElGI8qj0AjCaCbvech30WbHyIJlx\n26/ywIE5/m+HJ+wnFAUDW3VQWakzZDPqhyc0GVo+yCixxjLGv++UslsdSncn\nx9g0k5l5bGQ2SCn8XbZRCG/jHm+bdD0NOjqvaE7bu7RvGPfniqZDpH6TzJmI\nlb55w8iPpR+GZ5vhypDdpQBT//ty33rNHb+Q9U4e9ZHnQNnwK15WaImfE7J5\nxfmA+8JRj3FBiMGpKU++NHOtI9Y+aK+CqCmVwlhlkpZT0z6oQphcsl2gqe4F\nuujs\r\n=q25B\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFVCO11i1G9/dgjkto0mkv30OIZOjyqKhV8oPKebk69aAiAipxVf8Vs0ptvMfLEdGwLLLc7iQxf6hreeLjoOsrhRgg=="}]},"maintainers":[{"name":"tarruda","email":"tpadilha84@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/has_1.0.2_1527973476308_0.8578208238940064"},"_hasShrinkwrap":false},"1.0.3":{"name":"has","description":"Object.prototype.hasOwnProperty.call shortcut","version":"1.0.3","homepage":"https://github.com/tarruda/has","author":{"name":"Thiago de Arruda","email":"tpadilha84@gmail.com"},"contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"}],"repository":{"type":"git","url":"git://github.com/tarruda/has.git"},"bugs":{"url":"https://github.com/tarruda/has/issues"},"license":"MIT","licenses":[{"type":"MIT","url":"https://github.com/tarruda/has/blob/master/LICENSE-MIT"}],"main":"./src","dependencies":{"function-bind":"^1.1.1"},"devDependencies":{"@ljharb/eslint-config":"^12.2.1","eslint":"^4.19.1","tape":"^4.9.0"},"engines":{"node":">= 0.4.0"},"scripts":{"lint":"eslint .","pretest":"npm run lint","test":"tape test"},"gitHead":"4edf96f2dec87ad6b6e68482e8f6d7c8eb7e07e6","_id":"has@1.0.3","_npmVersion":"6.1.0","_nodeVersion":"10.3.0","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"dist":{"integrity":"sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==","shasum":"722d7cbfc1f6aa8241f16dd814e011e1f41e8796","tarball":"http://localhost:4545/npm/registry/has/has-1.0.3.tgz","fileCount":5,"unpackedSize":2770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFWeCCRA9TVsSAnZWagAAGqoP/3/GpNmBbr2IzYG0v9Rg\n0jDcyA0p95I8Tc1GbOsB0YWMY+VMs5I3tggHG4yjB8OskeR7GyItfcZpe9b5\nEGNr2Yq/eOKI602MTnWzHaAczxF887EVcXDISg1qDlzjTYWFMNms5jDxH6OT\nKD0SLHE1qRCASPxNZsJLoxT/dPaVfRC5QMIz8msEaI+qUu8p54cO0/DSLSlT\n5kCGKA5CfbfIODAmyvddsKgOW0at16XJ97f+qHhrI5q6HoYdM2jcLzPXxPiw\nSgPKXICus5TjFvRdBoOXa2diz9urPjQGzVsGoKs8W0Z9EeoxpiJInVRN1Hec\nabW1NzLvINbuFcG1I/CvNVXKGKwIafKg25ba/T/PHa/5rHY2+S4Y9Mj0SLFv\n8V7HWSQMGaG86kZjH9vgd7MPP39lBZXw5msjftiRVxiDiZYutbGzBymwHcYd\nzPVrzCfdNg9o2OsG8mjIBGntCwz9/Yrx7npK9mP97nZQ4EDDoDoCga5efkMP\nmT02Vru1cCdHOfRnM/tlr4Onf0umRhgzUfsbjdSsrGapGCTeVvBvnm6XKznZ\nw9HWfGyNaZiT1J0/pmnRMwxEp/xcKMaAOa1c2pisTGpjkbQloGQx77IwfBiO\nAVBfV3yBDWCgutiwEb4zg0RPPjlS1RCJDMzg+CsXT1u9H6St8MUtuBikkNr4\ndlxV\r\n=Dlp+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDi2aj8+sqrebhyEkv7F23LOzXMTe0zUrFZqtxhj4ubhQIhAM8njQhxRA7zu5nYQHE3+EN4rJa5+9EpwlJiBeEkLn54"}]},"maintainers":[{"name":"tarruda","email":"tpadilha84@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/has_1.0.3_1528129409940_0.08921093934264301"},"_hasShrinkwrap":false}},"readme":"# has\n\n> Object.prototype.hasOwnProperty.call shortcut\n\n## Installation\n\n```sh\nnpm install --save has\n```\n\n## Usage\n\n```js\nvar has = require('has');\n\nhas({}, 'hasOwnProperty'); // false\nhas(Object.prototype, 'hasOwnProperty'); // true\n```\n","maintainers":[{"email":"tpadilha84@gmail.com","name":"tarruda"},{"email":"ljharb@gmail.com","name":"ljharb"}],"time":{"modified":"2022-11-08T10:38:33.269Z","created":"2013-10-08T00:54:01.609Z","0.0.1":"2013-10-08T00:54:07.309Z","1.0.0":"2014-10-07T18:41:58.615Z","1.0.1":"2015-07-24T08:49:30.633Z","1.0.2":"2018-06-02T21:04:36.393Z","1.0.3":"2018-06-04T16:23:29.998Z"},"author":{"name":"Thiago de Arruda","email":"tpadilha84@gmail.com"},"repository":{"type":"git","url":"git://github.com/tarruda/has.git"},"users":{"getify":true,"bradleymeck":true,"akiva":true,"rsp":true,"nickeltobias":true,"tobiasnickel":true,"ahmed-dinar":true,"maximusx":true,"tjfwalker":true,"iori20091101":true},"homepage":"https://github.com/tarruda/has","bugs":{"url":"https://github.com/tarruda/has/issues"},"readmeFilename":"README.md","contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"}],"license":"MIT"}
\ No newline at end of file
diff --git a/cli/tests/testdata/npm/registry/object-keys/object-keys-1.1.1.tgz b/cli/tests/testdata/npm/registry/object-keys/object-keys-1.1.1.tgz
new file mode 100644
index 0000000000..3e52f10dfe
Binary files /dev/null and b/cli/tests/testdata/npm/registry/object-keys/object-keys-1.1.1.tgz differ
diff --git a/cli/tests/testdata/npm/registry/object-keys/registry.json b/cli/tests/testdata/npm/registry/object-keys/registry.json
new file mode 100644
index 0000000000..bbfedec12b
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/object-keys/registry.json
@@ -0,0 +1 @@
+{"_id":"object-keys","_rev":"104-fff9f09b12add81f4389e3e50a2ff098","name":"object-keys","description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","dist-tags":{"latest":"1.1.1"},"versions":{"0.0.1":{"name":"object-keys","version":"0.0.1","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/kriskowal/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test.js"},"repository":{"type":"git","url":"git://github.com/ljharb/objectkeys.git"},"keywords":["Object.keys","keys","ES5","shim"],"devDependencies":{"tap":"~0.4.1"},"testling":{"files":"test/index.js","browsers":["ie/6..latest","firefox/3..latest","firefox/nightly","chrome/4..latest","chrome/canary","opera/10..latest","opera/next","safari/5..latest","ipad/6..latest","iphone/6..latest"]},"_id":"object-keys@0.0.1","dist":{"shasum":"ab917307b1042981453e094c41049246e99602d6","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.0.1.tgz","integrity":"sha512-/aM4V/OT388JOkoQJ57Gxeg43O8qI89rybO5CgLo1i4Z1rI/LXnC8RTdZZxmpxC273gOECNPb2qW9jerijQAwA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHpOEk4wRRe8+XOD49ps98iZzl4U4078lTFmqufTj3eQAiBw262wqFt1AZUWcLtoA6F+KvHVEkwE97aLaycw/EDPYw=="}]},"_from":".","_npmVersion":"1.2.15","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"deprecated":"Please update to the latest object-keys"},"0.1.0":{"name":"object-keys","version":"0.1.0","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/kriskowal/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"devDependencies":{"tap":"~0.4.1","tape":"~0.3.2"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0","firefox/15.0..latest","firefox/nightly","chrome/4.0","chrome/22.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/5.0.5..latest","ipad/6.0..latest","iphone/6.0..latest"]},"_id":"object-keys@0.1.0","dist":{"shasum":"f60a5d0b3f878089a4b9645b26e43df09436dbb8","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.1.0.tgz","integrity":"sha512-nmv/hFMWJmfEUuMUjE2m2ZDmwi4Q9RDeZto0S04PfD8wnwINgJT5Raib18UT/EAa/A3tIhpEPHewLX83OCRSzQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDvuUuQtK1x8psnRhTMpgczIFTWOSlTu0hV+851vuWrKQIgYdB+mrlzs3w/Bw74j9ju2BZEFbhiDMpRVvbTsHBJodc="}]},"_from":".","_npmVersion":"1.2.14","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"deprecated":"Please update to the latest object-keys"},"0.1.1":{"name":"object-keys","version":"0.1.1","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/kriskowal/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{"indexof":"~0.0.1","is-extended":"~0.0.4"},"devDependencies":{"tap":"~0.4.1","tape":"~0.3.3"},"testling":{"files":"test.js","browsers":["iexplore/6.0..latest","firefox/3.0","firefox/15.0..latest","firefox/nightly","chrome/4.0","chrome/22.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/5.0.5..latest","ipad/6.0..latest","iphone/6.0..latest"]},"_id":"object-keys@0.1.1","dist":{"shasum":"e35f1c8d9cbc5fe503c1b13ad57c334e3f637b3e","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.1.1.tgz","integrity":"sha512-0YAQMhYdszhy3qw0CZHKp2/+pw0VIBSbb5G5oMItAXW384Qbi6XRg4J8Q9O8kg43WVcFyFUT+GCCTt/rz6890w==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBRuwzoWqmGMBA4CCDak783BfOsQ6ycfmHlEnZqC5gWnAiAKsWT0JVhP/+dBICcDXulO75XJTJjG4yGibvL1UpxT6A=="}]},"_from":".","_npmVersion":"1.2.14","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"deprecated":"Please update to the latest object-keys"},"0.1.2":{"name":"object-keys","version":"0.1.2","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/kriskowal/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{"indexof":"~0.0.1","is-extended":"~0.0.5"},"devDependencies":{"tap":"~0.4.1","tape":"~0.3.3"},"testling":{"files":"test.js","browsers":["iexplore/6.0..latest","firefox/3.0","firefox/15.0..latest","firefox/nightly","chrome/4.0","chrome/22.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/5.0.5..latest","ipad/6.0..latest","iphone/6.0..latest"]},"_id":"object-keys@0.1.2","dist":{"shasum":"df74e8662eb0e8b5ee64fc8eda750c2db4debc7b","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.1.2.tgz","integrity":"sha512-WMWSee5aYXB5Iu7bfsD3wSdO9TaYqwrIfqHWoQQHIx3XbvhslTBAyqY+tOp9DpaNGjE75vM9IhwMFbDcEs0Ntw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHy+GeOlh7SUtU0NRAUk02ZvdJF+bUX7/XN9IrbewUc3AiEAmkuTbEEx+bYIAsEWhbMxFynWo+j5mtMl0weHc2vIqvE="}]},"_from":".","_npmVersion":"1.2.15","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"deprecated":"Please update to the latest object-keys"},"0.1.3":{"name":"object-keys","version":"0.1.3","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/kriskowal/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{"foreach":"~2.0.1","indexof":"~0.0.1","is-extended":"~0.0.5"},"devDependencies":{"tape":"~0.3.3"},"testling":{"files":"test.js","browsers":["iexplore/6.0..latest","firefox/3.0","firefox/15.0..latest","firefox/nightly","chrome/4.0","chrome/22.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/5.0.5..latest","ipad/6.0..latest","iphone/6.0..latest"]},"_id":"object-keys@0.1.3","dist":{"shasum":"201972597dfdbaef2512144a969351b67340966d","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.1.3.tgz","integrity":"sha512-P40wNJQL4FoACelJjjI0N0iO3oRfiy0Pvym34FvBmJbArXAmIj0u8p8dLPFjKtN3Bikqb2I3kYJLjS2RnIP2KQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEr/gqaTEuK7tXhDHdY5SxqAScNngeW1qXRGkAYdsqLEAiEAtFJBjZIJKSFL9yK6M4lUIVRqPyeLc4o5JBFufjryS9A="}]},"_from":".","_npmVersion":"1.2.14","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"deprecated":"Please update to the latest object-keys"},"0.1.4":{"name":"object-keys","version":"0.1.4","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/kriskowal/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{"foreach":"~2.0.1","indexof":"~0.0.1","is":"~0.2.0"},"devDependencies":{"tape":"~0.3.3"},"testling":{"files":"test.js","browsers":["iexplore/6.0..latest","firefox/3.0","firefox/15.0..latest","firefox/nightly","chrome/4.0","chrome/22.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/5.0.5..latest","ipad/6.0..latest","iphone/6.0..latest"]},"_id":"object-keys@0.1.4","dist":{"shasum":"094b203cdc23c0d61b04f13cc8135fe964cc314a","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.1.4.tgz","integrity":"sha512-EhLn1BDThRMKDUnB4a9Pu99R0V7FvciLi4M2Y7fyoa/qnl202sd4RhLuYCL6IfR0f133TaWpP4JgNPRpMBac6Q==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDCCt8XSKs0l1ykra1eRaTbBjfgyO/RAqXJZUWBu0LmewIhAP0IMkVWwACYSzm5FboLyb096r0WXAhEQaQS2m74C3E7"}]},"_from":".","_npmVersion":"1.2.14","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"deprecated":"Please update to the latest object-keys"},"0.1.5":{"name":"object-keys","version":"0.1.5","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/kriskowal/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{"foreach":"~2.0.1","indexof":"~0.0.1","is":"~0.2.1"},"devDependencies":{"tape":"~0.3.3"},"testling":{"files":"test.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest"]},"_id":"object-keys@0.1.5","dist":{"shasum":"ff9b7518e468804c4066ac553c5d452ec8ffbb27","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.1.5.tgz","integrity":"sha512-FWlklzi/z7zzTVU/hnBrUUyiMRw894gIwpgUCkeFqWSXD/m3y7KUzbcWe6oJWPr+PEZ/ACLa/lDWLIQsYmY0ng==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBdydC523dr2nuIF2D77vsdvVS6m7etNCltX7XRKBrgKAiAzGtetgYZd9SXs1ixPt+EqqAMeabvo92SNOZHVRFqXbQ=="}]},"_from":".","_npmVersion":"1.2.14","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"deprecated":"Please update to the latest object-keys"},"0.1.6":{"name":"object-keys","version":"0.1.6","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/kriskowal/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{"foreach":"~2.0.1","indexof":"~0.0.1","is":"~0.2.2"},"devDependencies":{"tape":"~0.3.3"},"testling":{"files":"test.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest"]},"_id":"object-keys@0.1.6","dist":{"shasum":"2d8d2c3e3f57979d08e56c5a72532750e8fc9aae","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.1.6.tgz","integrity":"sha512-0EVnJ1F9QV4PMwv/hVwr1Ww/4AmGuHC4Wk1TlkWzUjvxZOZsD/I3jYrP3Cj1wX0C4i5fmAlopJdnQ0XiHOup+Q==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB42TE8/g3tYNAcNlrGjmxVF0slnQzgQqHN6Ozb32j5cAiAb8eJ+WLFiVR5jEpIgg7FddzSK6CcDOrZS7fhOEaEVAA=="}]},"_from":".","_npmVersion":"1.2.14","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"deprecated":"Please update to the latest object-keys"},"0.1.7":{"name":"object-keys","version":"0.1.7","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/kriskowal/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{"foreach":"~2.0.1","indexof":"~0.0.1","is":"~0.2.3"},"devDependencies":{"tape":"~0.3.3"},"testling":{"files":"test.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest"]},"_id":"object-keys@0.1.7","dist":{"shasum":"fefce99868aeb040f357b3d3aa29ad26ec30bbd2","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.1.7.tgz","integrity":"sha512-q2+Sfmxqz5jDT7Ri0GZmZog2DCmsYzUo39+ESQFgE6AYSTITCZnrhp5thlTTWKxP0ilN23pvE5voVH2SAQp73Q==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC0ZbU1lhEEgxMCvgRZhnhW4CeB2kRvMvEeAQGqoxfxHgIgHu6pVbufE1cs9nnihYjZrfi6oEN4sQDd1+IVeMnv9us="}]},"_from":".","_npmVersion":"1.2.14","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"deprecated":"Please update to the latest object-keys"},"0.1.8":{"name":"object-keys","version":"0.1.8","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/kriskowal/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{"foreach":"~2.0.1","indexof":"~0.0.1","is":"~0.2.6"},"devDependencies":{"tape":"~1.0.2"},"testling":{"files":"test.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest"]},"_id":"object-keys@0.1.8","dist":{"shasum":"d40164df81104b0da49edfa6aba9dd29eb480293","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.1.8.tgz","integrity":"sha512-QVLwfAl2DJtsOVW8BXxa8g9gjzqwAJijFj/hTCOknQ5uIfonbZIEeX+asYCgq93HYkfcMkWL51H6z3XLwALVaw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEstxBPiFXo98Vg3f5JaR5PY3HzLTVOKyPP3xJxIHeCHAiAJIjSWZJuR7zKuGcfALY9bv20LttxAEtWMngbhCcVELA=="}]},"_from":".","_npmVersion":"1.2.18","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"deprecated":"Please update to the latest object-keys"},"0.2.0":{"name":"object-keys","version":"0.2.0","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/kriskowal/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{"foreach":"~2.0.1","indexof":"~0.0.1","is":"~0.2.6"},"devDependencies":{"tape":"~1.0.2"},"testling":{"files":"test.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest"]},"_id":"object-keys@0.2.0","dist":{"shasum":"cddec02998b091be42bf1035ae32e49f1cb6ea67","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.2.0.tgz","integrity":"sha512-XODjdR2pBh/1qrjPcbSeSgEtKbYo7LqYNq64/TPuCf7j9SfDD3i21yatKoIy39yIWNvVM59iutfQQpCv1RfFzA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDHqI4i8TCwYU0W7hvKd5jX2WFPHuJ0kESFyw/as3++xgIgaKT/CU6g2wUXjGaGccKcj5U4akUaDasKizs8P3yDewE="}]},"_from":".","_npmVersion":"1.2.18","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"deprecated":"Please update to the latest object-keys"},"0.3.0":{"name":"object-keys","version":"0.3.0","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/kriskowal/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{"foreach":"~2.0.3","is":"~0.2.6"},"devDependencies":{"tape":"~1.0.2","indexof":"~0.0.1"},"testling":{"files":"test.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest"]},"bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"_id":"object-keys@0.3.0","dist":{"shasum":"4ce2945fee6669cf98424bbaa0f59c244ff97f1d","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.3.0.tgz","integrity":"sha512-5NWmqk9N0NPSzhUAjJwjA1fbpYkmCyc3DRpIObOIsOTEz98JZg8fiJUbnxKofPrRXXW/J5Sh0M4pku7my7KHWw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDt2X4Q2m0E/f+ITcYDQdhb9WZQobOe3l/s8X+WttvWEAiAn0ThWjlLuWOUW3FrAinp3k15grW86MXXMLNCLKBiOpg=="}]},"_from":".","_npmVersion":"1.2.21","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"deprecated":"Please update to the latest object-keys"},"0.4.0":{"name":"object-keys","version":"0.4.0","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/kriskowal/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test/index.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"~2.0.3","is":"~0.2.6","tape":"~1.0.4","indexof":"~0.0.1"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"_id":"object-keys@0.4.0","dist":{"shasum":"28a6aae7428dd2c3a92f3d95f21335dd204e0336","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.4.0.tgz","integrity":"sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHUDMLh3fWS5OpydQINZqo8WFrJ3lqEJiDuN+YFRsxG3AiBbbYCG5+dD0UXyu+R6+L4BfEXZJeODELgzbKLRDEEWOw=="}]},"_from":".","_npmVersion":"1.3.5","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"deprecated":""},"0.5.0":{"name":"object-keys","version":"0.5.0","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test/index.js","coverage":"covert test/index.js","coverage-quiet":"covert test/index.js --quiet"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"~2.0.4","is":"~0.2.7","tape":"~2.3.2","indexof":"~0.0.1","covert":"~0.3.0"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys","_id":"object-keys@0.5.0","dist":{"shasum":"09e211f3e00318afc4f592e36e7cdc10d9ad7293","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.5.0.tgz","integrity":"sha512-2GU36PPj0BVaGl9JDw1zY5vkLMV1hQ1QtI+PoBq7f5bZKY2j/7IO0uQDv0UcuBhimMYnditq7dz+uO9C1TXV4w==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDMwauwpEiIxU1RlG+eAIRnOLrboadeDQRORnvEQufqswIhANVI50TQxUwOhs2291FQ2NIdlE1uCKDjOx8jTVsGXZEn"}]},"_from":".","_npmVersion":"1.3.24","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{}},"0.5.1":{"name":"object-keys","version":"0.5.1","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"node test/index.js","coverage":"covert test/index.js","coverage-quiet":"covert test/index.js --quiet"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"~2.0.4","is":"~0.3.0","tape":"~2.10.2","indexof":"~0.0.1","covert":"~0.3.1"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys","_id":"object-keys@0.5.1","dist":{"shasum":"0eb20ffa0ce7c01977648681b42c515f297d2cc1","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.5.1.tgz","integrity":"sha512-VVh5OqHlY0N4Hueq9KteojSoj8BmEZeKC+nFyAmQFGF37dJSbcFB4jNhV7+6Xnn6t4t3jh0P0Cuy0hEA+xq+Mg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDoseRCnmntQ8ISi56+7YstplMr/rq01BE5OsLrcg/b6gIhAJKIVTCp4DmdXm8LavjKRr4lG/KH6m/RyVA72NGkvDSc"}]},"_from":".","_npmVersion":"1.4.3","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{}},"0.6.0":{"name":"object-keys","version":"0.6.0","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"npm run lint && node test/index.js","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"jscs test/*.js *.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"~2.0.4","is":"~0.3.0","tape":"~2.13.3","indexof":"~0.0.1","covert":"~0.4.0","jscs":"~1.5.8"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"3cbf74b330bb04f263a96d59925db5704c08968c","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys","_id":"object-keys@0.6.0","_shasum":"4638690dfaf1e65a63d43b5855d2f6ce04aeef6d","_from":".","_npmVersion":"1.4.21","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"dist":{"shasum":"4638690dfaf1e65a63d43b5855d2f6ce04aeef6d","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.6.0.tgz","integrity":"sha512-NwTyBxMHbTVCd46WsQlY4WMwYoJ+PXkIkU6x/S22usMJQewtKMrwPAV9jtB6HBXnL4+EzaXQrtllK0MPl+V4PQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEAekrpsVGBhFATo6EM1rcjVMSHjzwnEm8OSWZY5YhYqAiEA1YnRXNwRpg9sHlQweTFu1/6zpLR4rTQ50u+odWruJ+o="}]},"directories":{}},"0.6.1":{"name":"object-keys","version":"0.6.1","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"npm run lint && node test/index.js","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"jscs test/*.js *.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"~2.0.4","is":"~2.0.0","tape":"~2.14.0","indexof":"~0.0.1","covert":"~1.0.0","jscs":"~1.5.8"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"cfa534edc801eef5a3fd01512b30b025d177a79a","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys","_id":"object-keys@0.6.1","_shasum":"ed8d052b3662b093c9ee00152c259815c0db4d3c","_from":".","_npmVersion":"1.4.23","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"dist":{"shasum":"ed8d052b3662b093c9ee00152c259815c0db4d3c","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-0.6.1.tgz","integrity":"sha512-yFH+vVBczUKglNkPAb96wIWXv1AqdR4PCdoL8fYt6+uqm/Ucn4G7NVOgI54GG6Pai8yswIqzZIz0kLq4/3egQQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDhFPCvJ7VZ/jc+5VfjHDrTEIbXXMDA+p7qQPbB7D7QhgIhAIpfJj4sqHZfgrQO3bYBRwxqahD1d23Zea/rSaIZhqJ+"}]},"directories":{}},"1.0.0":{"name":"object-keys","version":"1.0.0","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"npm run lint && node test/index.js","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"jscs test/*.js *.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"~2.0.4","is":"~2.0.0","tape":"~2.14.0","indexof":"~0.0.1","covert":"~1.0.0","jscs":"~1.5.8"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"f78356a5eda9b059acdc841607edbd3940aed477","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys","_id":"object-keys@1.0.0","_shasum":"1b66cc8cafc27391944098216726f746b15c2a30","_from":".","_npmVersion":"1.4.23","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"dist":{"shasum":"1b66cc8cafc27391944098216726f746b15c2a30","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.0.0.tgz","integrity":"sha512-7zE2Pyy6jZ30PT8LSB/J+WfBvd8gw6PClm9Ilhq/S42rZ32NiDgBD0GtBDcmeObLtRIAC087WNyCW4QLAF/F1A==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD4P78vN3qENInRoJidkqsanNRVgDGq1o1IDbclEaeAugIgGF9eS40md3HbBTo2TP+LbsCZhL+mjAqG91O7hwXfVPY="}]},"directories":{}},"1.0.1":{"name":"object-keys","version":"1.0.1","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"npm run lint && node test/index.js","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"jscs test/*.js *.js"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"~2.0.4","is":"~2.0.1","tape":"~2.14.0","indexof":"~0.0.1","covert":"~1.0.0","jscs":"~1.6.1"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"2ecbaaa0405c2f03e8b669ccf4b70376318a8f8b","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys","_id":"object-keys@1.0.1","_shasum":"55802e85842c26bbb5ebbc157abf3be302569ba8","_from":".","_npmVersion":"1.4.23","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"dist":{"shasum":"55802e85842c26bbb5ebbc157abf3be302569ba8","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.0.1.tgz","integrity":"sha512-DsJ69TA3wPICBmxYj6rij6uGKvKb9s2mtebzhuN/eI1GabJ3xC7fZ7PWjW0GS06hSclD0GxKGGAHQo5P7R2ZTg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD4gYBznqPY/77jmrmzAiN5nRqHR25mrZuveDAAkyBi/wIhANnwIwT2H5eNkTIWUt3c+j4p5ovDyUM83vj0pvCHuFBL"}]},"directories":{}},"1.0.2":{"name":"object-keys","version":"1.0.2","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"npm run lint && node test/index.js && npm run security","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"npm run jscs && npm run eslint","jscs":"jscs test/*.js *.js","eslint":"eslint test/*.js *.js","eccheck":"editorconfig-tools check *.js **/*.js > /dev/null","security":"nsp package"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"~2.0.5","is":"~2.2.0","tape":"~3.0.3","indexof":"~0.0.1","covert":"1.0.0","jscs":"~1.9.0","editorconfig-tools":"~0.0.1","nsp":"~0.5.2","eslint":"~0.10.2"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"06f2d46a85a0be12fc9e0377e3ce7bef32be5eb3","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys","_id":"object-keys@1.0.2","_shasum":"810205bc58367a1d9dcf9e8b7b8c099ef2503c6c","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"dist":{"shasum":"810205bc58367a1d9dcf9e8b7b8c099ef2503c6c","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.0.2.tgz","integrity":"sha512-QaJ3L+WfJ2mCirdIvDbXRW8q76+WnsITenRbpAAJ2Z/fPcKaXvRAn94rv1YzwUGqxj/m08vu3HBvR6WdxXXRsw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAh4SRfmAsWSFsJGW/zwtEkL5i6WpjFvxOkmwnfDW/LTAiBr8+G5luLEkszDkl+ANwlTeCyO/PceL8aRv/UO/XqUtw=="}]},"directories":{}},"1.0.3":{"name":"object-keys","version":"1.0.3","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"npm run lint && node test/index.js && npm run security","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"npm run jscs && npm run eslint","jscs":"jscs test/*.js *.js","eslint":"eslint test/*.js *.js","eccheck":"editorconfig-tools check *.js **/*.js > /dev/null","security":"nsp package"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"~2.0.5","is":"~2.2.0","tape":"~3.0.3","indexof":"~0.0.1","covert":"1.0.0","jscs":"~1.9.0","editorconfig-tools":"~0.0.1","nsp":"~0.5.2","eslint":"~0.11.0"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"f0fc8ccdf81843fa7aa88c85777cf717c3ead129","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys","_id":"object-keys@1.0.3","_shasum":"1b679dbec65103da488edb32f782bd9a15e3de0a","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"dist":{"shasum":"1b679dbec65103da488edb32f782bd9a15e3de0a","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.0.3.tgz","integrity":"sha512-C9AHglIN4DeikXJitZAmcls7Ics4QJr0QnVXFtK4wVly8zo0udlW96Hfw0kLQ0LqiE21Z2HgBMIS7C6/s4L2Tg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGYBo/Zp8ilkQTBiGuDQvrpFHmLCZGxdimx6CQPuVK4PAiEAofwl6l/SVKlk89+QpAy6VRVczBPULX48M5hGH78V7Vc="}]},"directories":{}},"1.0.4":{"name":"object-keys","version":"1.0.4","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"npm run lint && node test/index.js && npm run security","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"npm run jscs && npm run eslint","jscs":"jscs test/*.js *.js","eslint":"eslint test/*.js *.js","eccheck":"editorconfig-tools check *.js **/*.js > /dev/null","security":"nsp package"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"^2.0.5","is":"^3.0.1","tape":"^4.0.0","indexof":"^0.0.1","covert":"^1.1.0","jscs":"^1.13.1","editorconfig-tools":"^0.1.1","nsp":"^1.0.1","eslint":"^0.21.0"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"fc869b3088d6047bcbf42e534304ffe034b06cb0","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys#readme","_id":"object-keys@1.0.4","_shasum":"b115f96a7ee837df1517fbc5bd91ea965e37685c","_from":".","_npmVersion":"2.9.0","_nodeVersion":"2.0.2","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"dist":{"shasum":"b115f96a7ee837df1517fbc5bd91ea965e37685c","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.0.4.tgz","integrity":"sha512-+MtQIw3zdFntcjAKeWGPRbCj0SZeCSN1Yhp1jAI1GmPgF6wCHTJkhJgfPE3kHgryFpX2MgFWQLcKsqHlSlPD9A==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDKrpjpmWkxUnjvqfhVyWqDoQh7rExWokqaM7GWI3do6wIhAONj4OP7k0W21ye/Mzi92MX8ageuQydTsWFK4cB75Zik"}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{}},"1.0.5":{"name":"object-keys","version":"1.0.5","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"npm run lint && node test/index.js && npm run security","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"npm run jscs && npm run eslint","jscs":"jscs test/*.js *.js","eslint":"eslint test/*.js *.js","eccheck":"editorconfig-tools check *.js **/*.js > /dev/null","security":"nsp package"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"^2.0.5","is":"^3.0.1","tape":"^4.0.0","indexof":"^0.0.1","covert":"^1.1.0","jscs":"^1.13.1","editorconfig-tools":"^0.1.1","nsp":"^1.0.3","eslint":"^0.24.0"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"a6fb624febfdbde087b5637bedd5233054520b18","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys#readme","_id":"object-keys@1.0.5","_shasum":"84fe12516867496e97796a49db0a89399053fe06","_from":".","_npmVersion":"2.11.3","_nodeVersion":"2.3.2","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"dist":{"shasum":"84fe12516867496e97796a49db0a89399053fe06","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.0.5.tgz","integrity":"sha512-ads8edXgDSXcILPLzQa0i8HaXMSPoCj1SYW8C+W+fL8cTIcpxp8M3/wFu4ODfegdiKP9LEatqLbcd7noEtoL2g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAdMlFebBqVB9aq9/VQPFrEI72Ai91euXsNekOZS67lKAiB6/Y3cE8bKzOVtc/erkWzyFt9rjyc4HBMVfRy2SN9V2A=="}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{}},"1.0.6":{"name":"object-keys","version":"1.0.6","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"npm run lint && node test/index.js && npm run security","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"npm run jscs && npm run eslint","jscs":"jscs test/*.js *.js","eslint":"eslint test/*.js *.js","eccheck":"editorconfig-tools check *.js **/*.js > /dev/null","security":"nsp package"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"^2.0.5","is":"^3.0.1","tape":"^4.0.0","indexof":"^0.0.1","covert":"^1.1.0","jscs":"^1.13.1","editorconfig-tools":"^0.1.1","nsp":"^1.0.3","eslint":"^0.24.0"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"3b0fbe74b40b5d78661461339f09a82f45a0a345","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys#readme","_id":"object-keys@1.0.6","_shasum":"f910c99bb3f57d8ba29b6580e1508eb0ebbfc177","_from":".","_npmVersion":"2.11.3","_nodeVersion":"2.3.3","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"dist":{"shasum":"f910c99bb3f57d8ba29b6580e1508eb0ebbfc177","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.0.6.tgz","integrity":"sha512-JFO9tB3N/R17IA/IVKb3K0amIIpaR5T7CSg9z47uRXOFv9Kw1LOm1t3NB6FjosNIuKqNwpExODZqNnJb8zIZgQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHoYa/sXYRU7F0BxiOvK1r/U1E2lj0iXpqZwIHQMjc29AiA66y8fVi2dNIyHyihDxm0gL/8pAm04MHpq25c6K5c33g=="}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{}},"1.0.7":{"name":"object-keys","version":"1.0.7","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"npm run lint && node test/index.js && npm run security","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"npm run jscs && npm run eslint","jscs":"jscs test/*.js *.js","eslint":"eslint test/*.js *.js","eccheck":"editorconfig-tools check *.js **/*.js > /dev/null","security":"nsp package"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"^2.0.5","is":"^3.0.1","tape":"^4.0.0","indexof":"^0.0.1","covert":"^1.1.0","jscs":"^1.13.1","editorconfig-tools":"^0.1.1","nsp":"^1.0.3","eslint":"^1.0.0-rc-1"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"c0c183e0aaed86487218f46127fcebec9258e84e","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys#readme","_id":"object-keys@1.0.7","_shasum":"e7d117261139d6acac8f0afabf261d700ebb0b93","_from":".","_npmVersion":"2.13.0","_nodeVersion":"2.4.0","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"dist":{"shasum":"e7d117261139d6acac8f0afabf261d700ebb0b93","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.0.7.tgz","integrity":"sha512-SLdJAA8lTumufd2VJDOEXwfb81eE/ujQccVmFsofTnoPv1RvHqSlrMjDkq06lTaqnJxCDaY3d8rUwUJIeFk5sA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCkZcFwsc8+AngNfDPYrHnGBGkQHFSyW+hmnv8O33Ng3AIgSwHqYxg+rHqOlyoYbs/OqwzctpcSXfGjgbwpsa34dDw="}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{}},"1.0.8":{"name":"object-keys","version":"1.0.8","author":{"name":"Jordan Harband"},"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"npm run lint && node test/index.js && npm run security","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"npm run jscs && npm run eslint","jscs":"jscs test/*.js *.js","eslint":"eslint test/*.js *.js","eccheck":"editorconfig-tools check *.js **/*.js > /dev/null","security":"nsp package"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"^2.0.5","is":"^3.1.0","tape":"^4.2.1","indexof":"^0.0.1","covert":"^1.1.0","jscs":"^2.3.1","editorconfig-tools":"^0.1.1","nsp":"^1.1.0","eslint":"^1.6.0","@ljharb/eslint-config":"^1.3.0"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"f094a4832583959d0a0a132ea80efa2f44a5d58e","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys#readme","_id":"object-keys@1.0.8","_shasum":"9a71ce236e200a943d7fbddba25332fba057c205","_from":".","_npmVersion":"2.14.7","_nodeVersion":"4.2.1","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"dist":{"shasum":"9a71ce236e200a943d7fbddba25332fba057c205","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.0.8.tgz","integrity":"sha512-yMyMdHyEjnPMnRpKnwOQLtTcS/2DQCItvwFh/A0RFvorh1aWqsIO46ZzfkaT0CmPXcKjCtrq7DhZo+unsR99hA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFHd2Fd8fYxiukgf0PCCQ4pAuKxhEwsecMScXYwTs/ntAiEA4b6t2m3zTXFek1FJZm3TEuhYZFwPhYCknyORUWHNXXo="}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{}},"1.0.9":{"name":"object-keys","version":"1.0.9","author":{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},"contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},{"name":"Raynos","email":"raynos2@gmail.com"},{"name":"Nathan Rajlich","email":"nathan@tootallnate.net"},{"name":"Ivan Starkov","email":"istarkov@gmail.com"},{"name":"Gary Katsevman","email":"git@gkatsev.com"}],"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"test":"npm run lint && node test/index.js && npm run security","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"npm run jscs && npm run eslint","jscs":"jscs test/*.js *.js","eslint":"eslint test/*.js *.js","eccheck":"editorconfig-tools check *.js **/*.js > /dev/null","security":"nsp package"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"^2.0.5","is":"^3.1.0","tape":"^4.2.1","indexof":"^0.0.1","covert":"^1.1.0","jscs":"^2.3.4","editorconfig-tools":"^0.1.1","nsp":"^1.1.0","eslint":"^1.7.2","@ljharb/eslint-config":"^1.4.1"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"e4331f920ff49824ad999b3449005349e31139f9","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys#readme","_id":"object-keys@1.0.9","_shasum":"cabb1202d9a7af29b50edface8094bb46da5ea21","_from":".","_npmVersion":"2.14.7","_nodeVersion":"4.2.1","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"dist":{"shasum":"cabb1202d9a7af29b50edface8094bb46da5ea21","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.0.9.tgz","integrity":"sha512-xRGFTKkyFuP9AilRkEw4KfMPqaD9spcc6PVVPiOxAau61l+m/4zHUW6crXGtSt8lBfXD2vgnqNFFY8cr8NOBTQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBJQxUXhh6hPZURj6mH0fOxfW7ePLUq0TXl/tfNBFT3aAiAsoqWESRjpTfRmFkKnYvuJgqI1ovXv42EHD2LzxAs71A=="}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{}},"1.0.10":{"name":"object-keys","version":"1.0.10","author":{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},"contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},{"name":"Raynos","email":"raynos2@gmail.com"},{"name":"Nathan Rajlich","email":"nathan@tootallnate.net"},{"name":"Ivan Starkov","email":"istarkov@gmail.com"},{"name":"Gary Katsevman","email":"git@gkatsev.com"}],"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"pretest":"npm run --silent lint","test":"npm run --silent tests-only","posttest":"npm run --silent security","tests-only":"node test/index.js","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"npm run --silent jscs && npm run --silent eslint","jscs":"jscs test/*.js *.js","eslint":"eslint test/*.js *.js","security":"nsp check"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"^2.0.5","is":"^3.1.0","tape":"^4.6.0","indexof":"^0.0.1","covert":"^1.1.0","jscs":"^3.0.6","nsp":"^2.5.0","eslint":"^3.0.0","@ljharb/eslint-config":"^6.0.0"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"a12ae2c01a443afb43414ab844175d2b6d5cd50a","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys#readme","_id":"object-keys@1.0.10","_shasum":"57e67f7041b66d145c45136fa8040a32717f7465","_from":".","_npmVersion":"3.9.5","_nodeVersion":"6.2.2","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"dist":{"shasum":"57e67f7041b66d145c45136fa8040a32717f7465","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.0.10.tgz","integrity":"sha512-fKnqZ/+BvdAsCto14RQRo1q0W9ObXswVgq2Vc/y/OQXfGVom9jEJ193KpHjgkO7QJNCxy8hBWTDBYUsSBExYFA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCUJa2v4dH/fEuWBmVFTYGyt6k+uRH9k63SnIhS07UPggIgarq6DuufB4ttn5xtTjnxChN0qzqHsyhVN2mhkaF1IBk="}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/object-keys-1.0.10.tgz_1467655315616_0.8326317083556205"},"directories":{}},"1.0.11":{"name":"object-keys","version":"1.0.11","author":{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},"contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},{"name":"Raynos","email":"raynos2@gmail.com"},{"name":"Nathan Rajlich","email":"nathan@tootallnate.net"},{"name":"Ivan Starkov","email":"istarkov@gmail.com"},{"name":"Gary Katsevman","email":"git@gkatsev.com"}],"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"pretest":"npm run --silent lint","test":"npm run --silent tests-only","posttest":"npm run --silent security","tests-only":"node test/index.js","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"npm run --silent jscs && npm run --silent eslint","jscs":"jscs test/*.js *.js","eslint":"eslint test/*.js *.js","security":"nsp check"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"foreach":"^2.0.5","is":"^3.1.0","tape":"^4.6.0","indexof":"^0.0.1","covert":"^1.1.0","jscs":"^3.0.6","nsp":"^2.5.0","eslint":"^3.0.0","@ljharb/eslint-config":"^6.0.0"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"3f869cc4b9f0f0489b2af7e80964f90d6c4403a4","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys#readme","_id":"object-keys@1.0.11","_shasum":"c54601778ad560f1142ce0e01bcca8b56d13426d","_from":".","_npmVersion":"3.9.5","_nodeVersion":"6.2.2","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"dist":{"shasum":"c54601778ad560f1142ce0e01bcca8b56d13426d","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.0.11.tgz","integrity":"sha512-I0jUsqFqmQFOIhQQFlW8QDuX3pVqUWkiiavYj8+TBiS7m+pM9hPCxSnYWqL1hHMBb7BbQ2HidT+6CZ8/BT/ilw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGVatYL5nqFjnyTPO0/FYHebFDZUNL6H4evuOwJXOd20AiAVQtHX+GpfjVa90v7F8y+Z0Nkf/bKGSVeNf/Sqys+gRg=="}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmOperationalInternal":{"host":"packages-16-east.internal.npmjs.com","tmp":"tmp/object-keys-1.0.11.tgz_1467740975903_0.8028358130250126"},"directories":{}},"1.0.12":{"name":"object-keys","version":"1.0.12","author":{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},"contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},{"name":"Raynos","email":"raynos2@gmail.com"},{"name":"Nathan Rajlich","email":"nathan@tootallnate.net"},{"name":"Ivan Starkov","email":"istarkov@gmail.com"},{"name":"Gary Katsevman","email":"git@gkatsev.com"}],"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"pretest":"npm run --silent lint","test":"npm run --silent tests-only","posttest":"npm run --silent security","tests-only":"node test/index.js","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"npm run --silent jscs && npm run --silent eslint","jscs":"jscs test/*.js *.js","eslint":"eslint test/*.js *.js","security":"nsp check"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"@ljharb/eslint-config":"^12.2.1","covert":"^1.1.0","eslint":"^4.19.1","foreach":"^2.0.5","indexof":"^0.0.1","is":"^3.2.1","jscs":"^3.0.7","nsp":"^3.2.1","tape":"^4.9.1"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"e3acd057c5b7be1029b3b9f6f69133292d77d558","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys#readme","_id":"object-keys@1.0.12","_npmVersion":"6.1.0","_nodeVersion":"10.4.1","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"dist":{"integrity":"sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==","shasum":"09c53855377575310cca62f55bb334abff7b3ed2","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.0.12.tgz","fileCount":11,"unpackedSize":28233,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKChkCRA9TVsSAnZWagAAVoIP/1jqNQKOVOx4jOpaSivw\nSRxdpzt236t7t9D5YnDgXhT5hrfbfajBz59CVtikezyYS3+ccurC/M2fTHno\nAT8VGxLmemptEoH7woqX27cFdWDFqyMlyfYZkC29w59+cXq44+J3+VFtyd8s\nV09lwj934D/DxdSCKZ/BVuZoffigow37yg7kIC9+VVS0em2XG3W633V8LQAF\nrRiUVSk0ne/BlO1TWV5fTmPQwranmUMnodZAqarVn2/vl0wN8rCTM9qGHdGH\nYWJNQC0ed73ZWOJN+C+OeQqtRdmjS/s5MbLrnMC7JdSQqEDFr6cuLf6TXYa8\nQmy4MCwN7IN1+XeUbDLsOQ1NdjIg9TVlybL5HjKiBjL5FYcjiZQHvtLYTOLa\n/x4eteDcVF8WObCLsUfrB3XuwH2sJX1tACds7IalOS0WLR2bHeBGjejQFyKK\n6k8strtCWMxaWt/nRSTOpZZfMz/HMtHmqVJ3C/VZGYvoexpt6EXqZm4Yemtx\n7AS82sEnfnKF92m/EXZbdP5Gz0fnAksKtzOncsFCOk7qomkD0PLNZkhIadfX\nWTOz9FW+gQNA+im76POpLk8EwQBFYIfTQesLVYB243Z3jH6O5EuTTkzclkWU\nVNXHIoouGL3S+1gPactA2lr6PM4G0hTkco98HSEvHMZpdgoHtz4Jx8xMtX6N\nGJLk\r\n=/f2l\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDBTqK5jg1fMSZC7viYJn8AgFqfaNKFJrUlPYMQVnIReQIgUtgVmrbrFyXy3Qupn7eBPqwBkzKQXN6D+aQtkn5/tEk="}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/object-keys_1.0.12_1529358434802_0.4383878957043432"},"_hasShrinkwrap":false},"1.1.0":{"name":"object-keys","version":"1.1.0","author":{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},"contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},{"name":"Raynos","email":"raynos2@gmail.com"},{"name":"Nathan Rajlich","email":"nathan@tootallnate.net"},{"name":"Ivan Starkov","email":"istarkov@gmail.com"},{"name":"Gary Katsevman","email":"git@gkatsev.com"}],"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"pretest":"npm run --silent lint","test":"npm run --silent tests-only","posttest":"npm run --silent audit","tests-only":"node test/index.js","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"eslint .","preaudit":"npm install --package-lock --package-lock-only","audit":"npm audit","postaudit":"rm package-lock.json"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"@ljharb/eslint-config":"^13.1.1","covert":"^1.1.1","eslint":"^5.13.0","foreach":"^2.0.5","indexof":"^0.0.1","is":"^3.3.0","tape":"^4.9.2"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"abd4ff039708a0166a57388b348730cbda4a1593","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys#readme","_id":"object-keys@1.1.0","_npmVersion":"6.5.0","_nodeVersion":"11.8.0","_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"dist":{"integrity":"sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==","shasum":"11bd22348dd2e096a045ab06f6c85bcc340fa032","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.1.0.tgz","fileCount":11,"unpackedSize":26395,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcYQZGCRA9TVsSAnZWagAAu9oP/3ed1S6D9BtHLFImT804\nudV4Mm/anfUE0jBXkJXLab4+qVIgkkqQOnEIg/Wl1ea/UHN/r21cRAaxVcdx\nqH6c5bcRpIr5gWNpcXnCgUVzOZHz2woa0jgZ4YQsAvt67m1lPAZBSppp98tx\nvw//RLPRQB0MNppFXRMLQEMABvIyP7bvNy6wK/SHp21hLuxzKdEmkwYwgKDN\nlhKdtpJQDrRjAmD6w2i8GW71S4K+Kis/ugzDGYX5eM4iwxJYQaKhtsgRJe34\nohUKHsVDe54wAYo4ZfJp+oJFLdJoY5DtYZM8VRkFlIya8X339oietwezlVoB\n0t3/8LuwJJZrG3tprTl6ek4d2AFL2Mf/xJhxSwKLY8B6UpFD+yNn4PuUt70u\ndea9T4zC9KE0swRdxLNRkKDTexCMlq3I3LZ28rH5MyPfcgdZMs1v9tGlaWhL\n7lXxQ5DIq/MSoMDKgOh8T0OBbvapnIfb6f1cqJgreZ0W0mpTm2Fu6joBfJKp\n7rp4erjunUXk4vNTsUxB9R/DMJgiefPRf7XPMG7evhO+MLlh3380Hf5DTf6E\nAqLqOZnAYLBzjigMxK8C3F0jxLVTFuwjA0Z3qejpWgwdl7RF7qyxekeSI1/g\n02ewe6HLdJilSbcev4i1zF8IKjGXFMoCWkVhpiz/q4DlNJbIv25C1482NbrF\nw+qE\r\n=/QeE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICDx3IvajHE/2cYYP/a/E25efhfP7DH10u0fg5s8927jAiEAmE9cIJNUB1ec8+cg2AkIDXhtz8ipkE80rlnTC4g6Pps="}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/object-keys_1.1.0_1549862469286_0.18723271962423693"},"_hasShrinkwrap":false},"1.1.1":{"name":"object-keys","version":"1.1.1","author":{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},"contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},{"name":"Raynos","email":"raynos2@gmail.com"},{"name":"Nathan Rajlich","email":"nathan@tootallnate.net"},{"name":"Ivan Starkov","email":"istarkov@gmail.com"},{"name":"Gary Katsevman","email":"git@gkatsev.com"}],"description":"An Object.keys replacement, in case Object.keys is not available. From https://github.com/es-shims/es5-shim","license":"MIT","main":"index.js","scripts":{"pretest":"npm run --silent lint","test":"npm run --silent tests-only","posttest":"npm run --silent audit","tests-only":"node test/index.js","coverage":"covert test/*.js","coverage-quiet":"covert test/*.js --quiet","lint":"eslint .","preaudit":"npm install --package-lock --package-lock-only","audit":"npm audit","postaudit":"rm package-lock.json"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"keywords":["Object.keys","keys","ES5","shim"],"dependencies":{},"devDependencies":{"@ljharb/eslint-config":"^13.1.1","covert":"^1.1.1","eslint":"^5.13.0","foreach":"^2.0.5","indexof":"^0.0.1","is":"^3.3.0","tape":"^4.9.2"},"testling":{"files":"test/index.js","browsers":["iexplore/6.0..latest","firefox/3.0..6.0","firefox/15.0..latest","firefox/nightly","chrome/4.0..10.0","chrome/20.0..latest","chrome/canary","opera/10.0..latest","opera/next","safari/4.0..latest","ipad/6.0..latest","iphone/6.0..latest","android-browser/4.2"]},"engines":{"node":">= 0.4"},"gitHead":"ba2c1989270c7de969aa8498fc3b7c8e677806f3","bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"homepage":"https://github.com/ljharb/object-keys#readme","_id":"object-keys@1.1.1","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==","shasum":"1c47f272df277f3b1daf061677d9c82e2322c60e","tarball":"http://localhost:4545/npm/registry/object-keys/object-keys-1.1.1.tgz","fileCount":11,"unpackedSize":26544,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcqWC7CRA9TVsSAnZWagAApOoQAIGgpq1xnaDCEe3hqJFt\n1fjwFbEKHTyK59hA/zVmvvR4ikMeltZc5KAIKNt5XyDBO4NtuRcA7E1b3D7C\nFX/IMtPwIq5OvZLWhEnGBNTmwlVq8PI9DwZ6AE2hWM4JAmkT5tay7QtjDAur\nYRdTEEB3eqWETNiaybnF9d1GLKuH4dKcM/v9yiHMp+qa9Ivpe9VtWRj7WTr+\nkxc39JZdSVGFbVYNCFkZ8oyj5VbLOtyMB++6JxbR9fYlZ06ibmT+XrFsz7CF\nr7hQ/XFHlyodg0pi34+YhlyDAsPIvk8DOxDoKGs4aFZ6EqZm3hVnWaAlKqgX\n3ikZAT9Z/4d9icoRkEhVMj7INySL4bSd7lFDIlwGruc4j6U6b6phhwgIlhQE\nMsnWmnLL7/AAaPB8oiNhb8Lt/9/jRJsAHwRBRH9NN/DH2VyP0F2hzp66L5dF\niIVw9YUIBCOzfRg5Gr0qd0GCGbIefcq4AomxsJEdBbV+3AFkJvj7dibMrSb7\nJcBVC/TwJCjNv+Ols7VZE+Yj6ZYbNrsuh5KbPkdFchg6qNgds1Dh1tH8GwrJ\nULdSyACz/0stHNGr8p+Boa85mDseApgozr42UUHdEQyohO1/meNonjDGJl9w\ncLlcHMcR7hnLzp4v54jcv+q74EDZa15iEk/ckLdYFoUXXhRhBJo0XyG43zf8\nEO6O\r\n=PzOq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDEXnivOzyezLnJgG1VUzWQj/PurnTkz1ZGYX4uYuK0JgIhAKKg/wGdPhn67UneiElcVANcbwapE+3GGhHJftwDOncL"}]},"maintainers":[{"name":"ljharb","email":"ljharb@gmail.com"}],"_npmUser":{"name":"ljharb","email":"ljharb@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/object-keys_1.1.1_1554604218505_0.17078310534837748"},"_hasShrinkwrap":false}},"readme":"#object-keys [![Version Badge][npm-version-svg]][package-url]\n\n[![Build Status][travis-svg]][travis-url]\n[![dependency status][deps-svg]][deps-url]\n[![dev dependency status][dev-deps-svg]][dev-deps-url]\n[![License][license-image]][license-url]\n[![Downloads][downloads-image]][downloads-url]\n\n[![npm badge][npm-badge-png]][package-url]\n\n[![browser support][testling-svg]][testling-url]\n\nAn Object.keys shim. Invoke its \"shim\" method to shim Object.keys if it is unavailable.\n\nMost common usage:\n```js\nvar keys = Object.keys || require('object-keys');\n```\n\n## Example\n\n```js\nvar keys = require('object-keys');\nvar assert = require('assert');\nvar obj = {\n\ta: true,\n\tb: true,\n\tc: true\n};\n\nassert.deepEqual(keys(obj), ['a', 'b', 'c']);\n```\n\n```js\nvar keys = require('object-keys');\nvar assert = require('assert');\n/* when Object.keys is not present */\ndelete Object.keys;\nvar shimmedKeys = keys.shim();\nassert.equal(shimmedKeys, keys);\nassert.deepEqual(Object.keys(obj), keys(obj));\n```\n\n```js\nvar keys = require('object-keys');\nvar assert = require('assert');\n/* when Object.keys is present */\nvar shimmedKeys = keys.shim();\nassert.equal(shimmedKeys, Object.keys);\nassert.deepEqual(Object.keys(obj), keys(obj));\n```\n\n## Source\nImplementation taken directly from [es5-shim][es5-shim-url], with modifications, including from [lodash][lodash-url].\n\n## Tests\nSimply clone the repo, `npm install`, and run `npm test`\n\n[package-url]: https://npmjs.org/package/object-keys\n[npm-version-svg]: http://versionbadg.es/ljharb/object-keys.svg\n[travis-svg]: https://travis-ci.org/ljharb/object-keys.svg\n[travis-url]: https://travis-ci.org/ljharb/object-keys\n[deps-svg]: https://david-dm.org/ljharb/object-keys.svg\n[deps-url]: https://david-dm.org/ljharb/object-keys\n[dev-deps-svg]: https://david-dm.org/ljharb/object-keys/dev-status.svg\n[dev-deps-url]: https://david-dm.org/ljharb/object-keys#info=devDependencies\n[testling-svg]: https://ci.testling.com/ljharb/object-keys.png\n[testling-url]: https://ci.testling.com/ljharb/object-keys\n[es5-shim-url]: https://github.com/es-shims/es5-shim/blob/master/es5-shim.js#L542-589\n[lodash-url]: https://github.com/lodash/lodash\n[npm-badge-png]: https://nodei.co/npm/object-keys.png?downloads=true&stars=true\n[license-image]: http://img.shields.io/npm/l/object-keys.svg\n[license-url]: LICENSE\n[downloads-image]: http://img.shields.io/npm/dm/object-keys.svg\n[downloads-url]: http://npm-stat.com/charts.html?package=object-keys\n\n","maintainers":[{"email":"ljharb@gmail.com","name":"ljharb"}],"time":{"modified":"2022-06-22T16:42:21.741Z","created":"2013-03-29T20:44:12.281Z","0.0.1":"2013-03-29T20:44:12.881Z","0.0.2":"2013-03-30T16:13:52.880Z","0.1.0":"2013-03-30T20:58:48.065Z","0.1.1":"2013-04-02T06:16:54.290Z","0.1.2":"2013-04-03T16:43:21.243Z","0.1.3":"2013-04-08T01:18:51.713Z","0.1.4":"2013-04-09T00:47:37.900Z","0.1.5":"2013-04-14T12:27:20.913Z","0.1.6":"2013-04-17T07:18:02.522Z","0.1.7":"2013-04-18T02:23:24.367Z","0.1.8":"2013-05-10T17:32:12.476Z","0.2.0":"2013-05-10T18:52:03.655Z","0.3.0":"2013-05-18T22:06:13.036Z","0.4.0":"2013-08-14T08:10:10.483Z","0.5.0":"2014-01-30T09:28:17.465Z","0.5.1":"2014-03-10T06:43:32.469Z","0.6.0":"2014-08-01T07:22:33.482Z","0.6.1":"2014-08-26T05:51:23.007Z","1.0.0":"2014-08-26T19:21:11.757Z","1.0.1":"2014-09-03T07:19:08.654Z","1.0.2":"2014-12-28T09:03:12.859Z","1.0.3":"2015-01-06T22:27:00.343Z","1.0.4":"2015-05-23T20:19:48.735Z","1.0.5":"2015-07-03T23:43:33.872Z","1.0.6":"2015-07-09T15:41:54.153Z","1.0.7":"2015-07-18T19:23:11.235Z","1.0.8":"2015-10-14T22:21:16.304Z","1.0.9":"2015-10-19T22:07:23.370Z","1.0.10":"2016-07-04T18:01:59.134Z","1.0.11":"2016-07-05T17:49:39.399Z","1.0.12":"2018-06-18T21:47:14.916Z","1.1.0":"2019-02-11T05:21:09.393Z","1.1.1":"2019-04-07T02:30:18.674Z"},"author":{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},"repository":{"type":"git","url":"git://github.com/ljharb/object-keys.git"},"users":{"claudiopro":true,"brostoch":true,"rocket0191":true},"readmeFilename":"README.md","homepage":"https://github.com/ljharb/object-keys#readme","keywords":["Object.keys","keys","ES5","shim"],"bugs":{"url":"https://github.com/ljharb/object-keys/issues"},"license":"MIT","contributors":[{"name":"Jordan Harband","email":"ljharb@gmail.com","url":"http://ljharb.codes"},{"name":"Raynos","email":"raynos2@gmail.com"},{"name":"Nathan Rajlich","email":"nathan@tootallnate.net"},{"name":"Ivan Starkov","email":"istarkov@gmail.com"},{"name":"Gary Katsevman","email":"git@gkatsev.com"}]}
\ No newline at end of file
diff --git a/cli/tests/testdata/npm/tarball_with_global_header/main.out b/cli/tests/testdata/npm/tarball_with_global_header/main.out
index caf351e2e3..ff211087b6 100644
--- a/cli/tests/testdata/npm/tarball_with_global_header/main.out
+++ b/cli/tests/testdata/npm/tarball_with_global_header/main.out
@@ -1 +1 @@
-[Class: Client]
+[class Client extends EventEmitter]
diff --git a/cli/tests/testdata/package_json/basic/main.info.out b/cli/tests/testdata/package_json/basic/main.info.out
index bf36f4f19e..3572c75e11 100644
--- a/cli/tests/testdata/package_json/basic/main.info.out
+++ b/cli/tests/testdata/package_json/basic/main.info.out
@@ -5,4 +5,4 @@ size: [WILDCARD]
file:///[WILDCARD]/main.ts (63B)
└─┬ file:///[WILDCARD]/lib.ts (166B)
- └── npm:@denotest/esm-basic@1.0.0 (345B)
+ └── npm:@denotest/esm-basic@1.0.0 (416B)
diff --git a/cli/tests/testdata/package_json/invalid_value/task.out b/cli/tests/testdata/package_json/invalid_value/task.out
index 914dc27c6b..823c50612f 100644
--- a/cli/tests/testdata/package_json/invalid_value/task.out
+++ b/cli/tests/testdata/package_json/invalid_value/task.out
@@ -1,6 +1,6 @@
Warning Ignoring dependency '@denotest/cjs-default-export' in package.json because its version requirement failed to parse: Invalid npm specifier version requirement. Unexpected character.
invalid stuff that won't parse
~
-Warning Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in the upcoming release.
+Warning Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in an upcoming release.
Task test echo 1
1
diff --git a/cli/tests/testdata/run/042_dyn_import_evalcontext.ts.out b/cli/tests/testdata/run/042_dyn_import_evalcontext.ts.out
index 12a45b8da9..89e16b4781 100644
--- a/cli/tests/testdata/run/042_dyn_import_evalcontext.ts.out
+++ b/cli/tests/testdata/run/042_dyn_import_evalcontext.ts.out
@@ -1 +1 @@
-Module { isMod4: true }
+[Module: null prototype] { isMod4: true }
diff --git a/cli/tests/testdata/run/070_location.ts.out b/cli/tests/testdata/run/070_location.ts.out
index 8b2f9e49df..6827a555d4 100644
--- a/cli/tests/testdata/run/070_location.ts.out
+++ b/cli/tests/testdata/run/070_location.ts.out
@@ -1,5 +1,5 @@
-[WILDCARD][Class: Location]
-Location {}
+[WILDCARD][class Location]
+Object [Location] {}
Location {
hash: "#bat",
host: "foo",
diff --git a/cli/tests/testdata/run/071_location_unset.ts.out b/cli/tests/testdata/run/071_location_unset.ts.out
index dc67c55787..cf4a9d6059 100644
--- a/cli/tests/testdata/run/071_location_unset.ts.out
+++ b/cli/tests/testdata/run/071_location_unset.ts.out
@@ -1,5 +1,5 @@
-[WILDCARD][Class: Location]
-Location {}
+[WILDCARD][class Location]
+Object [Location] {}
undefined
/bar
[WILDCARD]
diff --git a/cli/tests/testdata/run/error_014_catch_dynamic_import_error.js.out b/cli/tests/testdata/run/error_014_catch_dynamic_import_error.js.out
index 701ddc3b57..868c971940 100644
--- a/cli/tests/testdata/run/error_014_catch_dynamic_import_error.js.out
+++ b/cli/tests/testdata/run/error_014_catch_dynamic_import_error.js.out
@@ -2,11 +2,15 @@ Caught direct dynamic import error.
TypeError: Relative import path "does not exist" not prefixed with / or ./ or ../
at [WILDCARD]/error_014_catch_dynamic_import_error.js:3:18
- at async [WILDCARD]/error_014_catch_dynamic_import_error.js:3:5
+ at [WILDCARD]/error_014_catch_dynamic_import_error.js:3:5 {
+ code: "ERR_MODULE_NOT_FOUND"
+}
Caught indirect direct dynamic import error.
TypeError: Relative import path "does not exist either" not prefixed with / or ./ or ../
at [WILDCARD]/subdir/indirect_import_error.js:1:15
- at async [WILDCARD]/error_014_catch_dynamic_import_error.js:10:5
+ at async [WILDCARD]/error_014_catch_dynamic_import_error.js:10:5 {
+ code: "ERR_MODULE_NOT_FOUND"
+}
Caught error thrown by dynamically imported module.
Error: An error
at [WILDCARD]/subdir/throws.js:6:7
diff --git a/cli/tests/testdata/run/error_015_dynamic_import_permissions.js b/cli/tests/testdata/run/error_015_dynamic_import_permissions.js
index 73da56fd89..47961cf63b 100644
--- a/cli/tests/testdata/run/error_015_dynamic_import_permissions.js
+++ b/cli/tests/testdata/run/error_015_dynamic_import_permissions.js
@@ -1,3 +1,3 @@
(async () => {
- await import("http://localhost:4545/subdir/mod4.js");
+ await import("" + "http://localhost:4545/subdir/mod4.js");
})();
diff --git a/cli/tests/testdata/run/error_015_dynamic_import_permissions.out b/cli/tests/testdata/run/error_015_dynamic_import_permissions.out
index ef54f331b0..87ce43e9cd 100644
--- a/cli/tests/testdata/run/error_015_dynamic_import_permissions.out
+++ b/cli/tests/testdata/run/error_015_dynamic_import_permissions.out
@@ -1,4 +1,4 @@
error: Uncaught (in promise) TypeError: Requires net access to "localhost:4545", run again with the --allow-net flag
- await import("http://localhost:4545/subdir/mod4.js");
+ await import("" + "http://localhost:4545/subdir/mod4.js");
^
at async file://[WILDCARD]/error_015_dynamic_import_permissions.js:2:3
diff --git a/cli/tests/testdata/run/error_with_errors_prop.js.out b/cli/tests/testdata/run/error_with_errors_prop.js.out
index 3154e86e65..946b5ad84e 100644
--- a/cli/tests/testdata/run/error_with_errors_prop.js.out
+++ b/cli/tests/testdata/run/error_with_errors_prop.js.out
@@ -2,7 +2,14 @@ Error: Error with errors prop.
at [WILDCARD]/error_with_errors_prop.js:1:15
Error: Error with errors prop.
- at [WILDCARD]/error_with_errors_prop.js:1:15
+ at [WILDCARD]/error_with_errors_prop.js:1:15 {
+ errors: [
+ Error: Error message 1.
+ at [WILDCARD]/error_with_errors_prop.js:3:3,
+ Error: Error message 2.
+ at [WILDCARD]/error_with_errors_prop.js:4:3
+ ]
+}
error: Uncaught Error: Error with errors prop.
const error = new Error("Error with errors prop.");
diff --git a/cli/tests/testdata/run/eval_context_throw_dom_exception.js.out b/cli/tests/testdata/run/eval_context_throw_dom_exception.js.out
index 39e1640832..ac7f7c2305 100644
--- a/cli/tests/testdata/run/eval_context_throw_dom_exception.js.out
+++ b/cli/tests/testdata/run/eval_context_throw_dom_exception.js.out
@@ -1 +1,5 @@
-{ thrown: DOMException: foo, isNativeError: true, isCompileError: false }
+[Object: null prototype] {
+ thrown: DOMException: foo,
+ isNativeError: true,
+ isCompileError: false
+}
diff --git a/cli/tests/testdata/run/event_listener_error_immediate_exit_worker.ts.out b/cli/tests/testdata/run/event_listener_error_immediate_exit_worker.ts.out
index 8bd3122980..85b52190bf 100644
--- a/cli/tests/testdata/run/event_listener_error_immediate_exit_worker.ts.out
+++ b/cli/tests/testdata/run/event_listener_error_immediate_exit_worker.ts.out
@@ -2,7 +2,7 @@
error: Uncaught (in worker "") Error: bar
throw new Error("bar");
^
- at [WILDCARD]/event_listener_error_immediate_exit.ts:4:9
+ at [WILDCARD]/event_listener_error_immediate_exit.ts:4:9[WILDCARD]
at [WILDCARD]/event_listener_error_immediate_exit.ts:11:1
error: Uncaught (in promise) Error: Unhandled error in child worker.
at [WILDCARD]
diff --git a/cli/tests/testdata/run/extension_dynamic_import.ts.out b/cli/tests/testdata/run/extension_dynamic_import.ts.out
index 18b05ea47d..4414ad9235 100644
--- a/cli/tests/testdata/run/extension_dynamic_import.ts.out
+++ b/cli/tests/testdata/run/extension_dynamic_import.ts.out
@@ -1,4 +1,10 @@
-error: Uncaught TypeError: Cannot load extension module from external code
+error: Uncaught (in promise) TypeError: Unsupported scheme "ext" for module "ext:runtime/01_errors.js". Supported schemes: [
+ "data",
+ "blob",
+ "file",
+ "http",
+ "https",
+]
await import("ext:runtime/01_errors.js");
^
- at [WILDCARD]/extension_dynamic_import.ts:1:1
+ at async [WILDCARD]/extension_dynamic_import.ts:1:1
diff --git a/cli/tests/testdata/run/extension_import.ts.out b/cli/tests/testdata/run/extension_import.ts.out
index f1d9d5eb20..88039a9ce8 100644
--- a/cli/tests/testdata/run/extension_import.ts.out
+++ b/cli/tests/testdata/run/extension_import.ts.out
@@ -5,4 +5,4 @@ error: Unsupported scheme "ext" for module "ext:runtime/01_errors.js". Supported
"http",
"https",
]
- at [WILDCARD]
+ at [WILDCARD]/extension_import.ts:1:8
diff --git a/cli/tests/testdata/run/fetch_response_finalization.js.out b/cli/tests/testdata/run/fetch_response_finalization.js.out
index 844a4e4b2d..1a8d7563df 100644
--- a/cli/tests/testdata/run/fetch_response_finalization.js.out
+++ b/cli/tests/testdata/run/fetch_response_finalization.js.out
@@ -1,2 +1,7 @@
-{ "0": "stdin", "1": "stdout", "2": "stderr", "5": "fetchResponseBody" }
+{
+ "0": "stdin",
+ "1": "stdout",
+ "2": "stderr",
+ "5": "fetchResponseBody"
+}
{ "0": "stdin", "1": "stdout", "2": "stderr" }
diff --git a/cli/tests/testdata/run/fix_js_imports.ts.out b/cli/tests/testdata/run/fix_js_imports.ts.out
index 5e45122de8..c427932a42 100644
--- a/cli/tests/testdata/run/fix_js_imports.ts.out
+++ b/cli/tests/testdata/run/fix_js_imports.ts.out
@@ -1 +1 @@
-Module {}
+[Module: null prototype] { }
diff --git a/cli/tests/testdata/run/heapstats.js.out b/cli/tests/testdata/run/heapstats.js.out
index b75a755f8e..9542663331 100644
--- a/cli/tests/testdata/run/heapstats.js.out
+++ b/cli/tests/testdata/run/heapstats.js.out
@@ -1,2 +1,2 @@
-Allocated: 4MB
-Freed: -4MB
+Allocated: 8MB
+Freed: -8MB
diff --git a/cli/tests/testdata/run/node_builtin_modules/mod.js.out b/cli/tests/testdata/run/node_builtin_modules/mod.js.out
index 0d96b31ab6..844e3d9275 100644
--- a/cli/tests/testdata/run/node_builtin_modules/mod.js.out
+++ b/cli/tests/testdata/run/node_builtin_modules/mod.js.out
@@ -1,8 +1,3 @@
[Function: createRequire]
v[WILDCARD].[WILDCARD].[WILDCARD]
-[
- "[WILDCARD]",
- "[WILDCARD]mod.js",
- "hello",
- "there"
-]
+[ [Getter], [Getter], "hello", "there" ]
diff --git a/cli/tests/testdata/run/node_builtin_modules/mod.ts.out b/cli/tests/testdata/run/node_builtin_modules/mod.ts.out
index f19bd81e67..844e3d9275 100644
--- a/cli/tests/testdata/run/node_builtin_modules/mod.ts.out
+++ b/cli/tests/testdata/run/node_builtin_modules/mod.ts.out
@@ -1,8 +1,3 @@
[Function: createRequire]
v[WILDCARD].[WILDCARD].[WILDCARD]
-[
- "[WILDCARD]",
- "[WILDCARD]mod.ts",
- "hello",
- "there"
-]
+[ [Getter], [Getter], "hello", "there" ]
diff --git a/cli/tests/testdata/run/top_level_await/loop.out b/cli/tests/testdata/run/top_level_await/loop.out
index 7f72048c2d..1bdffbf660 100644
--- a/cli/tests/testdata/run/top_level_await/loop.out
+++ b/cli/tests/testdata/run/top_level_await/loop.out
@@ -1,5 +1,5 @@
loading [WILDCARD]a.js
-loaded Module { default: [Class: Foo] }
+loaded [Module: null prototype] { default: [class Foo] }
loading [WILDCARD]b.js
-loaded Module { default: [Class: Bar] }
+loaded [Module: null prototype] { default: [class Bar] }
all loaded
diff --git a/cli/tests/testdata/run/ts_decorators.ts.out b/cli/tests/testdata/run/ts_decorators.ts.out
index 381c7a8091..ee77417cf2 100644
--- a/cli/tests/testdata/run/ts_decorators.ts.out
+++ b/cli/tests/testdata/run/ts_decorators.ts.out
@@ -1,2 +1,2 @@
Check [WILDCARD]
-{ someField: "asdf" }
+SomeClass { someField: "asdf" }
diff --git a/cli/tests/testdata/run/type_directives_js_main.js.out b/cli/tests/testdata/run/type_directives_js_main.js.out
deleted file mode 100644
index 7bca837f02..0000000000
--- a/cli/tests/testdata/run/type_directives_js_main.js.out
+++ /dev/null
@@ -1,3 +0,0 @@
-[WILDCARD]
-DEBUG RS - [WILDCARD] - FileFetcher::fetch() - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts
-[WILDCARD]
diff --git a/cli/tests/testdata/run/websocket_server_idletimeout.ts b/cli/tests/testdata/run/websocket_server_idletimeout.ts
index 9ae6698cbf..211b5f6ea9 100644
--- a/cli/tests/testdata/run/websocket_server_idletimeout.ts
+++ b/cli/tests/testdata/run/websocket_server_idletimeout.ts
@@ -1,5 +1,5 @@
-import { assertEquals } from "../../../test_util/std/testing/asserts.ts";
-import { deferred } from "../../../test_util/std/async/deferred.ts";
+import { assertEquals } from "../../../../test_util/std/testing/asserts.ts";
+import { deferred } from "../../../../test_util/std/async/deferred.ts";
const errorDeferred = deferred();
const closeDeferred = deferred();
diff --git a/cli/tests/testdata/run/websocket_test.ts b/cli/tests/testdata/run/websocket_test.ts
index a9dc34ad1d..d80f03c92a 100644
--- a/cli/tests/testdata/run/websocket_test.ts
+++ b/cli/tests/testdata/run/websocket_test.ts
@@ -161,7 +161,10 @@ Deno.test("websocket error", async () => {
assert(err instanceof ErrorEvent);
// Error message got changed because we don't use warp in test_util
- assertEquals(err.message, "UnexpectedEof: tls handshake eof");
+ assertEquals(
+ err.message,
+ "InvalidData: received corrupt message of type InvalidContentType",
+ );
promise1.resolve();
};
await promise1;
diff --git a/cli/tests/testdata/run/with_package_json/no_deno_json/main.out b/cli/tests/testdata/run/with_package_json/no_deno_json/main.out
index 45bcbb819c..b3af7331d7 100644
--- a/cli/tests/testdata/run/with_package_json/no_deno_json/main.out
+++ b/cli/tests/testdata/run/with_package_json/no_deno_json/main.out
@@ -1,13 +1,13 @@
[WILDCARD]package.json file found at '[WILDCARD]with_package_json[WILDCARD]package.json'
[WILDCARD]
ok
-[Chalk (anonymous)] {
+[Function (anonymous)] Chalk {
constructor: [Function (anonymous)],
- Instance: [Class: ChalkClass],
+ Instance: [class ChalkClass],
supportsColor: false,
- stderr: [Chalk (anonymous)] {
+ stderr: [Function (anonymous)] Chalk {
constructor: [Function (anonymous)],
- Instance: [Class: ChalkClass],
+ Instance: [class ChalkClass],
supportsColor: false
}
}
diff --git a/cli/tests/testdata/run/worker_close_in_wasm_reactions.js.out b/cli/tests/testdata/run/worker_close_in_wasm_reactions.js.out
index 66eb8201cd..325180de4f 100644
--- a/cli/tests/testdata/run/worker_close_in_wasm_reactions.js.out
+++ b/cli/tests/testdata/run/worker_close_in_wasm_reactions.js.out
@@ -1,2 +1,2 @@
-Error: CompileError: WebAssembly.compile(): expected length: @+10
+Error: CompileError: WebAssembly.compile(): reached end while decoding length: @+10
at file:///[WILDCARD]/close_in_wasm_reactions.js:18:13
diff --git a/cli/tests/testdata/run/worker_drop_handle_race.js.out b/cli/tests/testdata/run/worker_drop_handle_race.js.out
index ba66941591..451c3af3d5 100644
--- a/cli/tests/testdata/run/worker_drop_handle_race.js.out
+++ b/cli/tests/testdata/run/worker_drop_handle_race.js.out
@@ -5,5 +5,4 @@ error: Uncaught (in worker "") Error
at Object.action (ext:deno_web/02_timers.js:[WILDCARD])
at handleTimerMacrotask (ext:deno_web/02_timers.js:[WILDCARD])
error: Uncaught (in promise) Error: Unhandled error in child worker.
- at Worker.#pollControl (ext:runtime/11_workers.js:[WILDCARD])
- at eventLoopTick (ext:core/01_core.js:[WILDCARD])
+ at Worker.#pollControl [WILDCARD]
diff --git a/cli/tests/testdata/spawn_kill_permissions.ts b/cli/tests/testdata/spawn_kill_permissions.ts
new file mode 100644
index 0000000000..86626bd5cf
--- /dev/null
+++ b/cli/tests/testdata/spawn_kill_permissions.ts
@@ -0,0 +1,6 @@
+const child = new Deno.Command("cat", {
+ args: ["-"],
+ stdout: "null",
+ stderr: "null",
+}).spawn();
+child.kill("SIGTERM");
diff --git a/cli/tests/testdata/task/both/package_json_selected.out b/cli/tests/testdata/task/both/package_json_selected.out
index 06b735c9da..d317af4ed4 100644
--- a/cli/tests/testdata/task/both/package_json_selected.out
+++ b/cli/tests/testdata/task/both/package_json_selected.out
@@ -1,7 +1,7 @@
Download http://localhost:4545/npm/registry/@denotest/bin
Download http://localhost:4545/npm/registry/@denotest/bin/1.0.0.tgz
Initialize @denotest/bin@1.0.0
-Warning Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in the upcoming release.
+Warning Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in an upcoming release.
Task bin cli-esm testing this out "asdf"
testing
this
diff --git a/cli/tests/testdata/task/deno_json_pre_post/bin.out b/cli/tests/testdata/task/deno_json_pre_post/bin.out
new file mode 100644
index 0000000000..ad66595f1e
--- /dev/null
+++ b/cli/tests/testdata/task/deno_json_pre_post/bin.out
@@ -0,0 +1,2 @@
+Task test echo 'test'
+test
diff --git a/cli/tests/testdata/task/deno_json_pre_post/deno.json b/cli/tests/testdata/task/deno_json_pre_post/deno.json
new file mode 100644
index 0000000000..165b92e3ad
--- /dev/null
+++ b/cli/tests/testdata/task/deno_json_pre_post/deno.json
@@ -0,0 +1,7 @@
+{
+ "tasks": {
+ "pretest": "echo 'pretest'",
+ "posttest": "echo 'posttest'",
+ "test": "echo 'test'"
+ }
+}
diff --git a/cli/tests/testdata/task/deno_json_pre_post/echo.out b/cli/tests/testdata/task/deno_json_pre_post/echo.out
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/cli/tests/testdata/task/deno_json_pre_post/echo.out
@@ -0,0 +1 @@
+0
diff --git a/cli/tests/testdata/task/npx/non_existent.out b/cli/tests/testdata/task/npx/non_existent.out
index b08d29ece6..81065bf743 100644
--- a/cli/tests/testdata/task/npx/non_existent.out
+++ b/cli/tests/testdata/task/npx/non_existent.out
@@ -1,3 +1,3 @@
-Warning Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in the upcoming release.
+Warning Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in an upcoming release.
Task non-existent npx this-command-should-not-exist-for-you
npx: could not resolve command 'this-command-should-not-exist-for-you'
diff --git a/cli/tests/testdata/task/npx/on_own.out b/cli/tests/testdata/task/npx/on_own.out
index 80d8ed9db3..fc9673f7f6 100644
--- a/cli/tests/testdata/task/npx/on_own.out
+++ b/cli/tests/testdata/task/npx/on_own.out
@@ -1,3 +1,3 @@
-Warning Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in the upcoming release.
+Warning Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in an upcoming release.
Task on-own npx
npx: missing command
diff --git a/cli/tests/testdata/task/package_json/bin.out b/cli/tests/testdata/task/package_json/bin.out
index fac6921156..6cfa06d433 100644
--- a/cli/tests/testdata/task/package_json/bin.out
+++ b/cli/tests/testdata/task/package_json/bin.out
@@ -3,7 +3,7 @@ Download http://localhost:4545/npm/registry/@denotest/bin/0.5.0.tgz
Initialize @denotest/bin@0.5.0
Download http://localhost:4545/npm/registry/@denotest/bin/1.0.0.tgz
Initialize @denotest/bin@1.0.0
-Warning Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in the upcoming release.
+Warning Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in an upcoming release.
Task bin @denotest/bin hi && cli-esm testing this out && npx cli-cjs test "extra"
hi
testing
diff --git a/cli/tests/testdata/task/package_json_post/bin.out b/cli/tests/testdata/task/package_json_post/bin.out
new file mode 100644
index 0000000000..9864cc76d0
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_post/bin.out
@@ -0,0 +1,5 @@
+Warning Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in an upcoming release.
+Task test echo 'test'
+test
+Task posttest echo 'posttest'
+posttest
diff --git a/cli/tests/testdata/task/package_json_post/echo.out b/cli/tests/testdata/task/package_json_post/echo.out
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_post/echo.out
@@ -0,0 +1 @@
+0
diff --git a/cli/tests/testdata/task/package_json_post/package.json b/cli/tests/testdata/task/package_json_post/package.json
new file mode 100644
index 0000000000..82689f7d44
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_post/package.json
@@ -0,0 +1,6 @@
+{
+ "scripts": {
+ "posttest": "echo 'posttest'",
+ "test": "echo 'test'"
+ }
+}
diff --git a/cli/tests/testdata/task/package_json_post_only/bin.out b/cli/tests/testdata/task/package_json_post_only/bin.out
new file mode 100644
index 0000000000..9e7cea0916
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_post_only/bin.out
@@ -0,0 +1,4 @@
+Task not found: test
+Available tasks:
+- posttest (package.json)
+ echo 'posttest'
diff --git a/cli/tests/testdata/task/package_json_post_only/echo.out b/cli/tests/testdata/task/package_json_post_only/echo.out
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_post_only/echo.out
@@ -0,0 +1 @@
+0
diff --git a/cli/tests/testdata/task/package_json_post_only/package.json b/cli/tests/testdata/task/package_json_post_only/package.json
new file mode 100644
index 0000000000..ce8a6bbd54
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_post_only/package.json
@@ -0,0 +1,5 @@
+{
+ "scripts": {
+ "posttest": "echo 'posttest'"
+ }
+}
diff --git a/cli/tests/testdata/task/package_json_pre/bin.out b/cli/tests/testdata/task/package_json_pre/bin.out
new file mode 100644
index 0000000000..89c64f2e5a
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_pre/bin.out
@@ -0,0 +1,5 @@
+Warning Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in an upcoming release.
+Task pretest echo 'pretest'
+pretest
+Task test echo 'test'
+test
diff --git a/cli/tests/testdata/task/package_json_pre/echo.out b/cli/tests/testdata/task/package_json_pre/echo.out
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_pre/echo.out
@@ -0,0 +1 @@
+0
diff --git a/cli/tests/testdata/task/package_json_pre/package.json b/cli/tests/testdata/task/package_json_pre/package.json
new file mode 100644
index 0000000000..d3eba02a19
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_pre/package.json
@@ -0,0 +1,6 @@
+{
+ "scripts": {
+ "test": "echo 'test'",
+ "pretest": "echo 'pretest'"
+ }
+}
diff --git a/cli/tests/testdata/task/package_json_pre_only/bin.out b/cli/tests/testdata/task/package_json_pre_only/bin.out
new file mode 100644
index 0000000000..e96e8e3417
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_pre_only/bin.out
@@ -0,0 +1,4 @@
+Task not found: test
+Available tasks:
+- pretest (package.json)
+ echo 'pretest'
diff --git a/cli/tests/testdata/task/package_json_pre_only/echo.out b/cli/tests/testdata/task/package_json_pre_only/echo.out
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_pre_only/echo.out
@@ -0,0 +1 @@
+1
diff --git a/cli/tests/testdata/task/package_json_pre_only/package.json b/cli/tests/testdata/task/package_json_pre_only/package.json
new file mode 100644
index 0000000000..032a5d4eab
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_pre_only/package.json
@@ -0,0 +1,5 @@
+{
+ "scripts": {
+ "pretest": "echo 'pretest'"
+ }
+}
diff --git a/cli/tests/testdata/task/package_json_pre_post/bin.out b/cli/tests/testdata/task/package_json_pre_post/bin.out
new file mode 100644
index 0000000000..0c686b9cdb
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_pre_post/bin.out
@@ -0,0 +1,7 @@
+Warning Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in an upcoming release.
+Task pretest echo 'pretest'
+pretest
+Task test echo 'test'
+test
+Task posttest echo 'posttest'
+posttest
diff --git a/cli/tests/testdata/task/package_json_pre_post/echo.out b/cli/tests/testdata/task/package_json_pre_post/echo.out
new file mode 100644
index 0000000000..573541ac97
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_pre_post/echo.out
@@ -0,0 +1 @@
+0
diff --git a/cli/tests/testdata/task/package_json_pre_post/package.json b/cli/tests/testdata/task/package_json_pre_post/package.json
new file mode 100644
index 0000000000..24a3ff1ed7
--- /dev/null
+++ b/cli/tests/testdata/task/package_json_pre_post/package.json
@@ -0,0 +1,7 @@
+{
+ "scripts": {
+ "pretest": "echo 'pretest'",
+ "posttest": "echo 'posttest'",
+ "test": "echo 'test'"
+ }
+}
diff --git a/cli/tests/testdata/test/before_unload_prevent_default.out b/cli/tests/testdata/test/before_unload_prevent_default.out
new file mode 100644
index 0000000000..09da32ff96
--- /dev/null
+++ b/cli/tests/testdata/test/before_unload_prevent_default.out
@@ -0,0 +1,5 @@
+running 1 test from [WILDCARD]/before_unload_prevent_default.ts
+foo ... ok ([WILDCARD])
+
+ok | 1 passed | 0 failed ([WILDCARD])
+
diff --git a/cli/tests/testdata/test/before_unload_prevent_default.ts b/cli/tests/testdata/test/before_unload_prevent_default.ts
new file mode 100644
index 0000000000..421ded5200
--- /dev/null
+++ b/cli/tests/testdata/test/before_unload_prevent_default.ts
@@ -0,0 +1,6 @@
+addEventListener("beforeunload", (e) => {
+ // The worker should be killed once tests are done regardless of this.
+ e.preventDefault();
+});
+
+Deno.test("foo", () => {});
diff --git a/cli/tests/testdata/test/captured_output.ts b/cli/tests/testdata/test/captured_output.ts
index 43295f027b..905156fd41 100644
--- a/cli/tests/testdata/test/captured_output.ts
+++ b/cli/tests/testdata/test/captured_output.ts
@@ -1,4 +1,5 @@
Deno.test("output", async () => {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [Deno.execPath(), "eval", "console.log(0); console.error(1);"],
});
diff --git a/cli/tests/testdata/test/collect.deprecated.out b/cli/tests/testdata/test/collect.deprecated.out
new file mode 100644
index 0000000000..9bf68807cf
--- /dev/null
+++ b/cli/tests/testdata/test/collect.deprecated.out
@@ -0,0 +1,10 @@
+Warning: "files" configuration is deprecated. Please use "include" and "exclude" instead.
+Check [WILDCARD]/test/collect/include/2_test.ts
+Check [WILDCARD]/test/collect/include/test.ts
+Check [WILDCARD]/test/collect/test.ts
+running 0 tests from ./test/collect/include/2_test.ts
+running 0 tests from ./test/collect/include/test.ts
+running 0 tests from ./test/collect/test.ts
+
+ok | 0 passed | 0 failed ([WILDCARD])
+
diff --git a/cli/tests/testdata/test/collect/deno.deprecated.jsonc b/cli/tests/testdata/test/collect/deno.deprecated.jsonc
new file mode 100644
index 0000000000..b8acda27d0
--- /dev/null
+++ b/cli/tests/testdata/test/collect/deno.deprecated.jsonc
@@ -0,0 +1,7 @@
+{
+ "test": {
+ "files": {
+ "exclude": ["./ignore"]
+ }
+ }
+}
diff --git a/cli/tests/testdata/test/collect/deno.jsonc b/cli/tests/testdata/test/collect/deno.jsonc
index b8acda27d0..e14ce86da0 100644
--- a/cli/tests/testdata/test/collect/deno.jsonc
+++ b/cli/tests/testdata/test/collect/deno.jsonc
@@ -1,7 +1,5 @@
{
"test": {
- "files": {
- "exclude": ["./ignore"]
- }
+ "exclude": ["./ignore"]
}
}
diff --git a/cli/tests/testdata/test/collect/deno2.jsonc b/cli/tests/testdata/test/collect/deno2.jsonc
index a4d244e31e..b7af09d1c0 100644
--- a/cli/tests/testdata/test/collect/deno2.jsonc
+++ b/cli/tests/testdata/test/collect/deno2.jsonc
@@ -1,8 +1,6 @@
{
"test": {
- "files": {
- "include": ["./include/"],
- "exclude": ["./ignore", "./include/2_test.ts"]
- }
+ "include": ["./include/"],
+ "exclude": ["./ignore", "./include/2_test.ts"]
}
}
diff --git a/cli/tests/testdata/test/collect_with_malformed_config.out b/cli/tests/testdata/test/collect_with_malformed_config.out
index 25c34406fd..b31b18e6a2 100644
--- a/cli/tests/testdata/test/collect_with_malformed_config.out
+++ b/cli/tests/testdata/test/collect_with_malformed_config.out
@@ -1,4 +1,4 @@
error: Failed to parse "test" configuration
Caused by:
- unknown field `dont_know_this_field`, expected `files`
+ unknown field `dont_know_this_field`, expected one of `include`, `exclude`, `files`
diff --git a/cli/tests/testdata/test/deno.glob.json b/cli/tests/testdata/test/deno.glob.json
new file mode 100644
index 0000000000..9deb4d2f22
--- /dev/null
+++ b/cli/tests/testdata/test/deno.glob.json
@@ -0,0 +1,11 @@
+{
+ "test": {
+ "include": [
+ "glob/data/test1.?s",
+ "glob/nested/foo/*.ts",
+ "glob/nested/fizz/*.ts",
+ "glob/pages/[id].ts"
+ ],
+ "exclude": ["glob/nested/**/*bazz.ts"]
+ }
+}
diff --git a/cli/tests/testdata/test/glob/data/tes.ts b/cli/tests/testdata/test/glob/data/tes.ts
new file mode 100644
index 0000000000..26f07fba54
--- /dev/null
+++ b/cli/tests/testdata/test/glob/data/tes.ts
@@ -0,0 +1,3 @@
+function foo() {
+
+}
\ No newline at end of file
diff --git a/cli/tests/testdata/test/glob/data/test1.js b/cli/tests/testdata/test/glob/data/test1.js
new file mode 100644
index 0000000000..26f07fba54
--- /dev/null
+++ b/cli/tests/testdata/test/glob/data/test1.js
@@ -0,0 +1,3 @@
+function foo() {
+
+}
\ No newline at end of file
diff --git a/cli/tests/testdata/test/glob/data/test1.ts b/cli/tests/testdata/test/glob/data/test1.ts
new file mode 100644
index 0000000000..26f07fba54
--- /dev/null
+++ b/cli/tests/testdata/test/glob/data/test1.ts
@@ -0,0 +1,3 @@
+function foo() {
+
+}
\ No newline at end of file
diff --git a/cli/tests/testdata/test/glob/data/test12.ts b/cli/tests/testdata/test/glob/data/test12.ts
new file mode 100644
index 0000000000..26f07fba54
--- /dev/null
+++ b/cli/tests/testdata/test/glob/data/test12.ts
@@ -0,0 +1,3 @@
+function foo() {
+
+}
\ No newline at end of file
diff --git a/cli/tests/testdata/test/glob/nested/fizz/bar.ts b/cli/tests/testdata/test/glob/nested/fizz/bar.ts
new file mode 100644
index 0000000000..26f07fba54
--- /dev/null
+++ b/cli/tests/testdata/test/glob/nested/fizz/bar.ts
@@ -0,0 +1,3 @@
+function foo() {
+
+}
\ No newline at end of file
diff --git a/cli/tests/testdata/test/glob/nested/fizz/bazz.ts b/cli/tests/testdata/test/glob/nested/fizz/bazz.ts
new file mode 100644
index 0000000000..26f07fba54
--- /dev/null
+++ b/cli/tests/testdata/test/glob/nested/fizz/bazz.ts
@@ -0,0 +1,3 @@
+function foo() {
+
+}
\ No newline at end of file
diff --git a/cli/tests/testdata/test/glob/nested/fizz/fizz.ts b/cli/tests/testdata/test/glob/nested/fizz/fizz.ts
new file mode 100644
index 0000000000..6940729e9e
--- /dev/null
+++ b/cli/tests/testdata/test/glob/nested/fizz/fizz.ts
@@ -0,0 +1,2 @@
+function foo() {
+}
diff --git a/cli/tests/testdata/test/glob/nested/fizz/foo.ts b/cli/tests/testdata/test/glob/nested/fizz/foo.ts
new file mode 100644
index 0000000000..26f07fba54
--- /dev/null
+++ b/cli/tests/testdata/test/glob/nested/fizz/foo.ts
@@ -0,0 +1,3 @@
+function foo() {
+
+}
\ No newline at end of file
diff --git a/cli/tests/testdata/test/glob/nested/foo/bar.ts b/cli/tests/testdata/test/glob/nested/foo/bar.ts
new file mode 100644
index 0000000000..26f07fba54
--- /dev/null
+++ b/cli/tests/testdata/test/glob/nested/foo/bar.ts
@@ -0,0 +1,3 @@
+function foo() {
+
+}
\ No newline at end of file
diff --git a/cli/tests/testdata/test/glob/nested/foo/bazz.ts b/cli/tests/testdata/test/glob/nested/foo/bazz.ts
new file mode 100644
index 0000000000..26f07fba54
--- /dev/null
+++ b/cli/tests/testdata/test/glob/nested/foo/bazz.ts
@@ -0,0 +1,3 @@
+function foo() {
+
+}
\ No newline at end of file
diff --git a/cli/tests/testdata/test/glob/nested/foo/fizz.ts b/cli/tests/testdata/test/glob/nested/foo/fizz.ts
new file mode 100644
index 0000000000..26f07fba54
--- /dev/null
+++ b/cli/tests/testdata/test/glob/nested/foo/fizz.ts
@@ -0,0 +1,3 @@
+function foo() {
+
+}
\ No newline at end of file
diff --git a/cli/tests/testdata/test/glob/nested/foo/foo.ts b/cli/tests/testdata/test/glob/nested/foo/foo.ts
new file mode 100644
index 0000000000..26f07fba54
--- /dev/null
+++ b/cli/tests/testdata/test/glob/nested/foo/foo.ts
@@ -0,0 +1,3 @@
+function foo() {
+
+}
\ No newline at end of file
diff --git a/cli/tests/testdata/test/glob/pages/[id].ts b/cli/tests/testdata/test/glob/pages/[id].ts
new file mode 100644
index 0000000000..26f07fba54
--- /dev/null
+++ b/cli/tests/testdata/test/glob/pages/[id].ts
@@ -0,0 +1,3 @@
+function foo() {
+
+}
\ No newline at end of file
diff --git a/cli/tests/testdata/test/report_error.out b/cli/tests/testdata/test/report_error.out
new file mode 100644
index 0000000000..698550f97d
--- /dev/null
+++ b/cli/tests/testdata/test/report_error.out
@@ -0,0 +1,23 @@
+running 2 tests from [WILDCARD]/report_error.ts
+foo ...
+Uncaught error from [WILDCARD]/report_error.ts FAILED
+foo ... cancelled (0ms)
+bar ... cancelled (0ms)
+
+ ERRORS
+
+[WILDCARD]/report_error.ts (uncaught error)
+error: Error: foo
+ reportError(new Error("foo"));
+ ^
+ at [WILDCARD]/report_error.ts:2:15
+This error was not caught from a test and caused the test runner to fail on the referenced module.
+It most likely originated from a dangling promise, event/timeout handler or top-level code.
+
+ FAILURES
+
+[WILDCARD]/report_error.ts (uncaught error)
+
+FAILED | 0 passed | 3 failed ([WILDCARD])
+
+error: Test failed
diff --git a/cli/tests/testdata/test/report_error.ts b/cli/tests/testdata/test/report_error.ts
new file mode 100644
index 0000000000..56b6db26c8
--- /dev/null
+++ b/cli/tests/testdata/test/report_error.ts
@@ -0,0 +1,6 @@
+Deno.test("foo", () => {
+ reportError(new Error("foo"));
+ console.log(1);
+});
+
+Deno.test("bar", () => {});
diff --git a/cli/tests/testdata/vendor/npm_and_node_specifier.ts b/cli/tests/testdata/vendor/npm_and_node_specifier.ts
new file mode 100644
index 0000000000..61962e836b
--- /dev/null
+++ b/cli/tests/testdata/vendor/npm_and_node_specifier.ts
@@ -0,0 +1,2 @@
+export { default as path } from "node:path";
+export { getValue, setValue } from "npm:@denotest/esm-basic";
diff --git a/cli/tests/testdata/workers/dynamic_remote.ts b/cli/tests/testdata/workers/dynamic_remote.ts
index 381c7f374c..54e4a4714e 100644
--- a/cli/tests/testdata/workers/dynamic_remote.ts
+++ b/cli/tests/testdata/workers/dynamic_remote.ts
@@ -1,2 +1,2 @@
// This file doesn't really exist, but it doesn't matter, a "PermissionsDenied" error should be thrown.
-await import("https://example.com/some/file.ts");
+await import("" + "https://example.com/some/file.ts");
diff --git a/cli/tests/testdata/workers/permissions_blob_local.ts.out b/cli/tests/testdata/workers/permissions_blob_local.ts.out
index 8cfd41523c..0cd581f7b7 100644
--- a/cli/tests/testdata/workers/permissions_blob_local.ts.out
+++ b/cli/tests/testdata/workers/permissions_blob_local.ts.out
@@ -1,5 +1,4 @@
error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
at blob:null/[WILDCARD]:1:8
error: Uncaught (in promise) Error: Unhandled error in child worker.
- at Worker.#pollControl ([WILDCARD])
- at eventLoopTick (ext:core/01_core.js:[WILDCARD])
+ at Worker.#pollControl [WILDCARD]
diff --git a/cli/tests/testdata/workers/permissions_dynamic_remote.ts.out b/cli/tests/testdata/workers/permissions_dynamic_remote.ts.out
index cbd3f480f7..91f3cc6d5b 100644
--- a/cli/tests/testdata/workers/permissions_dynamic_remote.ts.out
+++ b/cli/tests/testdata/workers/permissions_dynamic_remote.ts.out
@@ -1,7 +1,6 @@
error: Uncaught (in worker "") (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag
-await import("https://example.com/some/file.ts");
+await import("" + "https://example.com/some/file.ts");
^
at async http://localhost:4545/workers/dynamic_remote.ts:2:1
[WILDCARD]error: Uncaught (in promise) Error: Unhandled error in child worker.
- at Worker.#pollControl ([WILDCARD])
- at eventLoopTick (ext:core/01_core.js:[WILDCARD])
+ at Worker.#pollControl [WILDCARD]
diff --git a/cli/tests/testdata/workers/permissions_remote_remote.ts.out b/cli/tests/testdata/workers/permissions_remote_remote.ts.out
index 001370f2fc..bb065740aa 100644
--- a/cli/tests/testdata/workers/permissions_remote_remote.ts.out
+++ b/cli/tests/testdata/workers/permissions_remote_remote.ts.out
@@ -1,5 +1,4 @@
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
at http://localhost:4545/workers/static_remote.ts:2:8
error: Uncaught (in promise) Error: Unhandled error in child worker.
- at Worker.#pollControl ([WILDCARD])
- at eventLoopTick (ext:core/01_core.js:[WILDCARD])
+ at Worker.#pollControl [WILDCARD]
diff --git a/cli/tests/testdata/workers/worker_async_error.ts.out b/cli/tests/testdata/workers/worker_async_error.ts.out
index 84863f0166..8d017859c4 100644
--- a/cli/tests/testdata/workers/worker_async_error.ts.out
+++ b/cli/tests/testdata/workers/worker_async_error.ts.out
@@ -4,5 +4,4 @@ error: Uncaught (in worker "foo") (in promise) Error: bar
at [WILDCARD]/async_error.ts:[WILDCARD]
at [WILDCARD]/async_error.ts:[WILDCARD]
error: Uncaught (in promise) Error: Unhandled error in child worker.
- at Worker.#pollControl ([WILDCARD])
- at eventLoopTick (ext:core/01_core.js:[WILDCARD])
+ at Worker.#pollControl [WILDCARD]
diff --git a/cli/tests/testdata/workers/worker_error.ts.out b/cli/tests/testdata/workers/worker_error.ts.out
index 89f579fb74..78d0c423ed 100644
--- a/cli/tests/testdata/workers/worker_error.ts.out
+++ b/cli/tests/testdata/workers/worker_error.ts.out
@@ -2,5 +2,4 @@
at foo ([WILDCARD])
at [WILDCARD]
error: Uncaught (in promise) Error: Unhandled error in child worker.
- at Worker.#pollControl ([WILDCARD])
- at eventLoopTick (ext:core/01_core.js:[WILDCARD])
+ at Worker.#pollControl [WILDCARD]
diff --git a/cli/tests/testdata/workers/worker_message_handler_error.ts.out b/cli/tests/testdata/workers/worker_message_handler_error.ts.out
index 76449f989e..0f97e97036 100644
--- a/cli/tests/testdata/workers/worker_message_handler_error.ts.out
+++ b/cli/tests/testdata/workers/worker_message_handler_error.ts.out
@@ -4,5 +4,4 @@ error: Uncaught (in worker "foo") Error: bar
at onmessage ([WILDCARD]/message_handler_error.ts:[WILDCARD])
at [WILDCARD]
error: Uncaught (in promise) Error: Unhandled error in child worker.
- at Worker.#pollControl ([WILDCARD])
- at eventLoopTick (ext:core/01_core.js:[WILDCARD])
+ at Worker.#pollControl [WILDCARD]
diff --git a/cli/tests/testdata/workers/worker_nested_error.ts.out b/cli/tests/testdata/workers/worker_nested_error.ts.out
index dd65036b28..15cb85b48c 100644
--- a/cli/tests/testdata/workers/worker_nested_error.ts.out
+++ b/cli/tests/testdata/workers/worker_nested_error.ts.out
@@ -4,8 +4,6 @@
at foo ([WILDCARD]/workers/error.ts:[WILDCARD])
at [WILDCARD]/workers/error.ts:[WILDCARD]
error: Uncaught (in worker "baz") (in promise) Error: Unhandled error in child worker.
- at Worker.#pollControl ([WILDCARD])
- at eventLoopTick (ext:core/01_core.js:[WILDCARD])
+ at Worker.#pollControl [WILDCARD]
error: Uncaught (in promise) Error: Unhandled error in child worker.
- at Worker.#pollControl ([WILDCARD])
- at eventLoopTick (ext:core/01_core.js:[WILDCARD])
+ at Worker.#pollControl [WILDCARD]
diff --git a/cli/tests/unit/body_test.ts b/cli/tests/unit/body_test.ts
index e7a38b7a6b..8aebfadd30 100644
--- a/cli/tests/unit/body_test.ts
+++ b/cli/tests/unit/body_test.ts
@@ -53,6 +53,59 @@ Deno.test(
},
);
+// FormData: non-ASCII names and filenames
+Deno.test(
+ { permissions: { net: true } },
+ async function bodyMultipartFormDataNonAsciiNames() {
+ const boundary = "----01230123";
+ const payload = [
+ `--${boundary}`,
+ `Content-Disposition: form-data; name="文字"`,
+ "",
+ "文字",
+ `--${boundary}`,
+ `Content-Disposition: form-data; name="file"; filename="文字"`,
+ "Content-Type: application/octet-stream",
+ "",
+ "",
+ `--${boundary}--`,
+ ].join("\r\n");
+
+ const body = buildBody(
+ new TextEncoder().encode(payload),
+ new Headers({
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
+ }),
+ );
+
+ const formData = await body.formData();
+ assert(formData.has("文字"));
+ assertEquals(formData.get("文字"), "文字");
+ assert(formData.has("file"));
+ assert(formData.get("file") instanceof File);
+ assertEquals((formData.get("file") as File).name, "文字");
+ },
+);
+
+// FormData: non-ASCII names and filenames roundtrip
+Deno.test(
+ { permissions: { net: true } },
+ async function bodyMultipartFormDataNonAsciiRoundtrip() {
+ const inFormData = new FormData();
+ inFormData.append("文字", "文字");
+ inFormData.append("file", new File([], "文字"));
+
+ const body = buildBody(inFormData);
+
+ const formData = await body.formData();
+ assert(formData.has("文字"));
+ assertEquals(formData.get("文字"), "文字");
+ assert(formData.has("file"));
+ assert(formData.get("file") instanceof File);
+ assertEquals((formData.get("file") as File).name, "文字");
+ },
+);
+
Deno.test(
{ permissions: { net: true } },
async function bodyURLEncodedFormData() {
diff --git a/cli/tests/unit/command_test.ts b/cli/tests/unit/command_test.ts
index 0763a7ac68..198f94aedb 100644
--- a/cli/tests/unit/command_test.ts
+++ b/cli/tests/unit/command_test.ts
@@ -867,3 +867,21 @@ Deno.test(
}
},
);
+
+Deno.test(
+ { permissions: { run: true, read: true } },
+ async function commandKillAfterStatus() {
+ const command = new Deno.Command(Deno.execPath(), {
+ args: ["help"],
+ stdout: "null",
+ stderr: "null",
+ });
+ const child = command.spawn();
+ await child.status;
+ assertThrows(
+ () => child.kill(),
+ TypeError,
+ "Child process has already terminated.",
+ );
+ },
+);
diff --git a/cli/tests/unit/console_test.ts b/cli/tests/unit/console_test.ts
index 3f0f4b7023..4cedf35846 100644
--- a/cli/tests/unit/console_test.ts
+++ b/cli/tests/unit/console_test.ts
@@ -152,16 +152,16 @@ Deno.test(
},
),
`{
- [Symbol("foo\\b")]: 'Symbol("foo\\n\")',
- [Symbol("bar\\n")]: 'Symbol("bar\\n\")',
- [Symbol("bar\\r")]: 'Symbol("bar\\r\")',
- [Symbol("baz\\t")]: 'Symbol("baz\\t\")',
- [Symbol("qux\\x00")]: 'Symbol(\"qux\\x00")'
+ [Symbol("foo\\b")]: 'Symbol("foo\\n")',
+ [Symbol("bar\\n")]: 'Symbol("bar\\n")',
+ [Symbol("bar\\r")]: 'Symbol("bar\\r")',
+ [Symbol("baz\\t")]: 'Symbol("baz\\t")',
+ [Symbol("qux\\x00")]: 'Symbol("qux\\x00")'
}`,
);
assertEquals(
stringify(new Set(["foo\n", "foo\r", "foo\0"])),
- `Set { "foo\\n", "foo\\r", "foo\\x00" }`,
+ `Set(3) { "foo\\n", "foo\\r", "foo\\x00" }`,
);
},
);
@@ -236,8 +236,8 @@ Deno.test(function consoleTestStringifyCircular() {
nu: null,
arrowFunc: [Function: arrowFunc],
extendedClass: Extended { a: 1, b: 2 },
- nFunc: [Function (anonymous)],
- extendedCstr: [Class: Extended],
+ nFunc: [Function: anonymous],
+ extendedCstr: [class Extended extends Base],
o: {
num: 2,
bool: false,
@@ -267,7 +267,7 @@ Deno.test(function consoleTestStringifyCircular() {
stringify(new Date("2018-12-10T02:26:59.002Z")),
"2018-12-10T02:26:59.002Z",
);
- assertEquals(stringify(new Set([1, 2, 3])), "Set { 1, 2, 3 }");
+ assertEquals(stringify(new Set([1, 2, 3])), "Set(3) { 1, 2, 3 }");
assertEquals(
stringify(
new Map([
@@ -275,10 +275,10 @@ Deno.test(function consoleTestStringifyCircular() {
[2, "two"],
]),
),
- `Map { 1 => "one", 2 => "two" }`,
+ `Map(2) { 1 => "one", 2 => "two" }`,
);
- assertEquals(stringify(new WeakSet()), "WeakSet { [items unknown] }");
- assertEquals(stringify(new WeakMap()), "WeakMap { [items unknown] }");
+ assertEquals(stringify(new WeakSet()), "WeakSet { }");
+ assertEquals(stringify(new WeakMap()), "WeakMap { }");
assertEquals(stringify(Symbol(1)), `Symbol("1")`);
assertEquals(stringify(Object(Symbol(1))), `[Symbol: Symbol("1")]`);
assertEquals(stringify(null), "null");
@@ -304,19 +304,23 @@ Deno.test(function consoleTestStringifyCircular() {
stringify(new Uint8Array([1, 2, 3])),
"Uint8Array(3) [ 1, 2, 3 ]",
);
- assertEquals(stringify(Uint8Array.prototype), "Uint8Array {}");
+ assertEquals(stringify(Uint8Array.prototype), "TypedArray {}");
assertEquals(
stringify({ a: { b: { c: { d: new Set([1]) } } } }),
- "{ a: { b: { c: { d: [Set] } } } }",
+ `{
+ a: {
+ b: { c: { d: Set(1) { 1 } } }
+ }
+}`,
);
assertEquals(stringify(nestedObj), nestedObjExpected);
assertEquals(
stringify(JSON),
- "JSON {}",
+ "Object [JSON] {}",
);
assertEquals(
stringify(new Console(() => {})),
- `console {
+ `Object [console] {
log: [Function: log],
debug: [Function: debug],
info: [Function: info],
@@ -345,15 +349,11 @@ Deno.test(function consoleTestStringifyCircular() {
);
assertEquals(
stringify({ str: 1, [Symbol.for("sym")]: 2, [Symbol.toStringTag]: "TAG" }),
- 'TAG { str: 1, [Symbol(sym)]: 2, [Symbol(Symbol.toStringTag)]: "TAG" }',
- );
- assertEquals(
- stringify({
- [Symbol.for("Deno.customInspect")]: function () {
- return Deno.inspect(this);
- },
- }),
- "[Circular *1]",
+ `Object [TAG] {
+ str: 1,
+ [Symbol(sym)]: 2,
+ [Symbol(Symbol.toStringTag)]: "TAG"
+}`,
);
// test inspect is working the same
assertEquals(stripColor(Deno.inspect(nestedObj)), nestedObjExpected);
@@ -363,26 +363,28 @@ Deno.test(function consoleTestStringifyMultipleCircular() {
const y = { a: { b: {} }, foo: { bar: {} } };
y.a.b = y.a;
y.foo.bar = y.foo;
- console.log(y);
assertEquals(
stringify(y),
- "{ a: [ { b: [Circular *1] }, foo: ][ { bar: [Circular *2] } }",
+ "{\n" +
+ " a: ][ { b: [Circular *1] },\n" +
+ " foo: ][ { bar: [Circular *2] }\n" +
+ "}",
);
});
Deno.test(function consoleTestStringifyFunctionWithPrototypeRemoved() {
const f = function f() {};
Reflect.setPrototypeOf(f, null);
- assertEquals(stringify(f), "[Function: f]");
+ assertEquals(stringify(f), "[Function (null prototype): f]");
const af = async function af() {};
Reflect.setPrototypeOf(af, null);
- assertEquals(stringify(af), "[Function: af]");
+ assertEquals(stringify(af), "[Function (null prototype): af]");
const gf = function* gf() {};
Reflect.setPrototypeOf(gf, null);
- assertEquals(stringify(gf), "[Function: gf]");
+ assertEquals(stringify(gf), "[Function (null prototype): gf]");
const agf = async function* agf() {};
Reflect.setPrototypeOf(agf, null);
- assertEquals(stringify(agf), "[Function: agf]");
+ assertEquals(stringify(agf), "[Function (null prototype): agf]");
});
Deno.test(function consoleTestStringifyFunctionWithProperties() {
@@ -400,7 +402,7 @@ Deno.test(function consoleTestStringifyFunctionWithProperties() {
y: 3,
z: [Function (anonymous)],
b: [Function: bar],
- a: Map {}
+ a: Map(0) {}
}
}`,
);
@@ -417,7 +419,7 @@ Deno.test(function consoleTestStringifyFunctionWithProperties() {
y: 3,
z: [Function (anonymous)],
b: [Function: bar],
- a: Map {},
+ a: Map(0) {},
s: [Circular *1],
t: [Function: t] { x: [Circular *1] }
}
@@ -431,7 +433,75 @@ Deno.test(function consoleTestStringifyFunctionWithProperties() {
assertEquals(
stripColor(Deno.inspect(Array, { showHidden: true })),
- `[Function: Array] { [Symbol(Symbol.species)]: [Getter] }`,
+ `][ [Function: Array] {
+ [length]: 1,
+ [name]: "Array",
+ [prototype]: Object(0) [
+ [length]: 0,
+ [constructor]: [Circular *1],
+ [at]: [Function: at] { [length]: 1, [name]: "at" },
+ [concat]: [Function: concat] { [length]: 1, [name]: "concat" },
+ [copyWithin]: [Function: copyWithin] { [length]: 2, [name]: "copyWithin" },
+ [fill]: [Function: fill] { [length]: 1, [name]: "fill" },
+ [find]: [Function: find] { [length]: 1, [name]: "find" },
+ [findIndex]: [Function: findIndex] { [length]: 1, [name]: "findIndex" },
+ [findLast]: [Function: findLast] { [length]: 1, [name]: "findLast" },
+ [findLastIndex]: [Function: findLastIndex] { [length]: 1, [name]: "findLastIndex" },
+ [lastIndexOf]: [Function: lastIndexOf] { [length]: 1, [name]: "lastIndexOf" },
+ [pop]: [Function: pop] { [length]: 0, [name]: "pop" },
+ [push]: [Function: push] { [length]: 1, [name]: "push" },
+ [reverse]: [Function: reverse] { [length]: 0, [name]: "reverse" },
+ [shift]: [Function: shift] { [length]: 0, [name]: "shift" },
+ [unshift]: [Function: unshift] { [length]: 1, [name]: "unshift" },
+ [slice]: [Function: slice] { [length]: 2, [name]: "slice" },
+ [sort]: [Function: sort] { [length]: 1, [name]: "sort" },
+ [splice]: [Function: splice] { [length]: 2, [name]: "splice" },
+ [includes]: [Function: includes] { [length]: 1, [name]: "includes" },
+ [indexOf]: [Function: indexOf] { [length]: 1, [name]: "indexOf" },
+ [join]: [Function: join] { [length]: 1, [name]: "join" },
+ [keys]: [Function: keys] { [length]: 0, [name]: "keys" },
+ [entries]: [Function: entries] { [length]: 0, [name]: "entries" },
+ [values]: [Function: values] { [length]: 0, [name]: "values" },
+ [forEach]: [Function: forEach] { [length]: 1, [name]: "forEach" },
+ [filter]: [Function: filter] { [length]: 1, [name]: "filter" },
+ [flat]: [Function: flat] { [length]: 0, [name]: "flat" },
+ [flatMap]: [Function: flatMap] { [length]: 1, [name]: "flatMap" },
+ [map]: [Function: map] { [length]: 1, [name]: "map" },
+ [every]: [Function: every] { [length]: 1, [name]: "every" },
+ [some]: [Function: some] { [length]: 1, [name]: "some" },
+ [reduce]: [Function: reduce] { [length]: 1, [name]: "reduce" },
+ [reduceRight]: [Function: reduceRight] { [length]: 1, [name]: "reduceRight" },
+ [toLocaleString]: [Function: toLocaleString] { [length]: 0, [name]: "toLocaleString" },
+ [toString]: [Function: toString] { [length]: 0, [name]: "toString" },
+ [toReversed]: [Function: toReversed] { [length]: 0, [name]: "toReversed" },
+ [toSorted]: [Function: toSorted] { [length]: 1, [name]: "toSorted" },
+ [toSpliced]: [Function: toSpliced] { [length]: 2, [name]: "toSpliced" },
+ [with]: [Function: with] { [length]: 2, [name]: "with" },
+ [Symbol(Symbol.iterator)]: [Function: values] { [length]: 0, [name]: "values" },
+ [Symbol(Symbol.unscopables)]: [Object: null prototype] {
+ at: true,
+ copyWithin: true,
+ entries: true,
+ fill: true,
+ find: true,
+ findIndex: true,
+ findLast: true,
+ findLastIndex: true,
+ flat: true,
+ flatMap: true,
+ includes: true,
+ keys: true,
+ values: true,
+ toReversed: true,
+ toSorted: true,
+ toSpliced: true
+ }
+ ],
+ [isArray]: [Function: isArray] { [length]: 1, [name]: "isArray" },
+ [from]: [Function: from] { [length]: 1, [name]: "from" },
+ [of]: [Function: of] { [length]: 0, [name]: "of" },
+ [Symbol(Symbol.species)]: [Getter]
+}`,
);
});
@@ -440,21 +510,24 @@ Deno.test(function consoleTestStringifyWithDepth() {
const nestedObj: any = { a: { b: { c: { d: { e: { f: 42 } } } } } };
assertEquals(
stripColor(inspectArgs([nestedObj], { depth: 3 })),
- "{ a: { b: { c: [Object] } } }",
+ "{\n a: { b: { c: { d: [Object] } } }\n}",
);
assertEquals(
stripColor(inspectArgs([nestedObj], { depth: 4 })),
- "{ a: { b: { c: { d: [Object] } } } }",
+ "{\n a: {\n b: { c: { d: { e: [Object] } } }\n }\n}",
+ );
+ assertEquals(
+ stripColor(inspectArgs([nestedObj], { depth: 0 })),
+ "{ a: [Object] }",
);
- assertEquals(stripColor(inspectArgs([nestedObj], { depth: 0 })), "[Object]");
assertEquals(
stripColor(inspectArgs([nestedObj])),
- "{ a: { b: { c: { d: [Object] } } } }",
+ "{\n a: {\n b: { c: { d: { e: [Object] } } }\n }\n}",
);
// test inspect is working the same way
assertEquals(
stripColor(Deno.inspect(nestedObj, { depth: 4 })),
- "{ a: { b: { c: { d: [Object] } } } }",
+ "{\n a: {\n b: { c: { d: { e: [Object] } } }\n }\n}",
);
});
@@ -502,13 +575,15 @@ Deno.test(function consoleTestStringifyIterable() {
assertEquals(
stringify(longArray),
`[
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,
... 100 more items
]`,
);
@@ -519,13 +594,15 @@ Deno.test(function consoleTestStringifyIterable() {
`{
a: "a",
longArray: [
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,
... 100 more items
]
}`,
@@ -535,7 +612,7 @@ Deno.test(function consoleTestStringifyIterable() {
["a", 0],
["b", 1],
]);
- assertEquals(stringify(shortMap), `Map { "a" => 0, "b" => 1 }`);
+ assertEquals(stringify(shortMap), `Map(2) { "a" => 0, "b" => 1 }`);
const longMap = new Map();
for (const key of Array(200).keys()) {
@@ -543,7 +620,7 @@ Deno.test(function consoleTestStringifyIterable() {
}
assertEquals(
stringify(longMap),
- `Map {
+ `Map(200) {
"0" => 0,
"1" => 1,
"2" => 2,
@@ -649,14 +726,14 @@ Deno.test(function consoleTestStringifyIterable() {
);
const shortSet = new Set([1, 2, 3]);
- assertEquals(stringify(shortSet), `Set { 1, 2, 3 }`);
+ assertEquals(stringify(shortSet), `Set(3) { 1, 2, 3 }`);
const longSet = new Set();
for (const key of Array(200).keys()) {
longSet.add(key);
}
assertEquals(
stringify(longSet),
- `Set {
+ `Set(200) {
0,
1,
2,
@@ -1059,7 +1136,7 @@ Deno.test(function consoleTestWithObjectFormatSpecifier() {
assertEquals(stringify("%o", { a: 42 }), "{ a: 42 }");
assertEquals(
stringify("%o", { a: { b: { c: { d: new Set([1]) } } } }),
- "{ a: { b: { c: { d: [Set] } } } }",
+ "{\n a: {\n b: { c: { d: Set(1) { 1 } } }\n }\n}",
);
});
@@ -1503,15 +1580,15 @@ Deno.test(function consoleTable() {
assertEquals(
stripColor(out.toString()),
`\
-┌───────┬───────────┬───────────────────┬────────┐
-│ (idx) │ c │ e │ Values │
-├───────┼───────────┼───────────────────┼────────┤
-│ a │ │ │ true │
-│ b │ { d: 10 } │ [ 1, 2, [Array] ] │ │
-│ f │ │ │ "test" │
-│ g │ │ │ │
-│ h │ │ │ │
-└───────┴───────────┴───────────────────┴────────┘
+┌───────┬───────────┬────────────────────┬────────┐
+│ (idx) │ c │ e │ Values │
+├───────┼───────────┼────────────────────┼────────┤
+│ a │ │ │ true │
+│ b │ { d: 10 } │ [ 1, 2, [ 5, 6 ] ] │ │
+│ f │ │ │ "test" │
+│ g │ │ │ │
+│ h │ │ │ │
+└───────┴───────────┴────────────────────┴────────┘
`,
);
});
@@ -1797,7 +1874,7 @@ Deno.test(function inspectGetters() {
return 0;
},
}, { getters: true })),
- "{ foo: 0 }",
+ "{ foo: [Getter: 0] }",
);
assertEquals(
@@ -1806,13 +1883,13 @@ Deno.test(function inspectGetters() {
throw new Error("bar");
},
}, { getters: true }),
- "{ foo: [Thrown Error: bar] }",
+ "{ foo: [Getter: ] }",
);
});
Deno.test(function inspectPrototype() {
class A {}
- assertEquals(Deno.inspect(A.prototype), "A {}");
+ assertEquals(Deno.inspect(A.prototype), "{}");
});
Deno.test(function inspectSorted() {
@@ -1822,7 +1899,7 @@ Deno.test(function inspectSorted() {
);
assertEquals(
stripColor(Deno.inspect(new Set(["b", "a"]), { sorted: true })),
- `Set { "a", "b" }`,
+ `Set(2) { "a", "b" }`,
);
assertEquals(
stripColor(Deno.inspect(
@@ -1832,7 +1909,7 @@ Deno.test(function inspectSorted() {
]),
{ sorted: true },
)),
- `Map { "a" => 1, "b" => 2 }`,
+ `Map(2) { "a" => 1, "b" => 2 }`,
);
});
@@ -1871,7 +1948,7 @@ Deno.test(function inspectTrailingComma() {
]),
{ trailingComma: true },
)),
- `Set {
+ `Set(2) {
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
}`,
@@ -1884,7 +1961,7 @@ Deno.test(function inspectTrailingComma() {
]),
{ trailingComma: true },
)),
- `Map {
+ `Map(2) {
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => 1,
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" => 2,
}`,
@@ -1904,11 +1981,11 @@ Deno.test(function inspectCompact() {
Deno.test(function inspectIterableLimit() {
assertEquals(
stripColor(Deno.inspect(["a", "b", "c"], { iterableLimit: 2 })),
- `[ "a", "b", ... 1 more items ]`,
+ `[ "a", "b", ... 1 more item ]`,
);
assertEquals(
stripColor(Deno.inspect(new Set(["a", "b", "c"]), { iterableLimit: 2 })),
- `Set { "a", "b", ... 1 more items }`,
+ `Set(3) { "a", "b", ... 1 more item }`,
);
assertEquals(
stripColor(Deno.inspect(
@@ -1919,7 +1996,7 @@ Deno.test(function inspectIterableLimit() {
]),
{ iterableLimit: 2 },
)),
- `Map { "a" => 1, "b" => 2, ... 1 more items }`,
+ `Map(3) { "a" => 1, "b" => 2, ... 1 more item }`,
);
});
@@ -1958,7 +2035,7 @@ Deno.test(function inspectProxy() {
},
}),
)),
- `MyProxy { prop1: 5, prop2: 5 }`,
+ `Object [MyProxy] { prop1: 5, prop2: 5 }`,
);
assertEquals(
stripColor(Deno.inspect(
@@ -1983,10 +2060,13 @@ Deno.test(function inspectProxy() {
new Proxy([1, 2, 3, 4, 5, 6, 7], { get() {} }),
{ showProxy: true },
)),
- `Proxy [ [
+ `Proxy [
+ [
1, 2, 3, 4,
5, 6, 7
- ], { get: [Function: get] } ]`,
+ ],
+ { get: [Function: get] }
+]`,
);
assertEquals(
stripColor(Deno.inspect(
@@ -2057,7 +2137,7 @@ Deno.test(function inspectEmptyArray() {
compact: false,
trailingComma: true,
}),
- "[\n]",
+ "[]",
);
});
@@ -2072,8 +2152,7 @@ Deno.test(function inspectDeepEmptyArray() {
trailingComma: true,
}),
`{
- arr: [
- ],
+ arr: [],
}`,
);
});
@@ -2086,11 +2165,11 @@ Deno.test(function inspectEmptyMap() {
compact: false,
trailingComma: true,
}),
- "Map {\n}",
+ "Map(0) {}",
);
});
-Deno.test(function inspectEmptyMap() {
+Deno.test(function inspectEmptySet() {
const set = new Set();
assertEquals(
@@ -2098,11 +2177,11 @@ Deno.test(function inspectEmptyMap() {
compact: false,
trailingComma: true,
}),
- "Set {\n}",
+ "Set(0) {}",
);
});
-Deno.test(function inspectEmptyMap() {
+Deno.test(function inspectEmptyUint8Array() {
const typedArray = new Uint8Array(0);
assertEquals(
@@ -2110,7 +2189,32 @@ Deno.test(function inspectEmptyMap() {
compact: false,
trailingComma: true,
}),
- "Uint8Array(0) [\n]",
+ "Uint8Array(0) []",
+ );
+});
+
+Deno.test(function inspectLargeArrayBuffer() {
+ const arrayBuffer = new ArrayBuffer(2 ** 32 + 1);
+ assertEquals(
+ Deno.inspect(arrayBuffer),
+ `ArrayBuffer {
+ [Uint8Contents]: <00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... 4294967197 more bytes>,
+ byteLength: 4294967297
+}`,
+ );
+ structuredClone(arrayBuffer, { transfer: [arrayBuffer] });
+ assertEquals(
+ Deno.inspect(arrayBuffer),
+ "ArrayBuffer { (detached), byteLength: 0 }",
+ );
+
+ const sharedArrayBuffer = new SharedArrayBuffer(2 ** 32 + 1);
+ assertEquals(
+ Deno.inspect(sharedArrayBuffer),
+ `SharedArrayBuffer {
+ [Uint8Contents]: <00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... 4294967197 more bytes>,
+ byteLength: 4294967297
+}`,
);
});
@@ -2124,12 +2228,12 @@ Deno.test(function inspectStringAbbreviation() {
assertEquals(
Deno.inspect(obj, { strAbbreviateSize: 10 }),
- '{ str: "This is a ..." }',
+ '{ str: "This is a "... 59 more characters }',
);
assertEquals(
Deno.inspect(arr, { strAbbreviateSize: 10 }),
- '[ "This is a ..." ]',
+ '[ "This is a "... 59 more characters ]',
);
});
@@ -2156,6 +2260,13 @@ Deno.test(function inspectWithPrototypePollution() {
}
});
+Deno.test(function inspectPromiseLike() {
+ assertEquals(
+ Deno.inspect(Object.create(Promise.prototype)),
+ "Promise { }",
+ );
+});
+
Deno.test(function inspectorMethods() {
console.timeStamp("test");
console.profile("test");
@@ -2192,3 +2303,27 @@ Deno.test(function inspectAnonymousFunctions() {
"[AsyncGeneratorFunction (anonymous)]",
);
});
+
+Deno.test(function inspectBreakLengthOption() {
+ assertEquals(
+ Deno.inspect("123456789\n".repeat(3), { breakLength: 34 }),
+ `"123456789\\n123456789\\n123456789\\n"`,
+ );
+ assertEquals(
+ Deno.inspect("123456789\n".repeat(3), { breakLength: 33 }),
+ `"123456789\\n" +
+ "123456789\\n" +
+ "123456789\\n"`,
+ );
+});
+
+Deno.test(function inspectEscapeSequencesFalse() {
+ assertEquals(
+ Deno.inspect("foo\nbar", { escapeSequences: true }),
+ '"foo\\nbar"',
+ ); // default behavior
+ assertEquals(
+ Deno.inspect("foo\nbar", { escapeSequences: false }),
+ '"foo\nbar"',
+ );
+});
diff --git a/cli/tests/unit/event_target_test.ts b/cli/tests/unit/event_target_test.ts
index 49bd354aa2..c7acab364c 100644
--- a/cli/tests/unit/event_target_test.ts
+++ b/cli/tests/unit/event_target_test.ts
@@ -245,6 +245,20 @@ Deno.test(function eventTargetDispatchShouldSetTargetInListener() {
assertEquals(called, true);
});
+Deno.test(function eventTargetDispatchShouldFireCurrentListenersOnly() {
+ const target = new EventTarget();
+ const event = new Event("foo");
+ let callCount = 0;
+ target.addEventListener("foo", () => {
+ ++callCount;
+ target.addEventListener("foo", () => {
+ ++callCount;
+ });
+ });
+ target.dispatchEvent(event);
+ assertEquals(callCount, 1);
+});
+
Deno.test(function eventTargetAddEventListenerGlobalAbort() {
return new Promise((resolve) => {
const c = new AbortController();
diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts
index bafb23c2a9..db553f14d4 100644
--- a/cli/tests/unit/fetch_test.ts
+++ b/cli/tests/unit/fetch_test.ts
@@ -10,6 +10,8 @@ import {
} from "./test_util.ts";
import { Buffer } from "../../../test_util/std/io/buffer.ts";
+const listenPort = 4504;
+
Deno.test(
{ permissions: { net: true } },
async function fetchRequiresOneArgument() {
@@ -639,7 +641,7 @@ Deno.test(
permissions: { net: true },
},
async function fetchRequest() {
- const addr = "127.0.0.1:4501";
+ const addr = `127.0.0.1:${listenPort}`;
const bufPromise = bufferServer(addr);
const response = await fetch(`http://${addr}/blah`, {
method: "POST",
@@ -673,7 +675,7 @@ Deno.test(
permissions: { net: true },
},
async function fetchRequestAcceptHeaders() {
- const addr = "127.0.0.1:4501";
+ const addr = `127.0.0.1:${listenPort}`;
const bufPromise = bufferServer(addr);
const response = await fetch(`http://${addr}/blah`, {
method: "POST",
@@ -705,7 +707,7 @@ Deno.test(
permissions: { net: true },
},
async function fetchPostBodyString() {
- const addr = "127.0.0.1:4511";
+ const addr = `127.0.0.1:${listenPort}`;
const bufPromise = bufferServer(addr);
const body = "hello world";
const response = await fetch(`http://${addr}/blah`, {
@@ -743,7 +745,7 @@ Deno.test(
permissions: { net: true },
},
async function fetchPostBodyTypedArray() {
- const addr = "127.0.0.1:4503";
+ const addr = `127.0.0.1:${listenPort}`;
const bufPromise = bufferServer(addr);
const bodyStr = "hello world";
const body = new TextEncoder().encode(bodyStr);
@@ -781,7 +783,7 @@ Deno.test(
permissions: { net: true },
},
async function fetchUserSetContentLength() {
- const addr = "127.0.0.1:4501";
+ const addr = `127.0.0.1:${listenPort}`;
const bufPromise = bufferServer(addr);
const response = await fetch(`http://${addr}/blah`, {
method: "POST",
@@ -812,7 +814,7 @@ Deno.test(
permissions: { net: true },
},
async function fetchUserSetTransferEncoding() {
- const addr = "127.0.0.1:4501";
+ const addr = `127.0.0.1:${listenPort}`;
const bufPromise = bufferServer(addr);
const response = await fetch(`http://${addr}/blah`, {
method: "POST",
@@ -1158,7 +1160,7 @@ Deno.test(
permissions: { net: true },
},
async function fetchPostBodyReadableStream() {
- const addr = "127.0.0.1:4511";
+ const addr = `127.0.0.1:${listenPort}`;
const bufPromise = bufferServer(addr);
const stream = new TransformStream();
const writer = stream.writable.getWriter();
@@ -1217,7 +1219,7 @@ Deno.test(
async function fetchFilterOutCustomHostHeader(): Promise<
void
> {
- const addr = "127.0.0.1:4511";
+ const addr = `127.0.0.1:${listenPort}`;
const [hostname, port] = addr.split(":");
const listener = Deno.listen({
hostname,
@@ -1495,6 +1497,18 @@ Deno.test(
},
);
+Deno.test(
+ { permissions: { net: true, read: true } },
+ async function fetchSupportsHttpsOverIpAddress() {
+ const caCert = await Deno.readTextFile("cli/tests/testdata/tls/RootCA.pem");
+ const client = Deno.createHttpClient({ caCerts: [caCert] });
+ const res = await fetch("https://localhost:5546/http_version", { client });
+ assert(res.ok);
+ assertEquals(await res.text(), "HTTP/1.1");
+ client.close();
+ },
+);
+
Deno.test(
{ permissions: { net: true, read: true } },
async function fetchSupportsHttp1Only() {
@@ -1519,6 +1533,30 @@ Deno.test(
},
);
+Deno.test(
+ { permissions: { net: true, read: true } },
+ async function fetchForceHttp1OnHttp2Server() {
+ const client = Deno.createHttpClient({ http2: false, http1: true });
+ await assertRejects(
+ () => fetch("http://localhost:5549/http_version", { client }),
+ TypeError,
+ );
+ client.close();
+ },
+);
+
+Deno.test(
+ { permissions: { net: true, read: true } },
+ async function fetchForceHttp2OnHttp1Server() {
+ const client = Deno.createHttpClient({ http2: true, http1: false });
+ await assertRejects(
+ () => fetch("http://localhost:5548/http_version", { client }),
+ TypeError,
+ );
+ client.close();
+ },
+);
+
Deno.test(
{ permissions: { net: true, read: true } },
async function fetchPrefersHttp2() {
@@ -1681,7 +1719,7 @@ Deno.test(
async function fetchWithInvalidContentLengthAndTransferEncoding(): Promise<
void
> {
- const addr = "127.0.0.1:4516";
+ const addr = `127.0.0.1:${listenPort}`;
const data = "a".repeat(10 << 10);
const body = new TextEncoder().encode(
@@ -1713,7 +1751,7 @@ Deno.test(
async function fetchWithInvalidContentLength(): Promise<
void
> {
- const addr = "127.0.0.1:4517";
+ const addr = `127.0.0.1:${listenPort}`;
const data = "a".repeat(10 << 10);
const body = new TextEncoder().encode(
@@ -1741,7 +1779,7 @@ Deno.test(
async function fetchWithInvalidContentLength(): Promise<
void
> {
- const addr = "127.0.0.1:4518";
+ const addr = `127.0.0.1:${listenPort}`;
const data = "a".repeat(10 << 10);
const contentLength = data.length / 2;
@@ -1768,7 +1806,7 @@ Deno.test(
async function fetchWithInvalidContentLength(): Promise<
void
> {
- const addr = "127.0.0.1:4519";
+ const addr = `127.0.0.1:${listenPort}`;
const data = "a".repeat(10 << 10);
const contentLength = data.length * 2;
@@ -1893,3 +1931,19 @@ Deno.test(
await server;
},
);
+
+Deno.test("Request with subarray TypedArray body", async () => {
+ const body = new Uint8Array([1, 2, 3, 4, 5]).subarray(1);
+ const req = new Request("https://example.com", { method: "POST", body });
+ const actual = new Uint8Array(await req.arrayBuffer());
+ const expected = new Uint8Array([2, 3, 4, 5]);
+ assertEquals(actual, expected);
+});
+
+Deno.test("Response with subarray TypedArray body", async () => {
+ const body = new Uint8Array([1, 2, 3, 4, 5]).subarray(1);
+ const req = new Response(body);
+ const actual = new Uint8Array(await req.arrayBuffer());
+ const expected = new Uint8Array([2, 3, 4, 5]);
+ assertEquals(actual, expected);
+});
diff --git a/cli/tests/unit/http_test.ts b/cli/tests/unit/http_test.ts
index f407c9186e..549234986b 100644
--- a/cli/tests/unit/http_test.ts
+++ b/cli/tests/unit/http_test.ts
@@ -943,7 +943,7 @@ Deno.test(
file.close();
let httpConn: Deno.HttpConn;
- const listener = Deno.listen({ port: 4503 });
+ const listener = Deno.listen({ port: 4501 });
const promise = (async () => {
const conn = await listener.accept();
httpConn = Deno.serveHttp(conn);
@@ -952,7 +952,7 @@ Deno.test(
const f = await Deno.open(tmpFile, { read: true });
await respondWith(new Response(f.readable, { status: 200 }));
})();
- const resp = await fetch("http://127.0.0.1:4503/");
+ const resp = await fetch("http://127.0.0.1:4501/");
const body = await resp.arrayBuffer();
assertEquals(body.byteLength, 70 * 1024);
await promise;
@@ -2085,6 +2085,7 @@ Deno.test({
"--header",
"Accept-Encoding: deflate, gzip",
];
+ // deno-lint-ignore no-deprecated-deno-api
const proc = Deno.run({ cmd, stdout: "piped", stderr: "null" });
const status = await proc.status();
assert(status.success);
@@ -2147,6 +2148,7 @@ Deno.test({
"--header",
"Accept-Encoding: deflate, gzip",
];
+ // deno-lint-ignore no-deprecated-deno-api
const proc = Deno.run({ cmd, stdout: "piped", stderr: "null" });
const status = await proc.status();
assert(status.success);
diff --git a/cli/tests/unit/kv_test.ts b/cli/tests/unit/kv_test.ts
index 60cf11b8ef..3c5efa5887 100644
--- a/cli/tests/unit/kv_test.ts
+++ b/cli/tests/unit/kv_test.ts
@@ -66,6 +66,7 @@ dbTest("basic read-write-delete and versionstamps", async (db) => {
assertEquals(result1.versionstamp, null);
const setRes = await db.set(["a"], "b");
+ assert(setRes.ok);
assertEquals(setRes.versionstamp, "00000000000000010000");
const result2 = await db.get(["a"]);
assertEquals(result2.key, ["a"]);
@@ -122,6 +123,36 @@ dbTest("set and get recursive object", async (db) => {
assert(resultValue.a === resultValue);
});
+// invalid values (as per structured clone algorithm with _for storage_, NOT JSON)
+const INVALID_VALUE_CASES = [
+ { name: "function", value: () => {} },
+ { name: "symbol", value: Symbol() },
+ { name: "WeakMap", value: new WeakMap() },
+ { name: "WeakSet", value: new WeakSet() },
+ {
+ name: "WebAssembly.Module",
+ value: new WebAssembly.Module(
+ new Uint8Array([0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00]),
+ ),
+ },
+ {
+ name: "SharedArrayBuffer",
+ value: new SharedArrayBuffer(3),
+ },
+];
+
+for (const { name, value } of INVALID_VALUE_CASES) {
+ dbTest(`set and get ${name} value (invalid)`, async (db) => {
+ await assertRejects(
+ async () => await db.set(["a"], value),
+ Error,
+ );
+ const res = await db.get(["a"]);
+ assertEquals(res.key, ["a"]);
+ assertEquals(res.value, null);
+ });
+}
+
const keys = [
["a"],
["a", "b"],
@@ -183,7 +214,7 @@ dbTest("compare and mutate", async (db) => {
.check({ key: ["t"], versionstamp: currentValue.versionstamp })
.set(currentValue.key, "2")
.commit();
- assert(res);
+ assert(res.ok);
assertEquals(res.versionstamp, "00000000000000020000");
const newValue = await db.get(["t"]);
@@ -194,7 +225,7 @@ dbTest("compare and mutate", async (db) => {
.check({ key: ["t"], versionstamp: currentValue.versionstamp })
.set(currentValue.key, "3")
.commit();
- assertEquals(res, null);
+ assert(!res.ok);
const newValue2 = await db.get(["t"]);
assertEquals(newValue2.versionstamp, "00000000000000020000");
@@ -206,7 +237,7 @@ dbTest("compare and mutate not exists", async (db) => {
.check({ key: ["t"], versionstamp: null })
.set(["t"], "1")
.commit();
- assert(res);
+ assert(res.ok);
const newValue = await db.get(["t"]);
assertEquals(newValue.versionstamp, "00000000000000010000");
@@ -216,7 +247,37 @@ dbTest("compare and mutate not exists", async (db) => {
.check({ key: ["t"], versionstamp: null })
.set(["t"], "2")
.commit();
- assertEquals(res, null);
+ assert(!res.ok);
+});
+
+dbTest("atomic mutation helper (sum)", async (db) => {
+ await db.set(["t"], new Deno.KvU64(42n));
+ assertEquals((await db.get(["t"])).value, new Deno.KvU64(42n));
+
+ await db.atomic().sum(["t"], 1n).commit();
+ assertEquals((await db.get(["t"])).value, new Deno.KvU64(43n));
+});
+
+dbTest("atomic mutation helper (min)", async (db) => {
+ await db.set(["t"], new Deno.KvU64(42n));
+ assertEquals((await db.get(["t"])).value, new Deno.KvU64(42n));
+
+ await db.atomic().min(["t"], 1n).commit();
+ assertEquals((await db.get(["t"])).value, new Deno.KvU64(1n));
+
+ await db.atomic().min(["t"], 2n).commit();
+ assertEquals((await db.get(["t"])).value, new Deno.KvU64(1n));
+});
+
+dbTest("atomic mutation helper (max)", async (db) => {
+ await db.set(["t"], new Deno.KvU64(42n));
+ assertEquals((await db.get(["t"])).value, new Deno.KvU64(42n));
+
+ await db.atomic().max(["t"], 41n).commit();
+ assertEquals((await db.get(["t"])).value, new Deno.KvU64(42n));
+
+ await db.atomic().max(["t"], 43n).commit();
+ assertEquals((await db.get(["t"])).value, new Deno.KvU64(43n));
});
dbTest("compare multiple and mutate", async (db) => {
@@ -234,7 +295,7 @@ dbTest("compare multiple and mutate", async (db) => {
.set(currentValue1.key, "3")
.set(currentValue2.key, "4")
.commit();
- assert(res);
+ assert(res.ok);
const newValue1 = await db.get(["t1"]);
assertEquals(newValue1.versionstamp, "00000000000000030000");
@@ -250,7 +311,7 @@ dbTest("compare multiple and mutate", async (db) => {
.set(newValue1.key, "5")
.set(newValue2.key, "6")
.commit();
- assertEquals(res2, null);
+ assert(!res2.ok);
const newValue3 = await db.get(["t1"]);
assertEquals(newValue3.versionstamp, "00000000000000030000");
@@ -266,7 +327,7 @@ dbTest("atomic mutation ordering (set before delete)", async (db) => {
.set(["a"], "2")
.delete(["a"])
.commit();
- assert(res);
+ assert(res.ok);
const result = await db.get(["a"]);
assertEquals(result.value, null);
});
@@ -277,7 +338,7 @@ dbTest("atomic mutation ordering (delete before set)", async (db) => {
.delete(["a"])
.set(["a"], "2")
.commit();
- assert(res);
+ assert(res.ok);
const result = await db.get(["a"]);
assertEquals(result.value, "2");
});
@@ -286,7 +347,7 @@ dbTest("atomic mutation type=set", async (db) => {
const res = await db.atomic()
.mutate({ key: ["a"], value: "1", type: "set" })
.commit();
- assert(res);
+ assert(res.ok);
const result = await db.get(["a"]);
assertEquals(result.value, "1");
});
@@ -296,7 +357,7 @@ dbTest("atomic mutation type=set overwrite", async (db) => {
const res = await db.atomic()
.mutate({ key: ["a"], value: "2", type: "set" })
.commit();
- assert(res);
+ assert(res.ok);
const result = await db.get(["a"]);
assertEquals(result.value, "2");
});
@@ -306,7 +367,7 @@ dbTest("atomic mutation type=delete", async (db) => {
const res = await db.atomic()
.mutate({ key: ["a"], type: "delete" })
.commit();
- assert(res);
+ assert(res.ok);
const result = await db.get(["a"]);
assertEquals(result.value, null);
});
@@ -315,7 +376,7 @@ dbTest("atomic mutation type=delete no exists", async (db) => {
const res = await db.atomic()
.mutate({ key: ["a"], type: "delete" })
.commit();
- assert(res);
+ assert(res.ok);
const result = await db.get(["a"]);
assertEquals(result.value, null);
});
@@ -325,7 +386,7 @@ dbTest("atomic mutation type=sum", async (db) => {
const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(1n), type: "sum" })
.commit();
- assert(res);
+ assert(res.ok);
const result = await db.get(["a"]);
assertEquals(result.value, new Deno.KvU64(11n));
});
@@ -334,7 +395,7 @@ dbTest("atomic mutation type=sum no exists", async (db) => {
const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(1n), type: "sum" })
.commit();
- assert(res);
+ assert(res.ok);
const result = await db.get(["a"]);
assert(result.value);
assertEquals(result.value, new Deno.KvU64(1n));
@@ -345,7 +406,7 @@ dbTest("atomic mutation type=sum wrap around", async (db) => {
const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(10n), type: "sum" })
.commit();
- assert(res);
+ assert(res.ok);
const result = await db.get(["a"]);
assertEquals(result.value, new Deno.KvU64(9n));
@@ -393,7 +454,7 @@ dbTest("atomic mutation type=min", async (db) => {
const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(5n), type: "min" })
.commit();
- assert(res);
+ assert(res.ok);
const result = await db.get(["a"]);
assertEquals(result.value, new Deno.KvU64(5n));
@@ -409,7 +470,7 @@ dbTest("atomic mutation type=min no exists", async (db) => {
const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(1n), type: "min" })
.commit();
- assert(res);
+ assert(res.ok);
const result = await db.get(["a"]);
assert(result.value);
assertEquals(result.value, new Deno.KvU64(1n));
@@ -447,7 +508,7 @@ dbTest("atomic mutation type=max", async (db) => {
const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(5n), type: "max" })
.commit();
- assert(res);
+ assert(res.ok);
const result = await db.get(["a"]);
assertEquals(result.value, new Deno.KvU64(10n));
@@ -463,7 +524,7 @@ dbTest("atomic mutation type=max no exists", async (db) => {
const res = await db.atomic()
.mutate({ key: ["a"], value: new Deno.KvU64(1n), type: "max" })
.commit();
- assert(res);
+ assert(res.ok);
const result = await db.get(["a"]);
assert(result.value);
assertEquals(result.value, new Deno.KvU64(1n));
@@ -517,19 +578,31 @@ Deno.test("KvU64 underflow", () => {
}, RangeError);
});
-Deno.test("KvU64 frozen", () => {
- const a = new Deno.KvU64(1n);
- assertThrows(() => {
- // @ts-expect-error value is readonly
- a.value = 2n;
- }, TypeError);
-});
-
Deno.test("KvU64 unbox", () => {
const a = new Deno.KvU64(1n);
assertEquals(a.value, 1n);
});
+Deno.test("KvU64 unbox with valueOf", () => {
+ const a = new Deno.KvU64(1n);
+ assertEquals(a.valueOf(), 1n);
+});
+
+Deno.test("KvU64 auto-unbox", () => {
+ const a = new Deno.KvU64(1n);
+ assertEquals(a as unknown as bigint + 1n, 2n);
+});
+
+Deno.test("KvU64 toString", () => {
+ const a = new Deno.KvU64(1n);
+ assertEquals(a.toString(), "1");
+});
+
+Deno.test("KvU64 inspect", () => {
+ const a = new Deno.KvU64(1n);
+ assertEquals(Deno.inspect(a), "[Deno.KvU64: 1n]");
+});
+
async function collect(
iter: Deno.KvListIterator,
): Promise[]> {
@@ -1183,6 +1256,12 @@ dbTest("keys must be arrays", async (db) => {
);
});
+Deno.test("Deno.Kv constructor throws", () => {
+ assertThrows(() => {
+ new Deno.Kv();
+ });
+});
+
// This function is never called, it is just used to check that all the types
// are behaving as expected.
async function _typeCheckingTests() {
diff --git a/cli/tests/unit/metrics_test.ts b/cli/tests/unit/metrics_test.ts
index df2f1b2be5..5fdfebc85b 100644
--- a/cli/tests/unit/metrics_test.ts
+++ b/cli/tests/unit/metrics_test.ts
@@ -80,12 +80,14 @@ Deno.test(function metricsForOpCrates() {
// Test that op_names == Objects.keys(Deno[Deno.internal].core.ops)
// since building the per-op metrics depends on op_names being complete
Deno.test(function opNamesMatch() {
+ // @ts-ignore: Deno[Deno.internal].core allowed
+ const ops = Object.keys(Deno[Deno.internal].core.ops);
+ // @ts-ignore: Deno[Deno.internal].core allowed
+ ops.concat(Object.keys(Deno[Deno.internal].core.asyncOps));
+
assertEquals(
// @ts-ignore: Deno[Deno.internal].core allowed
Deno[Deno.internal].core.opNames().sort(),
- // @ts-ignore: Deno[Deno.internal].core allowed
- Object.keys(Deno[Deno.internal].core.ops).sort().filter((name) =>
- name !== "asyncOpsInfo"
- ),
+ ops.sort().filter((name) => name !== "asyncOpsInfo"),
);
});
diff --git a/cli/tests/unit/net_test.ts b/cli/tests/unit/net_test.ts
index 935a6f846b..32250bbd07 100644
--- a/cli/tests/unit/net_test.ts
+++ b/cli/tests/unit/net_test.ts
@@ -12,6 +12,10 @@ import {
} from "./test_util.ts";
import { join } from "../../../test_util/std/path/mod.ts";
+// Since these tests may run in parallel, ensure this port is unique to this file
+const listenPort = 4503;
+const listenPort2 = 4504;
+
let isCI: boolean;
try {
isCI = Deno.env.get("CI") !== undefined;
@@ -20,10 +24,10 @@ try {
}
Deno.test({ permissions: { net: true } }, function netTcpListenClose() {
- const listener = Deno.listen({ hostname: "127.0.0.1", port: 3500 });
+ const listener = Deno.listen({ hostname: "127.0.0.1", port: listenPort });
assert(listener.addr.transport === "tcp");
assertEquals(listener.addr.hostname, "127.0.0.1");
- assertEquals(listener.addr.port, 3500);
+ assertEquals(listener.addr.port, listenPort);
assertNotEquals(listener.rid, 0);
listener.close();
});
@@ -35,12 +39,12 @@ Deno.test(
function netUdpListenClose() {
const socket = Deno.listenDatagram({
hostname: "127.0.0.1",
- port: 3500,
+ port: listenPort,
transport: "udp",
});
assert(socket.addr.transport === "udp");
assertEquals(socket.addr.hostname, "127.0.0.1");
- assertEquals(socket.addr.port, 3500);
+ assertEquals(socket.addr.port, listenPort);
socket.close();
},
);
@@ -127,7 +131,7 @@ Deno.test(
permissions: { net: true },
},
async function netTcpCloseWhileAccept() {
- const listener = Deno.listen({ port: 4501 });
+ const listener = Deno.listen({ port: listenPort });
const p = listener.accept();
listener.close();
// TODO(piscisaureus): the error type should be `Interrupted` here, which
@@ -212,22 +216,22 @@ Deno.test(
);
Deno.test({ permissions: { net: true } }, async function netTcpDialListen() {
- const listener = Deno.listen({ port: 3500 });
+ const listener = Deno.listen({ port: listenPort });
listener.accept().then(
async (conn) => {
assert(conn.remoteAddr != null);
assert(conn.localAddr.transport === "tcp");
assertEquals(conn.localAddr.hostname, "127.0.0.1");
- assertEquals(conn.localAddr.port, 3500);
+ assertEquals(conn.localAddr.port, listenPort);
await conn.write(new Uint8Array([1, 2, 3]));
conn.close();
},
);
- const conn = await Deno.connect({ hostname: "127.0.0.1", port: 3500 });
+ const conn = await Deno.connect({ hostname: "127.0.0.1", port: listenPort });
assert(conn.remoteAddr.transport === "tcp");
assertEquals(conn.remoteAddr.hostname, "127.0.0.1");
- assertEquals(conn.remoteAddr.port, 3500);
+ assertEquals(conn.remoteAddr.port, listenPort);
assert(conn.localAddr != null);
const buf = new Uint8Array(1024);
const readResult = await conn.read(buf);
@@ -247,23 +251,23 @@ Deno.test({ permissions: { net: true } }, async function netTcpDialListen() {
});
Deno.test({ permissions: { net: true } }, async function netTcpSetNoDelay() {
- const listener = Deno.listen({ port: 3500 });
+ const listener = Deno.listen({ port: listenPort });
listener.accept().then(
async (conn) => {
assert(conn.remoteAddr != null);
assert(conn.localAddr.transport === "tcp");
assertEquals(conn.localAddr.hostname, "127.0.0.1");
- assertEquals(conn.localAddr.port, 3500);
+ assertEquals(conn.localAddr.port, listenPort);
await conn.write(new Uint8Array([1, 2, 3]));
conn.close();
},
);
- const conn = await Deno.connect({ hostname: "127.0.0.1", port: 3500 });
+ const conn = await Deno.connect({ hostname: "127.0.0.1", port: listenPort });
conn.setNoDelay(true);
assert(conn.remoteAddr.transport === "tcp");
assertEquals(conn.remoteAddr.hostname, "127.0.0.1");
- assertEquals(conn.remoteAddr.port, 3500);
+ assertEquals(conn.remoteAddr.port, listenPort);
assert(conn.localAddr != null);
const buf = new Uint8Array(1024);
const readResult = await conn.read(buf);
@@ -283,23 +287,23 @@ Deno.test({ permissions: { net: true } }, async function netTcpSetNoDelay() {
});
Deno.test({ permissions: { net: true } }, async function netTcpSetKeepAlive() {
- const listener = Deno.listen({ port: 3500 });
+ const listener = Deno.listen({ port: listenPort });
listener.accept().then(
async (conn) => {
assert(conn.remoteAddr != null);
assert(conn.localAddr.transport === "tcp");
assertEquals(conn.localAddr.hostname, "127.0.0.1");
- assertEquals(conn.localAddr.port, 3500);
+ assertEquals(conn.localAddr.port, listenPort);
await conn.write(new Uint8Array([1, 2, 3]));
conn.close();
},
);
- const conn = await Deno.connect({ hostname: "127.0.0.1", port: 3500 });
+ const conn = await Deno.connect({ hostname: "127.0.0.1", port: listenPort });
conn.setKeepAlive(true);
assert(conn.remoteAddr.transport === "tcp");
assertEquals(conn.remoteAddr.hostname, "127.0.0.1");
- assertEquals(conn.remoteAddr.port, 3500);
+ assertEquals(conn.remoteAddr.port, listenPort);
assert(conn.localAddr != null);
const buf = new Uint8Array(1024);
const readResult = await conn.read(buf);
@@ -360,14 +364,14 @@ Deno.test(
Deno.test(
{ permissions: { net: true } },
async function netUdpSendReceive() {
- const alice = Deno.listenDatagram({ port: 3500, transport: "udp" });
+ const alice = Deno.listenDatagram({ port: listenPort, transport: "udp" });
assert(alice.addr.transport === "udp");
- assertEquals(alice.addr.port, 3500);
+ assertEquals(alice.addr.port, listenPort);
assertEquals(alice.addr.hostname, "127.0.0.1");
- const bob = Deno.listenDatagram({ port: 4501, transport: "udp" });
+ const bob = Deno.listenDatagram({ port: listenPort2, transport: "udp" });
assert(bob.addr.transport === "udp");
- assertEquals(bob.addr.port, 4501);
+ assertEquals(bob.addr.port, listenPort2);
assertEquals(bob.addr.hostname, "127.0.0.1");
const sent = new Uint8Array([1, 2, 3]);
@@ -377,7 +381,7 @@ Deno.test(
const [recvd, remote] = await bob.receive();
assert(remote.transport === "udp");
- assertEquals(remote.port, 3500);
+ assertEquals(remote.port, listenPort);
assertEquals(recvd.length, 3);
assertEquals(1, recvd[0]);
assertEquals(2, recvd[1]);
@@ -393,18 +397,18 @@ Deno.test(
// Must bind sender to an address that can send to the broadcast address on MacOS.
// Macos will give us error 49 when sending the broadcast packet if we omit hostname here.
const alice = Deno.listenDatagram({
- port: 3500,
+ port: listenPort,
transport: "udp",
hostname: "0.0.0.0",
});
const bob = Deno.listenDatagram({
- port: 4501,
+ port: listenPort,
transport: "udp",
hostname: "0.0.0.0",
});
assert(bob.addr.transport === "udp");
- assertEquals(bob.addr.port, 4501);
+ assertEquals(bob.addr.port, listenPort);
assertEquals(bob.addr.hostname, "0.0.0.0");
const broadcastAddr = { ...bob.addr, hostname: "255.255.255.255" };
@@ -415,7 +419,7 @@ Deno.test(
assertEquals(byteLength, 3);
const [recvd, remote] = await bob.receive();
assert(remote.transport === "udp");
- assertEquals(remote.port, 3500);
+ assertEquals(remote.port, listenPort);
assertEquals(recvd.length, 3);
assertEquals(1, recvd[0]);
assertEquals(2, recvd[1]);
@@ -563,9 +567,9 @@ Deno.test(
Deno.test(
{ permissions: { net: true } },
async function netUdpConcurrentSendReceive() {
- const socket = Deno.listenDatagram({ port: 3500, transport: "udp" });
+ const socket = Deno.listenDatagram({ port: listenPort, transport: "udp" });
assert(socket.addr.transport === "udp");
- assertEquals(socket.addr.port, 3500);
+ assertEquals(socket.addr.port, listenPort);
assertEquals(socket.addr.hostname, "127.0.0.1");
const recvPromise = socket.receive();
@@ -588,7 +592,7 @@ Deno.test(
{ permissions: { net: true } },
async function netUdpBorrowMutError() {
const socket = Deno.listenDatagram({
- port: 4501,
+ port: listenPort,
transport: "udp",
});
// Panic happened on second send: BorrowMutError
@@ -761,7 +765,7 @@ Deno.test(
Deno.test(
{ permissions: { net: true } },
async function netListenAsyncIterator() {
- const addr = { hostname: "127.0.0.1", port: 3500 };
+ const addr = { hostname: "127.0.0.1", port: listenPort };
const listener = Deno.listen(addr);
const runAsyncIterator = async () => {
for await (const conn of listener) {
@@ -794,7 +798,7 @@ Deno.test(
permissions: { net: true },
},
async function netCloseWriteSuccess() {
- const addr = { hostname: "127.0.0.1", port: 3500 };
+ const addr = { hostname: "127.0.0.1", port: listenPort };
const listener = Deno.listen(addr);
const closeDeferred = deferred();
listener.accept().then(async (conn) => {
@@ -850,7 +854,7 @@ Deno.test(
}
}
- const addr = { hostname: "127.0.0.1", port: 3500 };
+ const addr = { hostname: "127.0.0.1", port: listenPort };
const listener = Deno.listen(addr);
const listenerPromise = iteratorReq(listener);
const connectionPromise = (async () => {
@@ -898,13 +902,13 @@ Deno.test(
Deno.test({ permissions: { net: true } }, async function whatwgStreams() {
(async () => {
- const listener = Deno.listen({ hostname: "127.0.0.1", port: 3500 });
+ const listener = Deno.listen({ hostname: "127.0.0.1", port: listenPort });
const conn = await listener.accept();
await conn.readable.pipeTo(conn.writable);
listener.close();
})();
- const conn = await Deno.connect({ hostname: "127.0.0.1", port: 3500 });
+ const conn = await Deno.connect({ hostname: "127.0.0.1", port: listenPort });
const reader = conn.readable.getReader();
const writer = conn.writable.getWriter();
const encoder = new TextEncoder();
@@ -957,7 +961,7 @@ Deno.test(
async function netListenUnref() {
const [statusCode, _output] = await execCode(`
async function main() {
- const listener = Deno.listen({ port: 3500 });
+ const listener = Deno.listen({ port: ${listenPort} });
listener.unref();
await listener.accept(); // This doesn't block the program from exiting
}
@@ -972,14 +976,14 @@ Deno.test(
async function netListenUnref() {
const [statusCode, _output] = await execCode(`
async function main() {
- const listener = Deno.listen({ port: 3500 });
+ const listener = Deno.listen({ port: ${listenPort} });
await listener.accept();
listener.unref();
await listener.accept(); // The program exits here
throw new Error(); // The program doesn't reach here
}
main();
- const conn = await Deno.connect({ port: 3500 });
+ const conn = await Deno.connect({ port: ${listenPort} });
conn.close();
`);
assertEquals(statusCode, 0);
@@ -991,7 +995,7 @@ Deno.test(
async function netListenUnrefAndRef() {
const p = execCode2(`
async function main() {
- const listener = Deno.listen({ port: 3500 });
+ const listener = Deno.listen({ port: ${listenPort} });
listener.unref();
listener.ref(); // This restores 'ref' state of listener
console.log("started");
@@ -1001,7 +1005,7 @@ Deno.test(
main();
`);
await p.waitStdoutText("started");
- const conn = await Deno.connect({ port: 3500 });
+ const conn = await Deno.connect({ port: listenPort });
conn.close();
const [statusCode, output] = await p.finished();
assertEquals(statusCode, 0);
@@ -1013,7 +1017,7 @@ Deno.test(
{ permissions: { net: true } },
async function netListenUnrefConcurrentAccept() {
const timer = setTimeout(() => {}, 1000);
- const listener = Deno.listen({ port: 3500 });
+ const listener = Deno.listen({ port: listenPort });
listener.accept().catch(() => {});
listener.unref();
// Unref'd listener still causes Busy error
@@ -1044,12 +1048,12 @@ Deno.test({
Deno.test(
{ permissions: { net: true, read: true, run: true } },
async function netConnUnref() {
- const listener = Deno.listen({ port: 3500 });
+ const listener = Deno.listen({ port: listenPort });
const intervalId = setInterval(() => {}); // This keeps event loop alive.
const program = execCode(`
async function main() {
- const conn = await Deno.connect({ port: 3500 });
+ const conn = await Deno.connect({ port: ${listenPort} });
conn.unref();
await conn.read(new Uint8Array(10)); // The program exits here
throw new Error(); // The program doesn't reach here
@@ -1068,12 +1072,12 @@ Deno.test(
Deno.test(
{ permissions: { net: true, read: true, run: true } },
async function netConnUnrefReadable() {
- const listener = Deno.listen({ port: 3500 });
+ const listener = Deno.listen({ port: listenPort });
const intervalId = setInterval(() => {}); // This keeps event loop alive.
const program = execCode(`
async function main() {
- const conn = await Deno.connect({ port: 3500 });
+ const conn = await Deno.connect({ port: ${listenPort} });
conn.unref();
const reader = conn.readable.getReader();
await reader.read(); // The program exits here
@@ -1093,7 +1097,7 @@ Deno.test(
Deno.test({ permissions: { net: true } }, async function netTcpReuseAddr() {
const listener1 = Deno.listen({
hostname: "127.0.0.1",
- port: 3500,
+ port: listenPort,
});
listener1.accept().then(
(conn) => {
@@ -1101,7 +1105,7 @@ Deno.test({ permissions: { net: true } }, async function netTcpReuseAddr() {
},
);
- const conn1 = await Deno.connect({ hostname: "127.0.0.1", port: 3500 });
+ const conn1 = await Deno.connect({ hostname: "127.0.0.1", port: listenPort });
const buf1 = new Uint8Array(1024);
await conn1.read(buf1);
listener1.close();
@@ -1109,7 +1113,7 @@ Deno.test({ permissions: { net: true } }, async function netTcpReuseAddr() {
const listener2 = Deno.listen({
hostname: "127.0.0.1",
- port: 3500,
+ port: listenPort,
});
listener2.accept().then(
@@ -1118,7 +1122,7 @@ Deno.test({ permissions: { net: true } }, async function netTcpReuseAddr() {
},
);
- const conn2 = await Deno.connect({ hostname: "127.0.0.1", port: 3500 });
+ const conn2 = await Deno.connect({ hostname: "127.0.0.1", port: listenPort });
const buf2 = new Uint8Array(1024);
await conn2.read(buf2);
diff --git a/cli/tests/unit/opcall_test.ts b/cli/tests/unit/opcall_test.ts
index 8985c97801..3b37f8c097 100644
--- a/cli/tests/unit/opcall_test.ts
+++ b/cli/tests/unit/opcall_test.ts
@@ -1,20 +1,18 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+import { assertEquals } from "https://deno.land/std@v0.42.0/testing/asserts.ts";
import { assert, assertStringIncludes, unreachable } from "./test_util.ts";
Deno.test(async function sendAsyncStackTrace() {
- const buf = new Uint8Array(10);
- const rid = 10;
try {
- await Deno.read(rid, buf);
+ await core.ops.op_error_async();
unreachable();
} catch (error) {
assert(error instanceof Error);
const s = error.stack?.toString();
assert(s);
- console.log(s);
assertStringIncludes(s, "opcall_test.ts");
- assertStringIncludes(s, "read");
+ assertStringIncludes(s, "sendAsyncStackTrace");
assert(
!s.includes("ext:core"),
"opcall stack traces should NOT include ext:core internals such as unwrapOpResult",
@@ -22,6 +20,31 @@ Deno.test(async function sendAsyncStackTrace() {
}
});
+Deno.test(async function sendAsyncStackTraceDeferred() {
+ try {
+ await core.ops.op_error_async_deferred();
+ unreachable();
+ } catch (error) {
+ assert(error instanceof Error);
+ const s = error.stack?.toString();
+ assert(s);
+ assertStringIncludes(s, "opcall_test.ts");
+ assertStringIncludes(s, "sendAsyncStackTraceDeferred");
+ assert(
+ !s.includes("ext:core"),
+ "opcall stack traces should NOT include ext:core internals such as unwrapOpResult",
+ );
+ }
+});
+
+Deno.test(function syncAdd() {
+ assertEquals(30, core.ops.op_add(10, 20));
+});
+
+Deno.test(async function asyncAdd() {
+ assertEquals(30, await core.ops.op_add_async(10, 20));
+});
+
// @ts-ignore This is not publicly typed namespace, but it's there for sure.
const core = Deno[Deno.internal].core;
diff --git a/cli/tests/unit/process_test.ts b/cli/tests/unit/process_test.ts
index e6c4bfe595..54ebb07b22 100644
--- a/cli/tests/unit/process_test.ts
+++ b/cli/tests/unit/process_test.ts
@@ -11,6 +11,7 @@ Deno.test(
{ permissions: { read: true, run: false } },
function runPermissions() {
assertThrows(() => {
+ // deno-lint-ignore no-deprecated-deno-api
Deno.run({
cmd: [Deno.execPath(), "eval", "console.log('hello world')"],
});
@@ -21,6 +22,7 @@ Deno.test(
Deno.test(
{ permissions: { run: true, read: true } },
async function runSuccess() {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
// freeze the array to ensure it's not modified
cmd: Object.freeze([
@@ -43,6 +45,7 @@ Deno.test(
Deno.test(
{ permissions: { run: true, read: true } },
async function runUrl() {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
new URL(`file:///${Deno.execPath()}`),
@@ -66,6 +69,7 @@ Deno.test(
async function runStdinRid0(): Promise<
void
> {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [Deno.execPath(), "eval", "console.log('hello world')"],
stdin: 0,
@@ -85,6 +89,7 @@ Deno.test(
{ permissions: { run: true, read: true } },
function runInvalidStdio() {
assertThrows(() =>
+ // deno-lint-ignore no-deprecated-deno-api
Deno.run({
cmd: [Deno.execPath(), "eval", "console.log('hello world')"],
// @ts-expect-error because Deno.run should throw on invalid stdin.
@@ -92,6 +97,7 @@ Deno.test(
})
);
assertThrows(() =>
+ // deno-lint-ignore no-deprecated-deno-api
Deno.run({
cmd: [Deno.execPath(), "eval", "console.log('hello world')"],
// @ts-expect-error because Deno.run should throw on invalid stdout.
@@ -99,6 +105,7 @@ Deno.test(
})
);
assertThrows(() =>
+ // deno-lint-ignore no-deprecated-deno-api
Deno.run({
cmd: [Deno.execPath(), "eval", "console.log('hello world')"],
// @ts-expect-error because Deno.run should throw on invalid stderr.
@@ -111,6 +118,7 @@ Deno.test(
Deno.test(
{ permissions: { run: true, read: true } },
async function runCommandFailedWithCode() {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [Deno.execPath(), "eval", "Deno.exit(41 + 1)"],
});
@@ -127,6 +135,7 @@ Deno.test(
permissions: { run: true, read: true },
},
async function runCommandFailedWithSignal() {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
Deno.execPath(),
@@ -150,6 +159,7 @@ Deno.test(
Deno.test({ permissions: { run: true } }, function runNotFound() {
let error;
try {
+ // deno-lint-ignore no-deprecated-deno-api
Deno.run({ cmd: ["this file hopefully doesn't exist"] });
} catch (e) {
error = e;
@@ -181,6 +191,7 @@ tryExit();
`;
Deno.writeFileSync(`${cwd}/${programFile}`, enc.encode(program));
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cwd,
cmd: [Deno.execPath(), "run", "--allow-read", programFile],
@@ -204,6 +215,7 @@ Deno.test(
async function runStdinPiped(): Promise<
void
> {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
Deno.execPath(),
@@ -235,6 +247,7 @@ Deno.test(
async function runStdoutPiped(): Promise<
void
> {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
Deno.execPath(),
@@ -271,6 +284,7 @@ Deno.test(
async function runStderrPiped(): Promise<
void
> {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
Deno.execPath(),
@@ -305,6 +319,7 @@ Deno.test(
Deno.test(
{ permissions: { run: true, read: true } },
async function runOutput() {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
Deno.execPath(),
@@ -325,6 +340,7 @@ Deno.test(
async function runStderrOutput(): Promise<
void
> {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
Deno.execPath(),
@@ -350,6 +366,7 @@ Deno.test(
write: true,
});
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
Deno.execPath(),
@@ -382,6 +399,7 @@ Deno.test(
await Deno.writeFile(fileName, encoder.encode("hello"));
const file = await Deno.open(fileName);
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
Deno.execPath(),
@@ -401,6 +419,7 @@ Deno.test(
Deno.test(
{ permissions: { run: true, read: true } },
async function runEnv() {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
Deno.execPath(),
@@ -423,6 +442,7 @@ Deno.test(
Deno.test(
{ permissions: { run: true, read: true } },
async function runClose() {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
Deno.execPath(),
@@ -446,6 +466,7 @@ Deno.test(
Deno.test(
{ permissions: { run: true, read: true } },
async function runKillAfterStatus() {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [Deno.execPath(), "eval", 'console.log("hello")'],
});
@@ -502,6 +523,7 @@ Deno.test(
Deno.test(
{ permissions: { run: true, read: true } },
async function killSuccess() {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [Deno.execPath(), "eval", "setTimeout(() => {}, 10000)"],
});
@@ -525,6 +547,7 @@ Deno.test(
);
Deno.test({ permissions: { run: true, read: true } }, function killFailed() {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [Deno.execPath(), "eval", "setTimeout(() => {}, 10000)"],
});
@@ -542,6 +565,7 @@ Deno.test({ permissions: { run: true, read: true } }, function killFailed() {
Deno.test(
{ permissions: { run: true, read: true, env: true } },
async function clearEnv(): Promise {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
Deno.execPath(),
@@ -574,6 +598,7 @@ Deno.test(
ignore: Deno.build.os === "windows",
},
async function uid(): Promise {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
"id",
@@ -587,6 +612,7 @@ Deno.test(
if (currentUid !== "0") {
assertThrows(() => {
+ // deno-lint-ignore no-deprecated-deno-api
Deno.run({
cmd: [
"echo",
@@ -605,6 +631,7 @@ Deno.test(
ignore: Deno.build.os === "windows",
},
async function gid(): Promise {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
"id",
@@ -618,6 +645,7 @@ Deno.test(
if (currentGid !== "0") {
assertThrows(() => {
+ // deno-lint-ignore no-deprecated-deno-api
Deno.run({
cmd: [
"echo",
@@ -636,6 +664,7 @@ Deno.test(
ignore: Deno.build.os === "windows",
},
async function non_existent_cwd(): Promise {
+ // deno-lint-ignore no-deprecated-deno-api
const p = Deno.run({
cmd: [
Deno.execPath(),
diff --git a/cli/tests/unit/read_text_file_test.ts b/cli/tests/unit/read_text_file_test.ts
index c40cb83e39..21b13c9281 100644
--- a/cli/tests/unit/read_text_file_test.ts
+++ b/cli/tests/unit/read_text_file_test.ts
@@ -164,7 +164,13 @@ Deno.test(
const bytes = new Uint8Array(kStringMaxLengthPlusOne);
const filePath = "cli/tests/testdata/too_big_a_file.txt";
- Deno.writeFileSync(filePath, bytes);
+ try {
+ Deno.writeFileSync(filePath, bytes);
+ } catch {
+ // NOTE(bartlomieju): writing a 0.5Gb file might be too much for CI,
+ // so skip running if writing fails.
+ return;
+ }
assertThrows(
() => {
@@ -185,7 +191,13 @@ Deno.test(
const bytes = new Uint8Array(kStringMaxLengthPlusOne);
const filePath = "cli/tests/testdata/too_big_a_file_2.txt";
- await Deno.writeFile(filePath, bytes);
+ try {
+ await Deno.writeFile(filePath, bytes);
+ } catch {
+ // NOTE(bartlomieju): writing a 0.5Gb file might be too much for CI,
+ // so skip running if writing fails.
+ return;
+ }
await assertRejects(
async () => {
diff --git a/cli/tests/unit/resources_test.ts b/cli/tests/unit/resources_test.ts
index 2d1f2fd75b..4a55f05a70 100644
--- a/cli/tests/unit/resources_test.ts
+++ b/cli/tests/unit/resources_test.ts
@@ -1,6 +1,8 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals, assertThrows } from "./test_util.ts";
+const listenPort = 4505;
+
Deno.test(function resourcesCloseBadArgs() {
assertThrows(() => {
Deno.close((null as unknown) as number);
@@ -16,8 +18,8 @@ Deno.test(function resourcesStdio() {
});
Deno.test({ permissions: { net: true } }, async function resourcesNet() {
- const listener = Deno.listen({ port: 4501 });
- const dialerConn = await Deno.connect({ port: 4501 });
+ const listener = Deno.listen({ port: listenPort });
+ const dialerConn = await Deno.connect({ port: listenPort });
const listenerConn = await listener.accept();
const res = Deno.resources();
diff --git a/cli/tests/unit/serve_test.ts b/cli/tests/unit/serve_test.ts
index 32d436d04f..24ae7f6664 100644
--- a/cli/tests/unit/serve_test.ts
+++ b/cli/tests/unit/serve_test.ts
@@ -1,20 +1,30 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-// deno-lint-ignore-file
-
+import { assertMatch } from "https://deno.land/std@v0.42.0/testing/asserts.ts";
import { Buffer, BufReader, BufWriter } from "../../../test_util/std/io/mod.ts";
import { TextProtoReader } from "../testdata/run/textproto.ts";
import {
assert,
assertEquals,
- assertRejects,
assertStringIncludes,
assertThrows,
Deferred,
deferred,
+ execCode,
fail,
} from "./test_util.ts";
+// Since these tests may run in parallel, ensure this port is unique to this file
+const servePort = 4502;
+
+const {
+ upgradeHttpRaw,
+ addTrailers,
+ serveHttpOnListener,
+ serveHttpOnConnection,
+ // @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol
+} = Deno[Deno.internal];
+
function createOnErrorCb(ac: AbortController): (err: unknown) => Response {
return (err) => {
console.error(err);
@@ -31,6 +41,42 @@ function onListen(
};
}
+Deno.test(async function httpServerShutsDownPortBeforeResolving() {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+
+ const server = Deno.serve({
+ handler: (_req) => new Response("ok"),
+ port: servePort,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ });
+
+ await listeningPromise;
+ assertThrows(() => Deno.listen({ port: servePort }));
+
+ ac.abort();
+ await server.finished;
+
+ const listener = Deno.listen({ port: servePort });
+ listener!.close();
+});
+
+Deno.test(
+ { permissions: { read: true, run: true } },
+ async function httpServerUnref() {
+ const [statusCode, _output] = await execCode(`
+ async function main() {
+ const server = Deno.serve({ port: 4501, handler: () => null });
+ server.unref();
+ await server.finished; // This doesn't block the program from exiting
+ }
+ main();
+ `);
+ assertEquals(statusCode, 0);
+ },
+);
+
Deno.test(async function httpServerCanResolveHostnames() {
const ac = new AbortController();
const listeningPromise = deferred();
@@ -38,14 +84,14 @@ Deno.test(async function httpServerCanResolveHostnames() {
const server = Deno.serve({
handler: (_req) => new Response("ok"),
hostname: "localhost",
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const resp = await fetch("http://localhost:4501/", {
+ const resp = await fetch(`http://localhost:${servePort}/`, {
headers: { "connection": "close" },
});
const text = await resp.text();
@@ -61,18 +107,19 @@ Deno.test(async function httpServerRejectsOnAddrInUse() {
const server = Deno.serve({
handler: (_req) => new Response("ok"),
hostname: "localhost",
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
+ await listeningPromise;
- assertRejects(
+ assertThrows(
() =>
Deno.serve({
handler: (_req) => new Response("ok"),
hostname: "localhost",
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
@@ -93,20 +140,20 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() {
// FIXME(bartlomieju):
// make sure that request can be inspected
console.log(request);
- assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/");
+ assertEquals(new URL(request.url).href, `http://127.0.0.1:${servePort}/`);
assertEquals(await request.text(), "");
assertEquals(remoteAddr.hostname, "127.0.0.1");
promise.resolve();
return new Response("Hello World", { headers: { "foo": "bar" } });
},
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const resp = await fetch("http://127.0.0.1:4501/", {
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`, {
headers: { "connection": "close" },
});
await promise;
@@ -120,13 +167,171 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() {
await server;
});
+// Test serving of HTTP on an arbitrary listener.
+Deno.test(
+ { permissions: { net: true } },
+ async function httpServerOnListener() {
+ const ac = new AbortController();
+ const promise = deferred();
+ const listeningPromise = deferred();
+ const listener = Deno.listen({ port: servePort });
+ const server = serveHttpOnListener(
+ listener,
+ ac.signal,
+ async (
+ request: Request,
+ { remoteAddr }: { remoteAddr: { hostname: string } },
+ ) => {
+ assertEquals(
+ new URL(request.url).href,
+ `http://127.0.0.1:${servePort}/`,
+ );
+ assertEquals(await request.text(), "");
+ assertEquals(remoteAddr.hostname, "127.0.0.1");
+ promise.resolve();
+ return new Response("Hello World", { headers: { "foo": "bar" } });
+ },
+ createOnErrorCb(ac),
+ onListen(listeningPromise),
+ );
+
+ await listeningPromise;
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`, {
+ headers: { "connection": "close" },
+ });
+ await promise;
+ const clone = resp.clone();
+ const text = await resp.text();
+ assertEquals(text, "Hello World");
+ assertEquals(resp.headers.get("foo"), "bar");
+ const cloneText = await clone.text();
+ assertEquals(cloneText, "Hello World");
+ ac.abort();
+ await server;
+ },
+);
+
+// Test serving of HTTP on an arbitrary connection.
+Deno.test(
+ { permissions: { net: true } },
+ async function httpServerOnConnection() {
+ const ac = new AbortController();
+ const promise = deferred();
+ const listeningPromise = deferred();
+ const listener = Deno.listen({ port: servePort });
+ const acceptPromise = listener.accept();
+ const fetchPromise = fetch(`http://127.0.0.1:${servePort}/`, {
+ headers: { "connection": "close" },
+ });
+
+ const server = serveHttpOnConnection(
+ await acceptPromise,
+ ac.signal,
+ async (
+ request: Request,
+ { remoteAddr }: { remoteAddr: { hostname: string } },
+ ) => {
+ assertEquals(
+ new URL(request.url).href,
+ `http://127.0.0.1:${servePort}/`,
+ );
+ assertEquals(await request.text(), "");
+ assertEquals(remoteAddr.hostname, "127.0.0.1");
+ promise.resolve();
+ return new Response("Hello World", { headers: { "foo": "bar" } });
+ },
+ createOnErrorCb(ac),
+ onListen(listeningPromise),
+ );
+
+ const resp = await fetchPromise;
+ await promise;
+ const clone = resp.clone();
+ const text = await resp.text();
+ assertEquals(text, "Hello World");
+ assertEquals(resp.headers.get("foo"), "bar");
+ const cloneText = await clone.text();
+ assertEquals(cloneText, "Hello World");
+ // Note that we don't need to abort this server -- it closes when the connection does
+ // ac.abort();
+ await server;
+ listener.close();
+ },
+);
+
+Deno.test({ permissions: { net: true } }, async function httpServerOnError() {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+ let requestStash: Request | null;
+
+ const server = Deno.serve({
+ handler: async (request: Request) => {
+ requestStash = request;
+ await new Promise((r) => setTimeout(r, 100));
+ throw "fail";
+ },
+ port: servePort,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ onError: () => {
+ return new Response("failed: " + requestStash!.url, { status: 500 });
+ },
+ });
+
+ await listeningPromise;
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`, {
+ headers: { "connection": "close" },
+ });
+ const text = await resp.text();
+ ac.abort();
+ await server;
+
+ assertEquals(text, `failed: http://127.0.0.1:${servePort}/`);
+});
+
+Deno.test(
+ { permissions: { net: true } },
+ async function httpServerOnErrorFails() {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+ // NOTE(bartlomieju): deno lint doesn't know that it's actually used later,
+ // but TypeScript can't see that either ¯\_(ツ)_/¯
+ // deno-lint-ignore no-unused-vars
+ let requestStash: Request | null;
+
+ const server = Deno.serve({
+ handler: async (request: Request) => {
+ requestStash = request;
+ await new Promise((r) => setTimeout(r, 100));
+ throw "fail";
+ },
+ port: servePort,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ onError: () => {
+ throw "again";
+ },
+ });
+
+ await listeningPromise;
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`, {
+ headers: { "connection": "close" },
+ });
+ const text = await resp.text();
+ ac.abort();
+ await server;
+
+ assertEquals(text, "Internal Server Error");
+ },
+);
+
Deno.test({ permissions: { net: true } }, async function httpServerOverload1() {
const ac = new AbortController();
const promise = deferred();
const listeningPromise = deferred();
const server = Deno.serve({
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
@@ -134,14 +339,14 @@ Deno.test({ permissions: { net: true } }, async function httpServerOverload1() {
// FIXME(bartlomieju):
// make sure that request can be inspected
console.log(request);
- assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/");
+ assertEquals(new URL(request.url).href, `http://127.0.0.1:${servePort}/`);
assertEquals(await request.text(), "");
promise.resolve();
return new Response("Hello World", { headers: { "foo": "bar" } });
});
await listeningPromise;
- const resp = await fetch("http://127.0.0.1:4501/", {
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`, {
headers: { "connection": "close" },
});
await promise;
@@ -160,23 +365,23 @@ Deno.test({ permissions: { net: true } }, async function httpServerOverload2() {
const promise = deferred();
const listeningPromise = deferred();
- const server = Deno.serve(async (request) => {
- // FIXME(bartlomieju):
- // make sure that request can be inspected
- console.log(request);
- assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/");
- assertEquals(await request.text(), "");
- promise.resolve();
- return new Response("Hello World", { headers: { "foo": "bar" } });
- }, {
- port: 4501,
+ const server = Deno.serve({
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
+ }, async (request) => {
+ // FIXME(bartlomieju):
+ // make sure that request can be inspected
+ console.log(request);
+ assertEquals(new URL(request.url).href, `http://127.0.0.1:${servePort}/`);
+ assertEquals(await request.text(), "");
+ promise.resolve();
+ return new Response("Hello World", { headers: { "foo": "bar" } });
});
await listeningPromise;
- const resp = await fetch("http://127.0.0.1:4501/", {
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`, {
headers: { "connection": "close" },
});
await promise;
@@ -192,18 +397,18 @@ Deno.test({ permissions: { net: true } }, async function httpServerOverload2() {
Deno.test(
{ permissions: { net: true } },
- async function httpServerErrorOverloadMissingHandler() {
+ function httpServerErrorOverloadMissingHandler() {
// @ts-ignore - testing invalid overload
- await assertRejects(() => Deno.serve(), TypeError, "handler");
+ assertThrows(() => Deno.serve(), TypeError, "handler");
// @ts-ignore - testing invalid overload
- await assertRejects(() => Deno.serve({}), TypeError, "handler");
- await assertRejects(
+ assertThrows(() => Deno.serve({}), TypeError, "handler");
+ assertThrows(
// @ts-ignore - testing invalid overload
() => Deno.serve({ handler: undefined }),
TypeError,
"handler",
);
- await assertRejects(
+ assertThrows(
// @ts-ignore - testing invalid overload
() => Deno.serve(undefined, { handler: () => {} }),
TypeError,
@@ -238,7 +443,7 @@ Deno.test(
console.log = (msg) => {
try {
const match = msg.match(/Listening on http:\/\/localhost:(\d+)\//);
- assert(!!match);
+ assert(!!match, `Didn't match ${msg}`);
const port = +match[1];
assert(port > 0 && port < 65536);
} finally {
@@ -279,14 +484,14 @@ Deno.test(
promise.resolve();
return new Response("");
},
- port: 2333,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 2333 });
+ const conn = await Deno.connect({ port: servePort });
// Send GET request with a body + content-length.
const encoder = new TextEncoder();
const body =
@@ -301,6 +506,109 @@ Deno.test(
},
);
+function createUrlTest(
+ name: string,
+ methodAndPath: string,
+ host: string | null,
+ expected: string,
+) {
+ Deno.test(`httpServerUrl${name}`, async () => {
+ const listeningPromise: Deferred = deferred();
+ const urlPromise = deferred();
+ const ac = new AbortController();
+ const server = Deno.serve({
+ handler: (request: Request) => {
+ urlPromise.resolve(request.url);
+ return new Response("");
+ },
+ port: 0,
+ signal: ac.signal,
+ onListen: ({ port }: { port: number }) => {
+ listeningPromise.resolve(port);
+ },
+ onError: createOnErrorCb(ac),
+ });
+
+ const port = await listeningPromise;
+ const conn = await Deno.connect({ port });
+
+ const encoder = new TextEncoder();
+ const body = `${methodAndPath} HTTP/1.1\r\n${
+ host ? ("Host: " + host + "\r\n") : ""
+ }Content-Length: 5\r\n\r\n12345`;
+ const writeResult = await conn.write(encoder.encode(body));
+ assertEquals(body.length, writeResult);
+
+ try {
+ const expectedResult = expected.replace("HOST", "localhost").replace(
+ "PORT",
+ `${port}`,
+ );
+ assertEquals(await urlPromise, expectedResult);
+ } finally {
+ ac.abort();
+ await server;
+ conn.close();
+ }
+ });
+}
+
+createUrlTest("WithPath", "GET /path", null, "http://HOST:PORT/path");
+createUrlTest(
+ "WithPathAndHost",
+ "GET /path",
+ "deno.land",
+ "http://deno.land/path",
+);
+createUrlTest(
+ "WithAbsolutePath",
+ "GET http://localhost/path",
+ null,
+ "http://localhost/path",
+);
+createUrlTest(
+ "WithAbsolutePathAndHost",
+ "GET http://localhost/path",
+ "deno.land",
+ "http://localhost/path",
+);
+createUrlTest(
+ "WithPortAbsolutePath",
+ "GET http://localhost:1234/path",
+ null,
+ "http://localhost:1234/path",
+);
+createUrlTest(
+ "WithPortAbsolutePathAndHost",
+ "GET http://localhost:1234/path",
+ "deno.land",
+ "http://localhost:1234/path",
+);
+createUrlTest(
+ "WithPortAbsolutePathAndHostWithPort",
+ "GET http://localhost:1234/path",
+ "deno.land:9999",
+ "http://localhost:1234/path",
+);
+
+createUrlTest("WithAsterisk", "OPTIONS *", null, "*");
+createUrlTest(
+ "WithAuthorityForm",
+ "CONNECT deno.land:80",
+ null,
+ "deno.land:80",
+);
+
+// TODO(mmastrac): These should probably be 400 errors
+createUrlTest("WithInvalidAsterisk", "GET *", null, "*");
+createUrlTest("WithInvalidNakedPath", "GET path", null, "path");
+createUrlTest(
+ "WithInvalidNakedAuthority",
+ "GET deno.land:1234",
+ null,
+ "deno.land:1234",
+);
+
Deno.test(
{ permissions: { net: true } },
async function httpServerGetRequestBody() {
@@ -314,18 +622,18 @@ Deno.test(
promise.resolve();
return new Response("", { headers: {} });
},
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4501 });
+ const conn = await Deno.connect({ port: servePort });
// Send GET request with a body + content-length.
const encoder = new TextEncoder();
const body =
- `GET / HTTP/1.1\r\nHost: 127.0.0.1:4501\r\nContent-Length: 5\r\n\r\n12345`;
+ `GET / HTTP/1.1\r\nHost: 127.0.0.1:${servePort}\r\nContent-Length: 5\r\n\r\n12345`;
const writeResult = await conn.write(encoder.encode(body));
assertEquals(body.length, writeResult);
@@ -341,36 +649,80 @@ Deno.test(
},
);
-Deno.test(
- { permissions: { net: true } },
- async function httpServerStreamResponse() {
- const stream = new TransformStream();
- const writer = stream.writable.getWriter();
- writer.write(new TextEncoder().encode("hello "));
- writer.write(new TextEncoder().encode("world"));
- writer.close();
+function createStreamTest(count: number, delay: number, action: string) {
+ function doAction(controller: ReadableStreamDefaultController, i: number) {
+ if (i == count) {
+ if (action == "Throw") {
+ controller.error(new Error("Expected error!"));
+ } else {
+ controller.close();
+ }
+ } else {
+ controller.enqueue(`a${i}`);
- const listeningPromise = deferred();
- const ac = new AbortController();
- const server = Deno.serve({
- handler: (request) => {
- assert(!request.body);
- return new Response(stream.readable);
+ if (delay == 0) {
+ doAction(controller, i + 1);
+ } else {
+ setTimeout(() => doAction(controller, i + 1), delay);
+ }
+ }
+ }
+
+ function makeStream(_count: number, delay: number): ReadableStream {
+ return new ReadableStream({
+ start(controller) {
+ if (delay == 0) {
+ doAction(controller, 0);
+ } else {
+ setTimeout(() => doAction(controller, 0), delay);
+ }
},
- port: 4501,
+ }).pipeThrough(new TextEncoderStream());
+ }
+
+ Deno.test(`httpServerStreamCount${count}Delay${delay}${action}`, async () => {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+ const server = Deno.serve({
+ handler: (_request) => {
+ return new Response(makeStream(count, delay));
+ },
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const resp = await fetch("http://127.0.0.1:4501/");
- const respBody = await resp.text();
- assertEquals("hello world", respBody);
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`);
+ const text = await resp.text();
+
ac.abort();
await server;
- },
-);
+ let expected = "";
+ if (action == "Throw" && count < 2 && delay < 1000) {
+ // NOTE: This is specific to the current implementation. In some cases where a stream errors, we
+ // don't send the first packet.
+ expected = "";
+ } else {
+ for (let i = 0; i < count; i++) {
+ expected += `a${i}`;
+ }
+ }
+
+ assertEquals(text, expected);
+ });
+}
+
+for (const count of [0, 1, 2, 3]) {
+ for (const delay of [0, 1, 1000]) {
+ // Creating a stream that errors in start will throw
+ if (delay > 0) {
+ createStreamTest(count, delay, "Throw");
+ }
+ createStreamTest(count, delay, "Close");
+ }
+}
Deno.test(
{ permissions: { net: true } },
@@ -388,14 +740,14 @@ Deno.test(
assertEquals("hello world", reqBody);
return new Response("yo");
},
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const resp = await fetch("http://127.0.0.1:4501/", {
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`, {
body: stream.readable,
method: "POST",
headers: { "connection": "close" },
@@ -412,18 +764,50 @@ Deno.test({ permissions: { net: true } }, async function httpServerClose() {
const listeningPromise = deferred();
const server = Deno.serve({
handler: () => new Response("ok"),
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const client = await Deno.connect({ port: 4501 });
+ const client = await Deno.connect({ port: servePort });
client.close();
ac.abort();
await server;
});
+// https://github.com/denoland/deno/issues/15427
+Deno.test({ permissions: { net: true } }, async function httpServerCloseGet() {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+ const requestPromise = deferred();
+ const responsePromise = deferred();
+ const server = Deno.serve({
+ handler: async () => {
+ requestPromise.resolve();
+ await new Promise((r) => setTimeout(r, 500));
+ responsePromise.resolve();
+ return new Response("ok");
+ },
+ port: servePort,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ onError: createOnErrorCb(ac),
+ });
+ await listeningPromise;
+ const conn = await Deno.connect({ port: servePort });
+ const encoder = new TextEncoder();
+ const body =
+ `GET / HTTP/1.1\r\nHost: example.domain\r\nConnection: close\r\n\r\n`;
+ const writeResult = await conn.write(encoder.encode(body));
+ assertEquals(body.length, writeResult);
+ await requestPromise;
+ conn.close();
+ await responsePromise;
+ ac.abort();
+ await server;
+});
+
// FIXME:
Deno.test(
{ permissions: { net: true } },
@@ -432,14 +816,14 @@ Deno.test(
const listeningPromise = deferred();
const server = Deno.serve({
handler: () => new Response(new Blob([])),
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const resp = await fetch("http://127.0.0.1:4501/");
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`);
const respBody = await resp.text();
assertEquals("", respBody);
@@ -467,7 +851,7 @@ Deno.test(
});
return new Response(body);
},
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: (err) => {
@@ -481,7 +865,7 @@ Deno.test(
});
await listeningPromise;
- const resp = await fetch("http://127.0.0.1:4501/");
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`);
// Incorrectly implemented reader ReadableStream should reject.
assertStringIncludes(await resp.text(), "Failed to execute 'enqueue'");
await errorPromise;
@@ -498,14 +882,14 @@ Deno.test(
const server = Deno.serve({
handler: () => new Response("韓國".repeat(10)),
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const decoder = new TextDecoder();
@@ -531,19 +915,22 @@ Deno.test({ permissions: { net: true } }, async function httpServerWebSocket() {
const ac = new AbortController();
const listeningPromise = deferred();
const server = Deno.serve({
- handler: async (request) => {
+ handler: (request) => {
const {
response,
socket,
} = Deno.upgradeWebSocket(request);
- socket.onerror = () => fail();
+ socket.onerror = (e) => {
+ console.error(e);
+ fail();
+ };
socket.onmessage = (m) => {
socket.send(m.data);
socket.close(1001);
};
return response;
},
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
@@ -551,9 +938,12 @@ Deno.test({ permissions: { net: true } }, async function httpServerWebSocket() {
await listeningPromise;
const def = deferred();
- const ws = new WebSocket("ws://localhost:4501");
+ const ws = new WebSocket(`ws://localhost:${servePort}`);
ws.onmessage = (m) => assertEquals(m.data, "foo");
- ws.onerror = () => fail();
+ ws.onerror = (e) => {
+ console.error(e);
+ fail();
+ };
ws.onclose = () => def.resolve();
ws.onopen = () => ws.send("foo");
@@ -562,6 +952,216 @@ Deno.test({ permissions: { net: true } }, async function httpServerWebSocket() {
await server;
});
+Deno.test(
+ { permissions: { net: true } },
+ async function httpServerWebSocketRaw() {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+ const server = Deno.serve({
+ handler: async (request) => {
+ const { conn, response } = upgradeHttpRaw(request);
+ const buf = new Uint8Array(1024);
+ let read;
+
+ // Write our fake HTTP upgrade
+ await conn.write(
+ new TextEncoder().encode(
+ "HTTP/1.1 101 Switching Protocols\r\nConnection: Upgraded\r\n\r\nExtra",
+ ),
+ );
+
+ // Upgrade data
+ read = await conn.read(buf);
+ assertEquals(
+ new TextDecoder().decode(buf.subarray(0, read!)),
+ "Upgrade data",
+ );
+ // Read the packet to echo
+ read = await conn.read(buf);
+ // Echo
+ await conn.write(buf.subarray(0, read!));
+
+ conn.close();
+ return response;
+ },
+ port: servePort,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ onError: createOnErrorCb(ac),
+ });
+
+ await listeningPromise;
+
+ const conn = await Deno.connect({ port: servePort });
+ await conn.write(
+ new TextEncoder().encode(
+ "GET / HTTP/1.1\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\nUpgrade data",
+ ),
+ );
+ const buf = new Uint8Array(1024);
+ let len;
+
+ // Headers
+ let headers = "";
+ for (let i = 0; i < 2; i++) {
+ len = await conn.read(buf);
+ headers += new TextDecoder().decode(buf.subarray(0, len!));
+ if (headers.endsWith("Extra")) {
+ break;
+ }
+ }
+ assertMatch(
+ headers,
+ /HTTP\/1\.1 101 Switching Protocols[ ,.A-Za-z:0-9\r\n]*Extra/im,
+ );
+
+ // Data to echo
+ await conn.write(new TextEncoder().encode("buffer data"));
+
+ // Echo
+ len = await conn.read(buf);
+ assertEquals(
+ new TextDecoder().decode(buf.subarray(0, len!)),
+ "buffer data",
+ );
+
+ conn.close();
+ ac.abort();
+ await server;
+ },
+);
+
+Deno.test(
+ { permissions: { net: true } },
+ async function httpServerWebSocketUpgradeTwice() {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+ const server = Deno.serve({
+ handler: (request) => {
+ const {
+ response,
+ socket,
+ } = Deno.upgradeWebSocket(request);
+ assertThrows(
+ () => {
+ Deno.upgradeWebSocket(request);
+ },
+ Deno.errors.Http,
+ "already upgraded",
+ );
+ socket.onerror = (e) => {
+ console.error(e);
+ fail();
+ };
+ socket.onmessage = (m) => {
+ socket.send(m.data);
+ socket.close(1001);
+ };
+ return response;
+ },
+ port: servePort,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ onError: createOnErrorCb(ac),
+ });
+
+ await listeningPromise;
+ const def = deferred();
+ const ws = new WebSocket(`ws://localhost:${servePort}`);
+ ws.onmessage = (m) => assertEquals(m.data, "foo");
+ ws.onerror = (e) => {
+ console.error(e);
+ fail();
+ };
+ ws.onclose = () => def.resolve();
+ ws.onopen = () => ws.send("foo");
+
+ await def;
+ ac.abort();
+ await server;
+ },
+);
+
+Deno.test(
+ { permissions: { net: true } },
+ async function httpServerWebSocketCloseFast() {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+ const server = Deno.serve({
+ handler: (request) => {
+ const {
+ response,
+ socket,
+ } = Deno.upgradeWebSocket(request);
+ socket.onopen = () => socket.close();
+ return response;
+ },
+ port: servePort,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ onError: createOnErrorCb(ac),
+ });
+
+ await listeningPromise;
+ const def = deferred();
+ const ws = new WebSocket(`ws://localhost:${servePort}`);
+ ws.onerror = (e) => {
+ console.error(e);
+ fail();
+ };
+ ws.onclose = () => def.resolve();
+
+ await def;
+ ac.abort();
+ await server;
+ },
+);
+
+Deno.test(
+ { permissions: { net: true } },
+ async function httpServerWebSocketCanAccessRequest() {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+ const server = Deno.serve({
+ handler: (request) => {
+ const {
+ response,
+ socket,
+ } = Deno.upgradeWebSocket(request);
+ socket.onerror = (e) => {
+ console.error(e);
+ fail();
+ };
+ socket.onmessage = (_m) => {
+ socket.send(request.url.toString());
+ socket.close(1001);
+ };
+ return response;
+ },
+ port: servePort,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ onError: createOnErrorCb(ac),
+ });
+
+ await listeningPromise;
+ const def = deferred();
+ const ws = new WebSocket(`ws://localhost:${servePort}`);
+ ws.onmessage = (m) =>
+ assertEquals(m.data, `http://localhost:${servePort}/`);
+ ws.onerror = (e) => {
+ console.error(e);
+ fail();
+ };
+ ws.onclose = () => def.resolve();
+ ws.onopen = () => ws.send("foo");
+
+ await def;
+ ac.abort();
+ await server;
+ },
+);
+
Deno.test(
{ permissions: { net: true } },
async function httpVeryLargeRequest() {
@@ -571,19 +1171,19 @@ Deno.test(
let headers: Headers;
const server = Deno.serve({
- handler: async (request) => {
+ handler: (request) => {
headers = request.headers;
promise.resolve();
return new Response("");
},
- port: 2333,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 2333 });
+ const conn = await Deno.connect({ port: servePort });
// Send GET request with a body + content-length.
const encoder = new TextEncoder();
const smthElse = "x".repeat(16 * 1024 + 256);
@@ -616,14 +1216,14 @@ Deno.test(
promise.resolve();
return new Response("");
},
- port: 2333,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 2333 });
+ const conn = await Deno.connect({ port: servePort });
// Send GET request with a body + content-length.
const encoder = new TextEncoder();
const smthElse = "x".repeat(16 * 1024 + 256);
@@ -659,14 +1259,14 @@ Deno.test(
promise.resolve();
return new Response("");
},
- port: 2333,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 2333 });
+ const conn = await Deno.connect({ port: servePort });
// Send GET request with a body + connection: close.
const encoder = new TextEncoder();
const body =
@@ -682,47 +1282,49 @@ Deno.test(
},
);
-// FIXME: auto request body reading is intefering with passing it as response.
-// Deno.test(
-// { permissions: { net: true } },
-// async function httpServerStreamDuplex() {
-// const promise = deferred();
-// const ac = new AbortController();
+Deno.test(
+ { permissions: { net: true } },
+ async function httpServerStreamDuplex() {
+ const promise = deferred();
+ const ac = new AbortController();
-// const server = Deno.serve(request => {
-// assert(request.body);
+ const server = Deno.serve(
+ { port: servePort, signal: ac.signal },
+ (request) => {
+ assert(request.body);
-// promise.resolve();
-// return new Response(request.body);
-// }, { port: 2333, signal: ac.signal });
+ promise.resolve();
+ return new Response(request.body);
+ },
+ );
-// const ts = new TransformStream();
-// const writable = ts.writable.getWriter();
+ const ts = new TransformStream();
+ const writable = ts.writable.getWriter();
-// const resp = await fetch("http://127.0.0.1:2333/", {
-// method: "POST",
-// body: ts.readable,
-// });
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`, {
+ method: "POST",
+ body: ts.readable,
+ });
-// await promise;
-// assert(resp.body);
-// const reader = resp.body.getReader();
-// await writable.write(new Uint8Array([1]));
-// const chunk1 = await reader.read();
-// assert(!chunk1.done);
-// assertEquals(chunk1.value, new Uint8Array([1]));
-// await writable.write(new Uint8Array([2]));
-// const chunk2 = await reader.read();
-// assert(!chunk2.done);
-// assertEquals(chunk2.value, new Uint8Array([2]));
-// await writable.close();
-// const chunk3 = await reader.read();
-// assert(chunk3.done);
+ await promise;
+ assert(resp.body);
+ const reader = resp.body.getReader();
+ await writable.write(new Uint8Array([1]));
+ const chunk1 = await reader.read();
+ assert(!chunk1.done);
+ assertEquals(chunk1.value, new Uint8Array([1]));
+ await writable.write(new Uint8Array([2]));
+ const chunk2 = await reader.read();
+ assert(!chunk2.done);
+ assertEquals(chunk2.value, new Uint8Array([2]));
+ await writable.close();
+ const chunk3 = await reader.read();
+ assert(chunk3.done);
-// ac.abort();
-// await server;
-// },
-// );
+ ac.abort();
+ await server;
+ },
+);
Deno.test(
{ permissions: { net: true } },
@@ -751,7 +1353,7 @@ Deno.test(
const w = new BufWriter(conn);
const r = new BufReader(conn);
- const body = `GET / HTTP/1.1\r\nHost: 127.0.0.1:4501\r\n\r\n`;
+ const body = `GET / HTTP/1.1\r\nHost: 127.0.0.1:${servePort}\r\n\r\n`;
const writeResult = await w.write(encoder.encode(body));
assertEquals(body.length, writeResult);
await w.flush();
@@ -809,7 +1411,7 @@ Deno.test(
promise.resolve();
return new Response(periodicStream());
},
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
@@ -817,7 +1419,7 @@ Deno.test(
await listeningPromise;
// start a client
- const clientConn = await Deno.connect({ port: 4501 });
+ const clientConn = await Deno.connect({ port: servePort });
const r1 = await writeRequest(clientConn);
assertEquals(r1, "0\n1\n2\n");
@@ -841,16 +1443,16 @@ Deno.test(
promise.resolve();
return new Response("hello", { headers: { "X-Header-Test": "Æ" } });
},
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const clientConn = await Deno.connect({ port: 4501 });
+ const clientConn = await Deno.connect({ port: servePort });
const requestText =
- "GET / HTTP/1.1\r\nHost: 127.0.0.1:4501\r\nX-Header-Test: á\r\n\r\n";
+ `GET / HTTP/1.1\r\nHost: 127.0.0.1:${servePort}\r\nX-Header-Test: á\r\n\r\n`;
const requestBytes = new Uint8Array(requestText.length);
for (let i = 0; i < requestText.length; i++) {
requestBytes[i] = requestText.charCodeAt(i);
@@ -864,13 +1466,13 @@ Deno.test(
await clientConn.read(buf);
await promise;
- let responseText = new TextDecoder("iso-8859-1").decode(buf);
+ const responseText = new TextDecoder("iso-8859-1").decode(buf);
clientConn.close();
- assert(/\r\n[Xx]-[Hh]eader-[Tt]est: Æ\r\n/.test(responseText));
-
ac.abort();
await server;
+
+ assertMatch(responseText, /\r\n[Xx]-[Hh]eader-[Tt]est: Æ\r\n/);
},
);
@@ -884,19 +1486,19 @@ Deno.test(
const server = Deno.serve({
handler: async (request) => {
// FIXME:
- // assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/");
+ // assertEquals(new URL(request.url).href, `http://127.0.0.1:${servePort}/`);
assertEquals(await request.text(), "");
promise.resolve();
return new Response("11");
},
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const clientConn = await Deno.connect({ port: 4501 });
+ const clientConn = await Deno.connect({ port: servePort });
async function writeRequest(conn: Deno.Conn) {
const encoder = new TextEncoder();
@@ -904,7 +1506,7 @@ Deno.test(
const w = new BufWriter(conn);
const r = new BufReader(conn);
const body =
- `CONNECT 127.0.0.1:4501 HTTP/1.1\r\nHost: 127.0.0.1:4501\r\n\r\n`;
+ `CONNECT 127.0.0.1:${servePort} HTTP/1.1\r\nHost: 127.0.0.1:${servePort}\r\n\r\n`;
const writeResult = await w.write(encoder.encode(body));
assertEquals(body.length, writeResult);
await w.flush();
@@ -942,7 +1544,7 @@ Deno.test(
promise.resolve();
return new Response("ok");
},
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
@@ -950,7 +1552,7 @@ Deno.test(
});
await listeningPromise;
- const resp = await fetch("http://127.0.0.1:4501/", {
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`, {
headers: [
["connection", "close"],
["cookie", "foo=bar"],
@@ -967,41 +1569,6 @@ Deno.test(
},
);
-Deno.test(
- { permissions: { net: true, write: true, read: true } },
- async function httpServerCorrectSizeResponse() {
- const promise = deferred();
- const listeningPromise = deferred();
- const ac = new AbortController();
-
- const tmpFile = await Deno.makeTempFile();
- const file = await Deno.open(tmpFile, { write: true, read: true });
- await file.write(new Uint8Array(70 * 1024).fill(1)); // 70kb sent in 64kb + 6kb chunks
- file.close();
-
- const server = Deno.serve({
- handler: async (request) => {
- const f = await Deno.open(tmpFile, { read: true });
- promise.resolve();
- return new Response(f.readable);
- },
- port: 4503,
- signal: ac.signal,
- onListen: onListen(listeningPromise),
- onError: createOnErrorCb(ac),
- });
-
- await listeningPromise;
- const resp = await fetch("http://127.0.0.1:4503/");
- await promise;
- const body = await resp.arrayBuffer();
-
- assertEquals(body.byteLength, 70 * 1024);
- ac.abort();
- await server;
- },
-);
-
// https://github.com/denoland/deno/issues/12741
// https://github.com/denoland/deno/pull/12746
// https://github.com/denoland/deno/pull/12798
@@ -1013,21 +1580,20 @@ Deno.test(
const ac = new AbortController();
const hostname = "localhost";
- const port = 4501;
const server = Deno.serve({
handler: () => {
promise.resolve();
return new Response("ok");
},
- port: port,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const url = `http://${hostname}:${port}/`;
+ const url = `http://${hostname}:${servePort}/`;
const args = ["-X", "DELETE", url];
const { success } = await new Deno.Command("curl", {
args,
@@ -1051,18 +1617,18 @@ Deno.test(
const ac = new AbortController();
const server = Deno.serve({
- handler: async (request) => {
+ handler: (request) => {
assertEquals(request.body, null);
promise.resolve();
return new Response(new Uint8Array([128]));
},
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const resp = await fetch("http://localhost:4501/");
+ const resp = await fetch(`http://localhost:${servePort}/`);
await promise;
@@ -1087,20 +1653,20 @@ Deno.test(
const ac = new AbortController();
const server = Deno.serve({
- handler: async (request) => {
+ handler: (request) => {
assertEquals(request.method, "GET");
assertEquals(request.headers.get("host"), "deno.land");
promise.resolve();
return new Response("ok");
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const body = `GET /echo HTTP/1.1\r\nHost: deno.land\r\n\r\n`;
const writeResult = await conn.write(encoder.encode(body));
@@ -1121,20 +1687,20 @@ Deno.test(
const ac = new AbortController();
const server = Deno.serve({
- handler: async (request) => {
+ handler: (request) => {
assertEquals(request.method, "GET");
assertEquals(request.headers.get("server"), "hello\tworld");
promise.resolve();
return new Response("ok");
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const body = `GET / HTTP/1.1\r\nserver: hello\tworld\r\n\r\n`;
const writeResult = await conn.write(encoder.encode(body));
@@ -1161,14 +1727,14 @@ Deno.test(
promise.resolve();
return new Response("ok");
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
// Connection: close = don't try to parse the body as a new request
const body =
@@ -1197,14 +1763,14 @@ Deno.test(
promise.resolve();
return new Response("ok");
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const body =
`POST / HTTP/1.1\r\nHost: example.domain\r\nContent-Length: 19\r\n\r\nI'm a good request.`;
@@ -1220,13 +1786,14 @@ Deno.test(
type TestCase = {
headers?: Record;
+ // deno-lint-ignore no-explicit-any
body: any;
- expects_chunked?: boolean;
- expects_con_len?: boolean;
+ expectsChunked?: boolean;
+ expectsConnLen?: boolean;
};
function hasHeader(msg: string, name: string): boolean {
- let n = msg.indexOf("\r\n\r\n") || msg.length;
+ const n = msg.indexOf("\r\n\r\n") || msg.length;
return msg.slice(0, n).includes(name);
}
@@ -1237,19 +1804,19 @@ function createServerLengthTest(name: string, testCase: TestCase) {
const listeningPromise = deferred();
const server = Deno.serve({
- handler: async (request) => {
+ handler: (request) => {
assertEquals(request.method, "GET");
promise.resolve();
return new Response(testCase.body, testCase.headers ?? {});
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const body =
`GET / HTTP/1.1\r\nHost: example.domain\r\nConnection: close\r\n\r\n`;
@@ -1268,23 +1835,23 @@ function createServerLengthTest(name: string, testCase: TestCase) {
msg += decoder.decode(buf.subarray(0, readResult));
try {
assert(
- testCase.expects_chunked == hasHeader(msg, "Transfer-Encoding:"),
+ testCase.expectsChunked == hasHeader(msg, "Transfer-Encoding:"),
);
- assert(testCase.expects_chunked == hasHeader(msg, "chunked"));
- assert(testCase.expects_con_len == hasHeader(msg, "Content-Length:"));
+ assert(testCase.expectsChunked == hasHeader(msg, "chunked"));
+ assert(testCase.expectsConnLen == hasHeader(msg, "Content-Length:"));
const n = msg.indexOf("\r\n\r\n") + 4;
- if (testCase.expects_chunked) {
+ if (testCase.expectsChunked) {
assertEquals(msg.slice(n + 1, n + 3), "\r\n");
assertEquals(msg.slice(msg.length - 7), "\r\n0\r\n\r\n");
}
- if (testCase.expects_con_len && typeof testCase.body === "string") {
+ if (testCase.expectsConnLen && typeof testCase.body === "string") {
assertEquals(msg.slice(n), testCase.body);
}
break;
- } catch (e) {
+ } catch {
continue;
}
}
@@ -1305,134 +1872,61 @@ function stream(s: string): ReadableStream {
createServerLengthTest("fixedResponseKnown", {
headers: { "content-length": "11" },
body: "foo bar baz",
- expects_chunked: false,
- expects_con_len: true,
+ expectsChunked: false,
+ expectsConnLen: true,
});
createServerLengthTest("fixedResponseUnknown", {
headers: { "content-length": "11" },
body: stream("foo bar baz"),
- expects_chunked: true,
- expects_con_len: false,
+ expectsChunked: true,
+ expectsConnLen: false,
});
createServerLengthTest("fixedResponseKnownEmpty", {
headers: { "content-length": "0" },
body: "",
- expects_chunked: false,
- expects_con_len: true,
+ expectsChunked: false,
+ expectsConnLen: true,
});
createServerLengthTest("chunkedRespondKnown", {
headers: { "transfer-encoding": "chunked" },
body: "foo bar baz",
- expects_chunked: false,
- expects_con_len: true,
+ expectsChunked: false,
+ expectsConnLen: true,
});
createServerLengthTest("chunkedRespondUnknown", {
headers: { "transfer-encoding": "chunked" },
body: stream("foo bar baz"),
- expects_chunked: true,
- expects_con_len: false,
+ expectsChunked: true,
+ expectsConnLen: false,
});
createServerLengthTest("autoResponseWithKnownLength", {
body: "foo bar baz",
- expects_chunked: false,
- expects_con_len: true,
+ expectsChunked: false,
+ expectsConnLen: true,
});
createServerLengthTest("autoResponseWithUnknownLength", {
body: stream("foo bar baz"),
- expects_chunked: true,
- expects_con_len: false,
+ expectsChunked: true,
+ expectsConnLen: false,
});
createServerLengthTest("autoResponseWithKnownLengthEmpty", {
body: "",
- expects_chunked: false,
- expects_con_len: true,
+ expectsChunked: false,
+ expectsConnLen: true,
});
-// FIXME: https://github.com/denoland/deno/issues/15892
-// createServerLengthTest("autoResponseWithUnknownLengthEmpty", {
-// body: stream(""),
-// expects_chunked: true,
-// expects_con_len: false,
-// });
-
-Deno.test(
- { permissions: { net: true } },
- async function httpServerGetChunkedResponseWithKa() {
- const promises = [deferred(), deferred()];
- let reqCount = 0;
- const listeningPromise = deferred();
- const ac = new AbortController();
-
- const server = Deno.serve({
- handler: async (request) => {
- assertEquals(request.method, "GET");
- promises[reqCount].resolve();
- reqCount++;
- return new Response(reqCount <= 1 ? stream("foo bar baz") : "zar quux");
- },
- port: 4503,
- signal: ac.signal,
- onListen: onListen(listeningPromise),
- onError: createOnErrorCb(ac),
- });
-
- await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
- const encoder = new TextEncoder();
- {
- const body =
- `GET / HTTP/1.1\r\nHost: example.domain\r\nConnection: keep-alive\r\n\r\n`;
- const writeResult = await conn.write(encoder.encode(body));
- assertEquals(body.length, writeResult);
- await promises[0];
- }
-
- const decoder = new TextDecoder();
- {
- let msg = "";
- while (true) {
- try {
- const buf = new Uint8Array(1024);
- const readResult = await conn.read(buf);
- assert(readResult);
- msg += decoder.decode(buf.subarray(0, readResult));
- assert(msg.endsWith("\r\nfoo bar baz\r\n0\r\n\r\n"));
- break;
- } catch {
- continue;
- }
- }
- }
-
- // once more!
- {
- const body =
- `GET /quux HTTP/1.1\r\nHost: example.domain\r\nConnection: close\r\n\r\n`;
- const writeResult = await conn.write(encoder.encode(body));
- assertEquals(body.length, writeResult);
- await promises[1];
- }
- {
- const buf = new Uint8Array(1024);
- const readResult = await conn.read(buf);
- assert(readResult);
- const msg = decoder.decode(buf.subarray(0, readResult));
- assert(msg.endsWith("zar quux"));
- }
-
- conn.close();
-
- ac.abort();
- await server;
- },
-);
+createServerLengthTest("autoResponseWithUnknownLengthEmpty", {
+ body: stream(""),
+ expectsChunked: true,
+ expectsConnLen: false,
+});
Deno.test(
{ permissions: { net: true } },
@@ -1449,14 +1943,14 @@ Deno.test(
promise.resolve();
return new Response("ok");
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const body =
@@ -1481,14 +1975,14 @@ Deno.test(
handler: () => {
throw new Error("unreachable");
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const decoder = new TextDecoder();
@@ -1524,14 +2018,14 @@ Deno.test(
promise.resolve();
return new Response("ok");
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const body =
@@ -1560,14 +2054,14 @@ Deno.test(
assertEquals(await r.text(), "12345");
return new Response("ok");
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const body =
@@ -1595,14 +2089,14 @@ Deno.test(
promise.resolve();
return new Response("NaN".repeat(100));
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const decoder = new TextDecoder();
@@ -1627,38 +2121,154 @@ Deno.test(
},
);
-Deno.test(
- { permissions: { net: true, write: true, read: true } },
- async function httpServerSendFile() {
- const promise = deferred();
- const ac = new AbortController();
- const listeningPromise = deferred();
- const tmpFile = await Deno.makeTempFile();
- const file = await Deno.open(tmpFile, { write: true, read: true });
- const data = new Uint8Array(70 * 1024).fill(1);
- await file.write(data);
- file.close();
- const server = Deno.serve({
- handler: async () => {
- const f = await Deno.open(tmpFile, { read: true });
- promise.resolve();
- return new Response(f.readable, { status: 200 });
- },
- port: 4503,
- signal: ac.signal,
- onListen: onListen(listeningPromise),
- onError: createOnErrorCb(ac),
- });
+function makeTempData(size: number) {
+ return new Uint8Array(size).fill(1);
+}
- await listeningPromise;
- const response = await fetch(`http://localhost:4503/`);
- assertEquals(response.status, 200);
- await promise;
- assertEquals(new Uint8Array(await response.arrayBuffer()), data);
- ac.abort();
- await server;
+async function makeTempFile(size: number) {
+ const tmpFile = await Deno.makeTempFile();
+ const file = await Deno.open(tmpFile, { write: true, read: true });
+ const data = makeTempData(size);
+ await file.write(data);
+ file.close();
+
+ return await Deno.open(tmpFile, { write: true, read: true });
+}
+
+const compressionTestCases = [
+ { name: "Empty", length: 0, in: {}, out: {}, expect: null },
+ {
+ name: "EmptyAcceptGzip",
+ length: 0,
+ in: { "Accept-Encoding": "gzip" },
+ out: {},
+ expect: null,
},
-);
+ // This technically would be compressible if not for the size, however the size_hint is not implemented
+ // for FileResource and we don't currently peek ahead on resources.
+ // {
+ // name: "EmptyAcceptGzip2",
+ // length: 0,
+ // in: { "Accept-Encoding": "gzip" },
+ // out: { "Content-Type": "text/plain" },
+ // expect: null,
+ // },
+ { name: "Uncompressible", length: 1024, in: {}, out: {}, expect: null },
+ {
+ name: "UncompressibleAcceptGzip",
+ length: 1024,
+ in: { "Accept-Encoding": "gzip" },
+ out: {},
+ expect: null,
+ },
+ {
+ name: "UncompressibleType",
+ length: 1024,
+ in: { "Accept-Encoding": "gzip" },
+ out: { "Content-Type": "text/fake" },
+ expect: null,
+ },
+ {
+ name: "CompressibleType",
+ length: 1024,
+ in: { "Accept-Encoding": "gzip" },
+ out: { "Content-Type": "text/plain" },
+ expect: "gzip",
+ },
+ {
+ name: "CompressibleType2",
+ length: 1024,
+ in: { "Accept-Encoding": "gzip, deflate, br" },
+ out: { "Content-Type": "text/plain" },
+ expect: "gzip",
+ },
+ {
+ name: "CompressibleType3",
+ length: 1024,
+ in: { "Accept-Encoding": "br" },
+ out: { "Content-Type": "text/plain" },
+ expect: "br",
+ },
+ {
+ name: "UncompressibleRange",
+ length: 1024,
+ in: { "Accept-Encoding": "gzip" },
+ out: { "Content-Type": "text/plain", "Content-Range": "1" },
+ expect: null,
+ },
+ {
+ name: "UncompressibleCE",
+ length: 1024,
+ in: { "Accept-Encoding": "gzip" },
+ out: { "Content-Type": "text/plain", "Content-Encoding": "random" },
+ expect: null,
+ },
+ {
+ name: "UncompressibleCC",
+ length: 1024,
+ in: { "Accept-Encoding": "gzip" },
+ out: { "Content-Type": "text/plain", "Cache-Control": "no-transform" },
+ expect: null,
+ },
+];
+
+for (const testCase of compressionTestCases) {
+ const name = `httpServerCompression${testCase.name}`;
+ Deno.test(
+ { permissions: { net: true, write: true, read: true } },
+ {
+ [name]: async function () {
+ const promise = deferred();
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+ const server = Deno.serve({
+ handler: async (_request) => {
+ const f = await makeTempFile(testCase.length);
+ promise.resolve();
+ // deno-lint-ignore no-explicit-any
+ const headers = testCase.out as any;
+ headers["Content-Length"] = testCase.length.toString();
+ return new Response(f.readable, {
+ headers: headers as HeadersInit,
+ });
+ },
+ port: servePort,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ onError: createOnErrorCb(ac),
+ });
+ try {
+ await listeningPromise;
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`, {
+ headers: testCase.in as HeadersInit,
+ });
+ await promise;
+ const body = await resp.arrayBuffer();
+ if (testCase.expect == null) {
+ assertEquals(body.byteLength, testCase.length);
+ assertEquals(
+ resp.headers.get("content-length"),
+ testCase.length.toString(),
+ );
+ assertEquals(
+ resp.headers.get("content-encoding"),
+ testCase.out["Content-Encoding"] || null,
+ );
+ } else if (testCase.expect == "gzip") {
+ // Note the fetch will transparently decompress this response, BUT we can detect that a response
+ // was compressed by the lack of a content length.
+ assertEquals(body.byteLength, testCase.length);
+ assertEquals(resp.headers.get("content-encoding"), null);
+ assertEquals(resp.headers.get("content-length"), null);
+ }
+ } finally {
+ ac.abort();
+ await server;
+ }
+ },
+ }[name],
+ );
+}
Deno.test(
{ permissions: { net: true, write: true, read: true } },
@@ -1667,27 +2277,24 @@ Deno.test(
const ac = new AbortController();
const listeningPromise = deferred();
- const tmpFile = await Deno.makeTempFile();
- const file = await Deno.open(tmpFile, { write: true, read: true });
- const data = new Uint8Array(70 * 1024).fill(1);
- await file.write(data);
- file.close();
-
const server = Deno.serve({
handler: async (request) => {
- assertEquals(new Uint8Array(await request.arrayBuffer()), data);
+ assertEquals(
+ new Uint8Array(await request.arrayBuffer()),
+ makeTempData(70 * 1024),
+ );
promise.resolve();
return new Response("ok");
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const f = await Deno.open(tmpFile, { write: true, read: true });
- const response = await fetch(`http://localhost:4503/`, {
+ const f = await makeTempFile(70 * 1024);
+ const response = await fetch(`http://localhost:${servePort}/`, {
method: "POST",
body: f.readable,
});
@@ -1708,12 +2315,11 @@ Deno.test(
const ac = new AbortController();
const listeningPromise = deferred();
const hostname = "127.0.0.1";
- const port = 4501;
const server = Deno.serve({
handler: () => new Response("Hello World"),
hostname,
- port,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
@@ -1724,7 +2330,7 @@ Deno.test(
await listeningPromise;
const caCert = Deno.readTextFileSync("cli/tests/testdata/tls/RootCA.pem");
const client = Deno.createHttpClient({ caCerts: [caCert] });
- const resp = await fetch(`https://localhost:${port}/`, {
+ const resp = await fetch(`https://localhost:${servePort}/`, {
client,
headers: { "connection": "close" },
});
@@ -1751,14 +2357,14 @@ Deno.test(
promise.resolve();
return new Response("ok");
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const body =
@@ -1784,7 +2390,7 @@ Deno.test(
handler: () => {
throw new Error("oops");
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
@@ -1802,7 +2408,7 @@ Deno.test(
await listeningPromise;
for (const teHeader of variations) {
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const body =
`POST / HTTP/1.1\r\nHost: example.domain\r\n${teHeader}\r\n\r\n0\r\n\r\n`;
const writeResult = await conn.write(encoder.encode(body));
@@ -1829,7 +2435,7 @@ Deno.test(
const ac = new AbortController();
const server = Deno.serve({
handler: (_request) => new Response(null, { status: 204 }),
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
@@ -1837,10 +2443,11 @@ Deno.test(
try {
await listeningPromise;
- const resp = await fetch("http://127.0.0.1:4501/", {
+ const resp = await fetch(`http://127.0.0.1:${servePort}/`, {
method: "GET",
headers: { "connection": "close" },
});
+ assertEquals(resp.status, 204);
assertEquals(resp.headers.get("Content-Length"), null);
} finally {
ac.abort();
@@ -1861,14 +2468,14 @@ Deno.test(
promise.resolve();
return new Response(null, { status: 304 });
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const decoder = new TextDecoder();
@@ -1907,14 +2514,14 @@ Deno.test(
assertEquals(await req.text(), "hello");
return new Response(null, { status: 304 });
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const decoder = new TextDecoder();
@@ -1968,14 +2575,14 @@ Deno.test(
assertEquals(await req.text(), "");
return new Response(null, { status: 304 });
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const decoder = new TextDecoder();
@@ -2020,14 +2627,14 @@ for (const [name, req] of badRequests) {
handler: () => {
throw new Error("oops");
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
await listeningPromise;
- const conn = await Deno.connect({ port: 4503 });
+ const conn = await Deno.connect({ port: servePort });
const encoder = new TextEncoder();
const decoder = new TextDecoder();
@@ -2064,7 +2671,7 @@ Deno.test(
let reqCount = -1;
let timerId: number | undefined;
const server = Deno.serve({
- handler: async (req) => {
+ handler: (_req) => {
reqCount++;
if (reqCount === 0) {
const msg = new TextEncoder().encode("data: hello\r\n\r\n");
@@ -2090,13 +2697,13 @@ Deno.test(
return new Response(`hello ${reqCount}`);
},
- port: 4503,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
});
- const sseRequest = await fetch(`http://localhost:4503/`);
+ const sseRequest = await fetch(`http://localhost:${servePort}/`);
const decoder = new TextDecoder();
const stream = sseRequest.body!.getReader();
@@ -2106,7 +2713,7 @@ Deno.test(
assertEquals(decoder.decode(value), "data: hello\r\n\r\n");
}
- const helloRequest = await fetch(`http://localhost:4503/`);
+ const helloRequest = await fetch(`http://localhost:${servePort}/`);
assertEquals(helloRequest.status, 200);
assertEquals(await helloRequest.text(), "hello 1");
@@ -2137,7 +2744,7 @@ Deno.test(
const server = Deno.serve({
handler: (_req) => new Response("ok"),
hostname: "localhost",
- port: 4501,
+ port: servePort,
signal: ac.signal,
onListen: onListen(listeningPromise),
onError: createOnErrorCb(ac),
@@ -2158,21 +2765,21 @@ Deno.test(
const ac = new AbortController();
const promise = deferred();
let count = 0;
- const server = Deno.serve(() => {
- count++;
- return new Response(`hello world ${count}`);
- }, {
- async onListen() {
- const res1 = await fetch("http://localhost:9000/");
+ const server = Deno.serve({
+ async onListen({ port }: { port: number }) {
+ const res1 = await fetch(`http://localhost:${port}/`);
assertEquals(await res1.text(), "hello world 1");
- const res2 = await fetch("http://localhost:9000/");
+ const res2 = await fetch(`http://localhost:${port}/`);
assertEquals(await res2.text(), "hello world 2");
promise.resolve();
ac.abort();
},
signal: ac.signal,
+ }, () => {
+ count++;
+ return new Response(`hello world ${count}`);
});
await promise;
@@ -2199,13 +2806,13 @@ Deno.test(
return new Response("ok");
},
signal: ac.signal,
- onListen: onListen(listeningPromise),
+ onListen: ({ port }: { port: number }) => listeningPromise.resolve(port),
onError: createOnErrorCb(ac),
});
try {
- await listeningPromise;
- const resp = await fetch("http://localhost:9000/", {
+ const port = await listeningPromise;
+ const resp = await fetch(`http://localhost:${port}/`, {
headers: { connection: "close" },
method: "POST",
body: '{"sus":true}',
@@ -2226,7 +2833,16 @@ Deno.test(
async function testIssue16567() {
const ac = new AbortController();
const promise = deferred();
- const server = Deno.serve(() =>
+ const server = Deno.serve({
+ async onListen({ port }) {
+ const res1 = await fetch(`http://localhost:${port}/`);
+ assertEquals((await res1.text()).length, 40 * 50_000);
+
+ promise.resolve();
+ ac.abort();
+ },
+ signal: ac.signal,
+ }, () =>
new Response(
new ReadableStream({
start(c) {
@@ -2237,16 +2853,7 @@ Deno.test(
c.close();
},
}),
- ), {
- async onListen() {
- const res1 = await fetch("http://localhost:9000/");
- assertEquals((await res1.text()).length, 40 * 50_000);
-
- promise.resolve();
- ac.abort();
- },
- signal: ac.signal,
- });
+ ));
await promise;
await server;
@@ -2384,3 +2991,129 @@ function isProhibitedForTrailer(key: string): boolean {
const s = new Set(["transfer-encoding", "content-length", "trailer"]);
return s.has(key.toLowerCase());
}
+
+Deno.test(
+ { permissions: { net: true, run: true } },
+ async function httpServeCurlH2C() {
+ const ac = new AbortController();
+ const server = Deno.serve(
+ { signal: ac.signal },
+ () => new Response("hello world!"),
+ );
+
+ assertEquals(
+ "hello world!",
+ await curlRequest(["http://localhost:8000/path"]),
+ );
+ assertEquals(
+ "hello world!",
+ await curlRequest(["http://localhost:8000/path", "--http2"]),
+ );
+ assertEquals(
+ "hello world!",
+ await curlRequest([
+ "http://localhost:8000/path",
+ "--http2",
+ "--http2-prior-knowledge",
+ ]),
+ );
+
+ ac.abort();
+ await server;
+ },
+);
+
+// TODO(mmastrac): This test should eventually use fetch, when we support trailers there.
+// This test is ignored because it's flaky and relies on cURL's verbose output.
+Deno.test(
+ { permissions: { net: true, run: true, read: true }, ignore: true },
+ async function httpServerTrailers() {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+
+ const server = Deno.serve({
+ handler: () => {
+ const response = new Response("Hello World", {
+ headers: {
+ "trailer": "baz",
+ "transfer-encoding": "chunked",
+ "foo": "bar",
+ },
+ });
+ addTrailers(response, [["baz", "why"]]);
+ return response;
+ },
+ port: servePort,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ onError: createOnErrorCb(ac),
+ });
+
+ // We don't have a great way to access this right now, so just fetch the trailers with cURL
+ const [_, stderr] = await curlRequestWithStdErr([
+ `http://localhost:${servePort}/path`,
+ "-v",
+ "--http2",
+ "--http2-prior-knowledge",
+ ]);
+ assertMatch(stderr, /baz: why/);
+ ac.abort();
+ await server;
+ },
+);
+
+Deno.test(
+ { permissions: { net: true, run: true, read: true } },
+ async function httpsServeCurlH2C() {
+ const ac = new AbortController();
+ const server = Deno.serve(
+ {
+ signal: ac.signal,
+ cert: Deno.readTextFileSync("cli/tests/testdata/tls/localhost.crt"),
+ key: Deno.readTextFileSync("cli/tests/testdata/tls/localhost.key"),
+ },
+ () => new Response("hello world!"),
+ );
+
+ assertEquals(
+ "hello world!",
+ await curlRequest(["https://localhost:9000/path", "-k"]),
+ );
+ assertEquals(
+ "hello world!",
+ await curlRequest(["https://localhost:9000/path", "-k", "--http2"]),
+ );
+ assertEquals(
+ "hello world!",
+ await curlRequest([
+ "https://localhost:9000/path",
+ "-k",
+ "--http2",
+ "--http2-prior-knowledge",
+ ]),
+ );
+
+ ac.abort();
+ await server;
+ },
+);
+
+async function curlRequest(args: string[]) {
+ const { success, stdout } = await new Deno.Command("curl", {
+ args,
+ stdout: "piped",
+ stderr: "null",
+ }).output();
+ assert(success);
+ return new TextDecoder().decode(stdout);
+}
+
+async function curlRequestWithStdErr(args: string[]) {
+ const { success, stdout, stderr } = await new Deno.Command("curl", {
+ args,
+ stdout: "piped",
+ stderr: "piped",
+ }).output();
+ assert(success);
+ return [new TextDecoder().decode(stdout), new TextDecoder().decode(stderr)];
+}
diff --git a/cli/tests/unit/stat_test.ts b/cli/tests/unit/stat_test.ts
index f386fd92fd..69730d439b 100644
--- a/cli/tests/unit/stat_test.ts
+++ b/cli/tests/unit/stat_test.ts
@@ -307,6 +307,10 @@ Deno.test(
assert(s.rdev === null);
assert(s.blksize === null);
assert(s.blocks === null);
+ assert(s.isBlockDevice === null);
+ assert(s.isCharDevice === null);
+ assert(s.isFifo === null);
+ assert(s.isSocket === null);
},
);
@@ -334,5 +338,9 @@ Deno.test(
assert(s.rdev !== null);
assert(s.blksize !== null);
assert(s.blocks !== null);
+ assert(!s.isBlockDevice);
+ assert(!s.isCharDevice);
+ assert(!s.isFifo);
+ assert(!s.isSocket);
},
);
diff --git a/cli/tests/unit/streams_deprecated.ts b/cli/tests/unit/streams_deprecated.ts
deleted file mode 100644
index 04dbfa3fb7..0000000000
--- a/cli/tests/unit/streams_deprecated.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-import { assertEquals } from "./test_util.ts";
-
-Deno.test(async function symlinkSyncPerm() {
- const rs = new ReadableStream({
- start(controller) {
- controller.enqueue("hello ");
- controller.enqueue("deno");
- controller.close();
- },
- });
-
- for await (const chunk of rs.getIterator()) {
- assertEquals(typeof chunk, "string");
- }
-});
diff --git a/cli/tests/unit/testing_test.ts b/cli/tests/unit/testing_test.ts
index 4e28d545c5..52e3baa133 100644
--- a/cli/tests/unit/testing_test.ts
+++ b/cli/tests/unit/testing_test.ts
@@ -147,3 +147,8 @@ Deno.test(async function parentOnTextContext(t1) {
});
});
});
+
+Deno.test("explicit undefined for boolean options", {
+ ignore: undefined,
+ only: undefined,
+}, () => {});
diff --git a/cli/tests/unit/timers_test.ts b/cli/tests/unit/timers_test.ts
index 8de7565169..c50cb779c6 100644
--- a/cli/tests/unit/timers_test.ts
+++ b/cli/tests/unit/timers_test.ts
@@ -557,7 +557,7 @@ Deno.test({
permissions: { run: true, read: true },
fn: async () => {
const [statusCode, output] = await execCode(`
- const timer = setTimeout(() => console.log("1"));
+ const timer = setTimeout(() => console.log("1"), 1);
Deno.unrefTimer(timer);
`);
assertEquals(statusCode, 0);
diff --git a/cli/tests/unit/tls_test.ts b/cli/tests/unit/tls_test.ts
index b7cde10208..c8dd7ddbeb 100644
--- a/cli/tests/unit/tls_test.ts
+++ b/cli/tests/unit/tls_test.ts
@@ -1337,7 +1337,7 @@ Deno.test(
await assertRejects(
() => conn.handshake(),
Deno.errors.InvalidData,
- "BadCertificate",
+ "received fatal alert",
);
}
conn.close();
@@ -1368,7 +1368,7 @@ Deno.test(
await assertRejects(
() => tlsConn.handshake(),
Deno.errors.InvalidData,
- "CertNotValidForName",
+ "NotValidForName",
);
tlsConn.close();
}
diff --git a/cli/tests/unit/url_test.ts b/cli/tests/unit/url_test.ts
index 644b8dd39a..28cf9a0e2c 100644
--- a/cli/tests/unit/url_test.ts
+++ b/cli/tests/unit/url_test.ts
@@ -32,6 +32,21 @@ Deno.test(function urlParsing() {
);
});
+Deno.test(function emptyUrl() {
+ assertThrows(
+ // @ts-ignore for test
+ () => new URL(),
+ TypeError,
+ "1 argument required, but only 0 present",
+ );
+ assertThrows(
+ // @ts-ignore for test
+ () => URL.canParse(),
+ TypeError,
+ "1 argument required, but only 0 present",
+ );
+});
+
Deno.test(function urlProtocolParsing() {
assertEquals(new URL("Aa+-.1://foo").protocol, "aa+-.1:");
assertEquals(new URL("aA+-.1://foo").protocol, "aa+-.1:");
diff --git a/cli/tests/unit/urlpattern_test.ts b/cli/tests/unit/urlpattern_test.ts
index 9bed092355..cb5fc76c53 100644
--- a/cli/tests/unit/urlpattern_test.ts
+++ b/cli/tests/unit/urlpattern_test.ts
@@ -1,5 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals } from "./test_util.ts";
+import { assertType, IsExact } from "../../../test_util/std/testing/types.ts";
Deno.test(function urlPatternFromString() {
const pattern = new URLPattern("https://deno.land/foo/:bar");
@@ -13,6 +14,10 @@ Deno.test(function urlPatternFromString() {
assert(match);
assertEquals(match.pathname.input, "/foo/x");
assertEquals(match.pathname.groups, { bar: "x" });
+
+ // group values should be nullable
+ const val = match.pathname.groups.val;
+ assertType>(true);
});
Deno.test(function urlPatternFromStringWithBase() {
diff --git a/cli/tests/unit/version_test.ts b/cli/tests/unit/version_test.ts
index f129de6b23..222aeeb851 100644
--- a/cli/tests/unit/version_test.ts
+++ b/cli/tests/unit/version_test.ts
@@ -6,5 +6,5 @@ Deno.test(function version() {
const pattern = /^\d+\.\d+\.\d+/;
assert(pattern.test(Deno.version.deno));
assert(pattern.test(Deno.version.v8));
- assertEquals(Deno.version.typescript, "5.0.3");
+ assertEquals(Deno.version.typescript, "5.0.4");
});
diff --git a/cli/tests/unit/websocket_test.ts b/cli/tests/unit/websocket_test.ts
index 948e2add23..795d5ebc18 100644
--- a/cli/tests/unit/websocket_test.ts
+++ b/cli/tests/unit/websocket_test.ts
@@ -1,5 +1,11 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-import { assertEquals, assertThrows, deferred, fail } from "./test_util.ts";
+import {
+ assert,
+ assertEquals,
+ assertThrows,
+ deferred,
+ fail,
+} from "./test_util.ts";
Deno.test({ permissions: "none" }, function websocketPermissionless() {
assertThrows(
@@ -37,6 +43,22 @@ Deno.test(async function websocketPingPong() {
ws.close();
});
+// TODO(mmastrac): This requires us to ignore bad certs
+// Deno.test(async function websocketSecureConnect() {
+// const promise = deferred();
+// const ws = new WebSocket("wss://localhost:4243/");
+// assertEquals(ws.url, "wss://localhost:4243/");
+// ws.onerror = (error) => {
+// console.log(error);
+// fail();
+// };
+// ws.onopen = () => ws.close();
+// ws.onclose = () => {
+// promise.resolve();
+// };
+// await promise;
+// });
+
// https://github.com/denoland/deno/issues/18700
Deno.test(
{ sanitizeOps: false, sanitizeResources: false },
@@ -82,3 +104,68 @@ Deno.test(
ws.close();
},
);
+
+// https://github.com/denoland/deno/issues/18775
+Deno.test({
+ sanitizeOps: false,
+ sanitizeResources: false,
+}, async function websocketDoubleClose() {
+ const promise = deferred();
+
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+
+ const server = Deno.serve({
+ handler: (req) => {
+ const { response, socket } = Deno.upgradeWebSocket(req);
+ let called = false;
+ socket.onopen = () => socket.send("Hello");
+ socket.onmessage = () => {
+ assert(!called);
+ called = true;
+ socket.send("bye");
+ socket.close();
+ };
+ socket.onclose = () => ac.abort();
+ socket.onerror = () => fail();
+ return response;
+ },
+ signal: ac.signal,
+ onListen: () => listeningPromise.resolve(),
+ hostname: "localhost",
+ port: 4247,
+ });
+
+ await listeningPromise;
+
+ const ws = new WebSocket("ws://localhost:4247/");
+ assertEquals(ws.url, "ws://localhost:4247/");
+ ws.onerror = () => fail();
+ ws.onmessage = () => ws.send("bye");
+ ws.onclose = () => {
+ promise.resolve();
+ };
+ await Promise.all([promise, server]);
+});
+
+Deno.test(
+ { sanitizeOps: false },
+ function websocketConstructorWithPrototypePollusion() {
+ const originalSymbolIterator = Array.prototype[Symbol.iterator];
+ try {
+ Array.prototype[Symbol.iterator] = () => {
+ throw Error("unreachable");
+ };
+ assertThrows(() => {
+ new WebSocket(
+ new URL("ws://localhost:4242/"),
+ // Allow `Symbol.iterator` to be called in WebIDL conversion to `sequence`
+ // deno-lint-ignore no-explicit-any
+ ["soap", "soap"].values() as any,
+ );
+ }, DOMException);
+ } finally {
+ Array.prototype[Symbol.iterator] = originalSymbolIterator;
+ }
+ },
+);
diff --git a/cli/tests/unit_node/_fs/_fs_handle_test.ts b/cli/tests/unit_node/_fs/_fs_handle_test.ts
new file mode 100644
index 0000000000..165608e1ce
--- /dev/null
+++ b/cli/tests/unit_node/_fs/_fs_handle_test.ts
@@ -0,0 +1,20 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+import * as path from "../../../../test_util/std/path/mod.ts";
+import {
+ assert,
+ assertEquals,
+} from "../../../../test_util/std/testing/asserts.ts";
+
+const moduleDir = path.dirname(path.fromFileUrl(import.meta.url));
+const testData = path.resolve(moduleDir, "testdata", "hello.txt");
+
+Deno.test("readFileSuccess", async function () {
+ const fs = await import("node:fs/promises");
+ const fileHandle = await fs.open(testData);
+ const data = await fileHandle.readFile();
+
+ assert(data instanceof Uint8Array);
+ assertEquals(new TextDecoder().decode(data as Uint8Array), "hello world");
+
+ await fileHandle.close();
+});
diff --git a/cli/tests/unit_node/async_hooks_test.ts b/cli/tests/unit_node/async_hooks_test.ts
index 73d6a99bc8..4062443151 100644
--- a/cli/tests/unit_node/async_hooks_test.ts
+++ b/cli/tests/unit_node/async_hooks_test.ts
@@ -41,7 +41,10 @@ Deno.test(async function bar() {
let differentScopeDone = false;
const als = new AsyncLocalStorage();
const ac = new AbortController();
- const server = Deno.serve(() => {
+ const server = Deno.serve({
+ signal: ac.signal,
+ port: 4000,
+ }, () => {
const differentScope = als.run(123, () =>
AsyncResource.bind(() => {
differentScopeDone = true;
@@ -54,9 +57,6 @@ Deno.test(async function bar() {
await new Promise((res) => setTimeout(res, 10));
return new Response(als.getStore() as string); // "Hello World"
});
- }, {
- signal: ac.signal,
- port: 4000,
});
const res = await fetch("http://localhost:4000");
diff --git a/cli/tests/unit_node/child_process_test.ts b/cli/tests/unit_node/child_process_test.ts
index b40cfd9ff4..f8de5b6f6d 100644
--- a/cli/tests/unit_node/child_process_test.ts
+++ b/cli/tests/unit_node/child_process_test.ts
@@ -577,3 +577,66 @@ Deno.test(
assertStringIncludes(output, "typescript");
},
);
+
+Deno.test(
+ "[node/child_process spawn] supports stdio array option",
+ async () => {
+ const cmdFinished = deferred();
+ let output = "";
+ const script = path.join(
+ path.dirname(path.fromFileUrl(import.meta.url)),
+ "testdata",
+ "child_process_stdio.js",
+ );
+ const cp = spawn(Deno.execPath(), ["run", "-A", script]);
+ cp.stdout?.on("data", (data) => {
+ output += data;
+ });
+ cp.on("close", () => cmdFinished.resolve());
+ await cmdFinished;
+
+ assertStringIncludes(output, "foo");
+ assertStringIncludes(output, "close");
+ },
+);
+
+Deno.test(
+ "[node/child_process spawn] supports stdio [0, 1, 2] option",
+ async () => {
+ const cmdFinished = deferred();
+ let output = "";
+ const script = path.join(
+ path.dirname(path.fromFileUrl(import.meta.url)),
+ "testdata",
+ "child_process_stdio_012.js",
+ );
+ const cp = spawn(Deno.execPath(), ["run", "-A", script]);
+ cp.stdout?.on("data", (data) => {
+ output += data;
+ });
+ cp.on("close", () => cmdFinished.resolve());
+ await cmdFinished;
+
+ assertStringIncludes(output, "foo");
+ assertStringIncludes(output, "close");
+ },
+);
+
+Deno.test({
+ name: "[node/child_process spawn] supports SIGIOT signal",
+ ignore: Deno.build.os === "windows",
+ async fn() {
+ const script = path.join(
+ path.dirname(path.fromFileUrl(import.meta.url)),
+ "testdata",
+ "child_process_stdin.js",
+ );
+ const cp = spawn(Deno.execPath(), ["run", "-A", script]);
+ const p = withTimeout();
+ cp.on("exit", () => p.resolve());
+ cp.kill("SIGIOT");
+ await p;
+ assert(cp.killed);
+ assertEquals(cp.signalCode, "SIGIOT");
+ },
+});
diff --git a/cli/tests/unit_node/crypto_cipher_test.ts b/cli/tests/unit_node/crypto/crypto_cipher_test.ts
similarity index 96%
rename from cli/tests/unit_node/crypto_cipher_test.ts
rename to cli/tests/unit_node/crypto/crypto_cipher_test.ts
index 2c8cca2567..a8a5130cf1 100644
--- a/cli/tests/unit_node/crypto_cipher_test.ts
+++ b/cli/tests/unit_node/crypto/crypto_cipher_test.ts
@@ -6,13 +6,13 @@ import { buffer, text } from "node:stream/consumers";
import {
assertEquals,
assertThrows,
-} from "../../../test_util/std/testing/asserts.ts";
+} from "../../../../test_util/std/testing/asserts.ts";
const rsaPrivateKey = Deno.readTextFileSync(
- new URL("./testdata/rsa_private.pem", import.meta.url),
+ new URL("../testdata/rsa_private.pem", import.meta.url),
);
const rsaPublicKey = Deno.readTextFileSync(
- new URL("./testdata/rsa_public.pem", import.meta.url),
+ new URL("../testdata/rsa_public.pem", import.meta.url),
);
const input = new TextEncoder().encode("hello world");
diff --git a/cli/tests/unit_node/crypto_hash.ts b/cli/tests/unit_node/crypto/crypto_hash_test.ts
similarity index 88%
rename from cli/tests/unit_node/crypto_hash.ts
rename to cli/tests/unit_node/crypto/crypto_hash_test.ts
index fae66e0244..6795777703 100644
--- a/cli/tests/unit_node/crypto_hash.ts
+++ b/cli/tests/unit_node/crypto/crypto_hash_test.ts
@@ -1,6 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { createHash, createHmac } from "node:crypto";
-import { assertEquals } from "../../../test_util/std/testing/asserts.ts";
+import { assertEquals } from "../../../../test_util/std/testing/asserts.ts";
// https://github.com/denoland/deno/issues/18140
Deno.test({
diff --git a/cli/tests/unit_node/crypto_key.ts b/cli/tests/unit_node/crypto/crypto_key_test.ts
similarity index 98%
rename from cli/tests/unit_node/crypto_key.ts
rename to cli/tests/unit_node/crypto/crypto_key_test.ts
index 49d81003f0..672c9fa7f0 100644
--- a/cli/tests/unit_node/crypto_key.ts
+++ b/cli/tests/unit_node/crypto/crypto_key_test.ts
@@ -13,7 +13,7 @@ import { Buffer } from "node:buffer";
import {
assertEquals,
assertThrows,
-} from "../../../test_util/std/testing/asserts.ts";
+} from "../../../../test_util/std/testing/asserts.ts";
import { createHmac } from "node:crypto";
const generateKeyPairAsync = promisify(
diff --git a/cli/tests/unit_node/crypto_sign_test.ts b/cli/tests/unit_node/crypto/crypto_sign_test.ts
similarity index 95%
rename from cli/tests/unit_node/crypto_sign_test.ts
rename to cli/tests/unit_node/crypto/crypto_sign_test.ts
index 9d346e7d02..9988ed71c3 100644
--- a/cli/tests/unit_node/crypto_sign_test.ts
+++ b/cli/tests/unit_node/crypto/crypto_sign_test.ts
@@ -3,18 +3,18 @@
import {
assert,
assertEquals,
-} from "../../../test_util/std/testing/asserts.ts";
+} from "../../../../test_util/std/testing/asserts.ts";
import { createSign, createVerify, sign, verify } from "node:crypto";
import { Buffer } from "node:buffer";
const rsaPrivatePem = Buffer.from(
await Deno.readFile(
- new URL("./testdata/rsa_private.pem", import.meta.url),
+ new URL("../testdata/rsa_private.pem", import.meta.url),
),
);
const rsaPublicPem = Buffer.from(
await Deno.readFile(
- new URL("./testdata/rsa_public.pem", import.meta.url),
+ new URL("../testdata/rsa_public.pem", import.meta.url),
),
);
diff --git a/cli/tests/unit_node/http2_test.ts b/cli/tests/unit_node/http2_test.ts
new file mode 100644
index 0000000000..543543cbdc
--- /dev/null
+++ b/cli/tests/unit_node/http2_test.ts
@@ -0,0 +1,104 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+import * as http2 from "node:http2";
+import * as net from "node:net";
+import { deferred } from "../../../test_util/std/async/deferred.ts";
+import { assertEquals } from "https://deno.land/std@v0.42.0/testing/asserts.ts";
+
+const {
+ HTTP2_HEADER_AUTHORITY,
+ HTTP2_HEADER_METHOD,
+ HTTP2_HEADER_PATH,
+ HTTP2_HEADER_STATUS,
+} = http2.constants;
+
+Deno.test("[node/http2 client]", async () => {
+ // Create a server to respond to the HTTP2 requests
+ const portPromise = deferred();
+ const reqPromise = deferred();
+ const ready = deferred();
+ const ac = new AbortController();
+ const server = Deno.serve({
+ port: 0,
+ signal: ac.signal,
+ onListen: ({ port }: { port: number }) => portPromise.resolve(port),
+ handler: async (req: Request) => {
+ reqPromise.resolve(req);
+ await ready;
+ return new Response("body", {
+ status: 401,
+ headers: { "resp-header-name": "resp-header-value" },
+ });
+ },
+ });
+
+ const port = await portPromise;
+
+ // Get a session
+ const sessionPromise = deferred();
+ const session = http2.connect(
+ `localhost:${port}`,
+ {},
+ sessionPromise.resolve.bind(sessionPromise),
+ );
+ const session2 = await sessionPromise;
+ assertEquals(session, session2);
+
+ // Write a request, including a body
+ const stream = session.request({
+ [HTTP2_HEADER_AUTHORITY]: `localhost:${port}`,
+ [HTTP2_HEADER_METHOD]: "POST",
+ [HTTP2_HEADER_PATH]: "/path",
+ "req-header-name": "req-header-value",
+ });
+ stream.write("body");
+ stream.end();
+
+ // Check the request
+ const req = await reqPromise;
+ assertEquals(req.headers.get("req-header-name"), "req-header-value");
+ assertEquals(await req.text(), "body");
+
+ ready.resolve();
+
+ // Read a response
+ const headerPromise = new Promise>((
+ resolve,
+ ) => stream.on("headers", resolve));
+ const headers = await headerPromise;
+ assertEquals(headers["resp-header-name"], "resp-header-value");
+ assertEquals(headers[HTTP2_HEADER_STATUS], "401");
+
+ ac.abort();
+ await server.finished;
+});
+
+Deno.test("[node/http2 server]", async () => {
+ const server = http2.createServer();
+ server.listen(0);
+ const port = ( server.address()).port;
+ const sessionPromise = new Promise((resolve) =>
+ server.on("session", resolve)
+ );
+
+ const responsePromise = fetch(`http://localhost:${port}/path`, {
+ method: "POST",
+ body: "body",
+ });
+
+ const session = await sessionPromise;
+ const stream = await new Promise((resolve) =>
+ session.on("stream", resolve)
+ );
+ const _headers = await new Promise((resolve) =>
+ stream.on("headers", resolve)
+ );
+ const _data = await new Promise((resolve) => stream.on("data", resolve));
+ const _end = await new Promise((resolve) => stream.on("end", resolve));
+ stream.respond();
+ stream.end();
+ const resp = await responsePromise;
+ await resp.text();
+
+ await new Promise((resolve) => server.close(resolve));
+});
diff --git a/cli/tests/unit_node/http_test.ts b/cli/tests/unit_node/http_test.ts
index 556ba16843..6b02282743 100644
--- a/cli/tests/unit_node/http_test.ts
+++ b/cli/tests/unit_node/http_test.ts
@@ -2,6 +2,7 @@
import EventEmitter from "node:events";
import http, { type RequestOptions } from "node:http";
+import https from "node:https";
import {
assert,
assertEquals,
@@ -12,6 +13,7 @@ import { deferred } from "../../../test_util/std/async/deferred.ts";
import { gzip } from "node:zlib";
import { Buffer } from "node:buffer";
import { serve } from "../../../test_util/std/http/server.ts";
+import { execCode } from "../unit/test_util.ts";
Deno.test("[node/http listen]", async () => {
{
@@ -185,27 +187,41 @@ Deno.test("[node/http] server can respond with 101, 204, 205, 304 status", async
Deno.test("[node/http] request default protocol", async () => {
const promise = deferred();
+ const promise2 = deferred();
const server = http.createServer((_, res) => {
res.end("ok");
});
+
+ // @ts-ignore IncomingMessageForClient
+ // deno-lint-ignore no-explicit-any
+ let clientRes: any;
+ // deno-lint-ignore no-explicit-any
+ let clientReq: any;
server.listen(() => {
- const req = http.request(
+ clientReq = http.request(
// deno-lint-ignore no-explicit-any
{ host: "localhost", port: (server.address() as any).port },
(res) => {
+ assert(res.socket instanceof EventEmitter);
+ assertEquals(res.complete, false);
res.on("data", () => {});
res.on("end", () => {
server.close();
});
+ clientRes = res;
assertEquals(res.statusCode, 200);
+ promise2.resolve();
},
);
- req.end();
+ clientReq.end();
});
server.on("close", () => {
promise.resolve();
});
await promise;
+ await promise2;
+ assert(clientReq.socket instanceof EventEmitter);
+ assertEquals(clientRes!.complete, true);
});
Deno.test("[node/http] request with headers", async () => {
@@ -292,32 +308,6 @@ Deno.test("[node/http] http.IncomingMessage can be created without url", () => {
});
*/
-Deno.test("[node/http] set http.IncomingMessage.statusMessage", () => {
- // deno-lint-ignore no-explicit-any
- const message = new (http as any).IncomingMessageForClient(
- new Response(null, { status: 404, statusText: "Not Found" }),
- {
- encrypted: true,
- readable: false,
- remoteAddress: "foo",
- address() {
- return { port: 443, family: "IPv4" };
- },
- // deno-lint-ignore no-explicit-any
- end(_cb: any) {
- return this;
- },
- // deno-lint-ignore no-explicit-any
- destroy(_e: any) {
- return;
- },
- },
- );
- assertEquals(message.statusMessage, "Not Found");
- message.statusMessage = "boom";
- assertEquals(message.statusMessage, "boom");
-});
-
Deno.test("[node/http] send request with non-chunked body", async () => {
let requestHeaders: Headers;
let requestBody = "";
@@ -484,3 +474,150 @@ Deno.test("[node/http] ServerResponse _implicitHeader", async () => {
await d;
});
+
+Deno.test("[node/http] server unref", async () => {
+ const [statusCode, _output] = await execCode(`
+ import http from "node:http";
+ const server = http.createServer((_req, res) => {
+ res.statusCode = status;
+ res.end("");
+ });
+
+ // This should let the program to exit without waiting for the
+ // server to close.
+ server.unref();
+
+ server.listen(async () => {
+ });
+ `);
+ assertEquals(statusCode, 0);
+});
+
+Deno.test("[node/http] ClientRequest handle non-string headers", async () => {
+ // deno-lint-ignore no-explicit-any
+ let headers: any;
+ const def = deferred();
+ const req = http.request("http://localhost:4545/echo_server", {
+ method: "POST",
+ headers: { 1: 2 },
+ }, (resp) => {
+ headers = resp.headers;
+
+ resp.on("data", () => {});
+
+ resp.on("end", () => {
+ def.resolve();
+ });
+ });
+ req.once("error", (e) => def.reject(e));
+ req.end();
+ await def;
+ assertEquals(headers!["1"], "2");
+});
+
+Deno.test("[node/http] ClientRequest uses HTTP/1.1", async () => {
+ let body = "";
+ const def = deferred();
+ const req = https.request("https://localhost:5545/http_version", {
+ method: "POST",
+ headers: { 1: 2 },
+ }, (resp) => {
+ resp.on("data", (chunk) => {
+ body += chunk;
+ });
+
+ resp.on("end", () => {
+ def.resolve();
+ });
+ });
+ req.once("error", (e) => def.reject(e));
+ req.end();
+ await def;
+ assertEquals(body, "HTTP/1.1");
+});
+
+Deno.test("[node/http] ClientRequest setTimeout", async () => {
+ let body = "";
+ const def = deferred();
+ const timer = setTimeout(() => def.reject("timed out"), 50000);
+ const req = http.request("http://localhost:4545/http_version", (resp) => {
+ resp.on("data", (chunk) => {
+ body += chunk;
+ });
+
+ resp.on("end", () => {
+ def.resolve();
+ });
+ });
+ req.setTimeout(120000);
+ req.once("error", (e) => def.reject(e));
+ req.end();
+ await def;
+ clearTimeout(timer);
+ assertEquals(body, "HTTP/1.1");
+});
+
+Deno.test("[node/http] ClientRequest PATCH", async () => {
+ let body = "";
+ const def = deferred();
+ const req = http.request("http://localhost:4545/echo_server", {
+ method: "PATCH",
+ }, (resp) => {
+ resp.on("data", (chunk) => {
+ body += chunk;
+ });
+
+ resp.on("end", () => {
+ def.resolve();
+ });
+ });
+ req.write("hello ");
+ req.write("world");
+ req.once("error", (e) => def.reject(e));
+ req.end();
+ await def;
+ assertEquals(body, "hello world");
+});
+
+Deno.test("[node/http] ClientRequest PUT", async () => {
+ let body = "";
+ const def = deferred();
+ const req = http.request("http://localhost:4545/echo_server", {
+ method: "PUT",
+ }, (resp) => {
+ resp.on("data", (chunk) => {
+ body += chunk;
+ });
+
+ resp.on("end", () => {
+ def.resolve();
+ });
+ });
+ req.write("hello ");
+ req.write("world");
+ req.once("error", (e) => def.reject(e));
+ req.end();
+ await def;
+ assertEquals(body, "hello world");
+});
+
+Deno.test("[node/http] ClientRequest search params", async () => {
+ let body = "";
+ const def = deferred();
+ const req = http.request({
+ host: "localhost:4545",
+ path: "search_params?foo=bar",
+ }, (resp) => {
+ resp.on("data", (chunk) => {
+ body += chunk;
+ });
+
+ resp.on("end", () => {
+ def.resolve();
+ });
+ });
+ req.once("error", (e) => def.reject(e));
+ req.end();
+ await def;
+ assertEquals(body, "foo=bar");
+});
diff --git a/cli/tests/unit_node/module_test.ts b/cli/tests/unit_node/module_test.ts
index d071ed2d18..3a675c7a17 100644
--- a/cli/tests/unit_node/module_test.ts
+++ b/cli/tests/unit_node/module_test.ts
@@ -1,7 +1,12 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import { Module } from "node:module";
-import { assertStrictEquals } from "../../../test_util/std/testing/asserts.ts";
+import {
+ assert,
+ assertEquals,
+} from "../../../test_util/std/testing/asserts.ts";
+import process from "node:process";
+import * as path from "node:path";
Deno.test("[node/module _preloadModules] has internal require hook", () => {
// Check if it's there
@@ -10,5 +15,46 @@ Deno.test("[node/module _preloadModules] has internal require hook", () => {
"./cli/tests/unit_node/testdata/add_global_property.js",
]);
// deno-lint-ignore no-explicit-any
- assertStrictEquals((globalThis as any).foo, "Hello");
+ assertEquals((globalThis as any).foo, "Hello");
+});
+
+Deno.test("[node/module runMain] loads module using the current process.argv", () => {
+ process.argv = [
+ process.argv[0],
+ "./cli/tests/unit_node/testdata/add_global_property_run_main.js",
+ ];
+
+ // deno-lint-ignore no-explicit-any
+ (Module as any).runMain();
+ // deno-lint-ignore no-explicit-any
+ assertEquals((globalThis as any).calledViaRunMain, true);
+});
+
+Deno.test("[node/module _nodeModulePaths] prevents duplicate /node_modules/node_modules suffix", () => {
+ // deno-lint-ignore no-explicit-any
+ const actual: string[] = (Module as any)._nodeModulePaths(
+ path.join(process.cwd(), "testdata", "node_modules", "foo"),
+ );
+
+ assert(
+ !actual.some((dir) => /node_modules[/\\]node_modules/g.test(dir)),
+ "Duplicate 'node_modules/node_modules' suffix found",
+ );
+});
+
+Deno.test("[node/module _nodeModulePaths] prevents duplicate root /node_modules", () => {
+ // deno-lint-ignore no-explicit-any
+ const actual: string[] = (Module as any)._nodeModulePaths(
+ path.join(process.cwd(), "testdata", "node_modules", "foo"),
+ );
+
+ assert(
+ new Set(actual).size === actual.length,
+ "Duplicate path entries found",
+ );
+ const root = path.parse(actual[0]).root;
+ assert(
+ actual.includes(path.join(root, "node_modules")),
+ "Missing root 'node_modules' directory",
+ );
});
diff --git a/cli/tests/unit_node/os_test.ts b/cli/tests/unit_node/os_test.ts
new file mode 100644
index 0000000000..85164d1e60
--- /dev/null
+++ b/cli/tests/unit_node/os_test.ts
@@ -0,0 +1,279 @@
+// deno-lint-ignore-file no-undef
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+import os from "node:os";
+import {
+ assert,
+ assertEquals,
+ assertThrows,
+} from "../../../test_util/std/testing/asserts.ts";
+
+Deno.test({
+ name: "build architecture is a string",
+ fn() {
+ assertEquals(typeof os.arch(), "string");
+ },
+});
+
+Deno.test({
+ name: "build architecture",
+ fn() {
+ if (Deno.build.arch == "x86_64") {
+ assertEquals(os.arch(), "x64");
+ } else if (Deno.build.arch == "aarch64") {
+ assertEquals(os.arch(), "arm64");
+ } else {
+ throw new Error("unreachable");
+ }
+ },
+});
+
+Deno.test({
+ name: "home directory is a string",
+ fn() {
+ assertEquals(typeof os.homedir(), "string");
+ },
+});
+
+Deno.test({
+ name: "tmp directory is a string",
+ fn() {
+ assertEquals(typeof os.tmpdir(), "string");
+ },
+});
+
+Deno.test({
+ name: "hostname is a string",
+ fn() {
+ assertEquals(typeof os.hostname(), "string");
+ },
+});
+
+Deno.test({
+ name: "platform is a string",
+ fn() {
+ assertEquals(typeof os.platform(), "string");
+ },
+});
+
+Deno.test({
+ name: "release is a string",
+ fn() {
+ assertEquals(typeof os.release(), "string");
+ },
+});
+
+Deno.test({
+ name: "type is a string",
+ fn() {
+ assertEquals(typeof os.type(), "string");
+ },
+});
+
+Deno.test({
+ name: "getPriority(): PID must be a 32 bit integer",
+ fn() {
+ assertThrows(
+ () => {
+ os.getPriority(3.15);
+ },
+ Error,
+ "pid must be 'an integer'",
+ );
+ assertThrows(
+ () => {
+ os.getPriority(9999999999);
+ },
+ Error,
+ "must be >= -2147483648 && <= 2147483647",
+ );
+ },
+});
+
+Deno.test({
+ name: "setPriority(): PID must be a 32 bit integer",
+ fn() {
+ assertThrows(
+ () => {
+ os.setPriority(3.15, 0);
+ },
+ Error,
+ "pid must be 'an integer'",
+ );
+ assertThrows(
+ () => {
+ os.setPriority(9999999999, 0);
+ },
+ Error,
+ "pid must be >= -2147483648 && <= 2147483647",
+ );
+ },
+});
+
+Deno.test({
+ name: "setPriority(): priority must be an integer between -20 and 19",
+ fn() {
+ assertThrows(
+ () => {
+ os.setPriority(0, 3.15);
+ },
+ Error,
+ "priority must be 'an integer'",
+ );
+ assertThrows(
+ () => {
+ os.setPriority(0, -21);
+ },
+ Error,
+ "priority must be >= -20 && <= 19",
+ );
+ assertThrows(
+ () => {
+ os.setPriority(0, 20);
+ },
+ Error,
+ "priority must be >= -20 && <= 19",
+ );
+ assertThrows(
+ () => {
+ os.setPriority(0, 9999999999);
+ },
+ Error,
+ "priority must be >= -20 && <= 19",
+ );
+ },
+});
+
+Deno.test({
+ name:
+ "setPriority(): if only one argument specified, then this is the priority, NOT the pid",
+ fn() {
+ assertThrows(
+ () => {
+ os.setPriority(3.15);
+ },
+ Error,
+ "priority must be 'an integer'",
+ );
+ assertThrows(
+ () => {
+ os.setPriority(-21);
+ },
+ Error,
+ "priority must be >= -20 && <= 19",
+ );
+ assertThrows(
+ () => {
+ os.setPriority(20);
+ },
+ Error,
+ "priority must be >= -20 && <= 19",
+ );
+ assertThrows(
+ () => {
+ os.setPriority(9999999999);
+ },
+ Error,
+ "priority must be >= -20 && <= 19",
+ );
+ },
+});
+
+Deno.test({
+ name: "EOL is as expected",
+ fn() {
+ assert(os.EOL == "\r\n" || os.EOL == "\n");
+ },
+});
+
+Deno.test({
+ name: "Endianness is determined",
+ fn() {
+ assert(["LE", "BE"].includes(os.endianness()));
+ },
+});
+
+Deno.test({
+ name: "Load average is an array of 3 numbers",
+ fn() {
+ const result = os.loadavg();
+ assert(result.length == 3);
+ assertEquals(typeof result[0], "number");
+ assertEquals(typeof result[1], "number");
+ assertEquals(typeof result[2], "number");
+ },
+});
+
+Deno.test({
+ name: "Primitive coercion works as expected",
+ fn() {
+ assertEquals(`${os.arch}`, os.arch());
+ assertEquals(`${os.endianness}`, os.endianness());
+ assertEquals(`${os.platform}`, os.platform());
+ },
+});
+
+Deno.test({
+ name: "Total memory amount should be greater than 0",
+ fn() {
+ assert(os.totalmem() > 0);
+ },
+});
+
+Deno.test({
+ name: "Free memory amount should be greater than 0",
+ fn() {
+ assert(os.freemem() > 0);
+ },
+});
+
+Deno.test({
+ name: "Uptime should be greater than 0",
+ fn() {
+ assert(os.uptime() > 0);
+ },
+});
+
+Deno.test({
+ name: "os.cpus()",
+ fn() {
+ assertEquals(os.cpus().length, navigator.hardwareConcurrency);
+
+ for (const cpu of os.cpus()) {
+ assertEquals(cpu.model, "");
+ assertEquals(cpu.speed, 0);
+ assertEquals(cpu.times.user, 0);
+ assertEquals(cpu.times.nice, 0);
+ assertEquals(cpu.times.sys, 0);
+ assertEquals(cpu.times.idle, 0);
+ assertEquals(cpu.times.irq, 0);
+ }
+ },
+});
+
+Deno.test({
+ name: "APIs not yet implemented",
+ fn() {
+ assertThrows(
+ () => {
+ os.getPriority();
+ },
+ Error,
+ "Not implemented",
+ );
+ assertThrows(
+ () => {
+ os.setPriority(0);
+ },
+ Error,
+ "Not implemented",
+ );
+ assertThrows(
+ () => {
+ os.userInfo();
+ },
+ Error,
+ "Not implemented",
+ );
+ },
+});
diff --git a/cli/tests/unit_node/process_test.ts b/cli/tests/unit_node/process_test.ts
index 686a3dbbc5..7e927a8ad2 100644
--- a/cli/tests/unit_node/process_test.ts
+++ b/cli/tests/unit_node/process_test.ts
@@ -2,6 +2,8 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
import process, { argv, env } from "node:process";
+import { Readable } from "node:stream";
+import { once } from "node:events";
import {
assert,
assertEquals,
@@ -727,3 +729,38 @@ Deno.test({
assertEquals(stripColor(decoder.decode(stdout).trim()), "exit");
},
});
+
+Deno.test({
+ name: "process.reallyExit",
+ async fn() {
+ const command = new Deno.Command(Deno.execPath(), {
+ args: [
+ "run",
+ "--quiet",
+ "--unstable",
+ "./testdata/process_really_exit.ts",
+ ],
+ cwd: testDir,
+ });
+ const { stdout } = await command.output();
+
+ const decoder = new TextDecoder();
+ assertEquals(stripColor(decoder.decode(stdout).trim()), "really exited");
+ },
+});
+
+Deno.test({
+ name: "process.stdout isn't closed when source stream ended",
+ async fn() {
+ const source = Readable.from(["foo", "bar"]);
+
+ source.pipe(process.stdout);
+ await once(source, "end");
+
+ // Wait a bit to ensure that streaming is completely finished.
+ await delay(10);
+
+ // This checks if the rid 1 is still valid.
+ assert(typeof process.stdout.isTTY === "boolean");
+ },
+});
diff --git a/cli/tests/unit_node/readline_test.ts b/cli/tests/unit_node/readline_test.ts
index bef9008dd7..914d23e4af 100644
--- a/cli/tests/unit_node/readline_test.ts
+++ b/cli/tests/unit_node/readline_test.ts
@@ -12,3 +12,16 @@ Deno.test("[node/readline] createInstance", () => {
// deno-lint-ignore no-explicit-any
assertInstanceOf(rl, Interface as any);
});
+
+// Test for https://github.com/denoland/deno/issues/19183
+Deno.test("[node/readline] don't throw on rl.question()", () => {
+ const rli = createInterface({
+ input: new Readable({ read() {} }),
+ output: new Writable({ write() {} }),
+ terminal: true,
+ });
+
+ // Calling this would throw
+ rli.question("foo", () => rli.close());
+ rli.close();
+});
diff --git a/cli/tests/unit_node/testdata/add_global_property_run_main.js b/cli/tests/unit_node/testdata/add_global_property_run_main.js
new file mode 100644
index 0000000000..c9db1cea66
--- /dev/null
+++ b/cli/tests/unit_node/testdata/add_global_property_run_main.js
@@ -0,0 +1 @@
+globalThis.calledViaRunMain = true;
diff --git a/cli/tests/unit_node/testdata/child_process_stdio.js b/cli/tests/unit_node/testdata/child_process_stdio.js
new file mode 100644
index 0000000000..399b890ed1
--- /dev/null
+++ b/cli/tests/unit_node/testdata/child_process_stdio.js
@@ -0,0 +1,15 @@
+import childProcess from "node:child_process";
+import process from "node:process";
+import * as path from "node:path";
+
+const script = path.join(
+ path.dirname(path.fromFileUrl(import.meta.url)),
+ "node_modules",
+ "foo",
+ "index.js",
+);
+
+const child = childProcess.spawn(process.execPath, [script], {
+ stdio: [process.stdin, process.stdout, process.stderr],
+});
+child.on("close", () => console.log("close"));
diff --git a/cli/tests/unit_node/testdata/child_process_stdio_012.js b/cli/tests/unit_node/testdata/child_process_stdio_012.js
new file mode 100644
index 0000000000..682d8a084a
--- /dev/null
+++ b/cli/tests/unit_node/testdata/child_process_stdio_012.js
@@ -0,0 +1,15 @@
+import childProcess from "node:child_process";
+import process from "node:process";
+import * as path from "node:path";
+
+const script = path.join(
+ path.dirname(path.fromFileUrl(import.meta.url)),
+ "node_modules",
+ "foo",
+ "index.js",
+);
+
+const child = childProcess.spawn(process.execPath, [script], {
+ stdio: [0, 1, 2],
+});
+child.on("close", () => console.log("close"));
diff --git a/cli/tests/unit_node/testdata/process_really_exit.ts b/cli/tests/unit_node/testdata/process_really_exit.ts
new file mode 100644
index 0000000000..16f30b33d2
--- /dev/null
+++ b/cli/tests/unit_node/testdata/process_really_exit.ts
@@ -0,0 +1,10 @@
+import process from "node:process";
+
+//deno-lint-ignore no-undef
+// @ts-ignore - Node typings don't even have this because it's
+// been deprecated for 4 years. But it's used in `signal-exit`,
+// which in turn is used in `node-tap`.
+process.reallyExit = function () {
+ console.info("really exited");
+};
+process.exit();
diff --git a/cli/tests/unit_node/testdata/worker_threads.mjs b/cli/tests/unit_node/testdata/worker_threads.mjs
new file mode 100644
index 0000000000..03dc462f02
--- /dev/null
+++ b/cli/tests/unit_node/testdata/worker_threads.mjs
@@ -0,0 +1,34 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+import {
+ getEnvironmentData,
+ isMainThread,
+ parentPort,
+ threadId,
+ workerData,
+} from "node:worker_threads";
+import { once } from "node:events";
+
+async function message(expectedMessage) {
+ const [message] = await once(parentPort, "message");
+ if (message !== expectedMessage) {
+ console.log(`Expected the message "${expectedMessage}", but got`, message);
+ // fail test
+ parentPort.close();
+ }
+}
+
+await message("Hello, how are you my thread?");
+
+parentPort.postMessage("I'm fine!");
+
+await new Promise((resolve) => setTimeout(resolve, 100));
+
+parentPort.postMessage({
+ isMainThread,
+ threadId,
+ workerData: Array.isArray(workerData) &&
+ workerData[workerData.length - 1] instanceof MessagePort
+ ? workerData.slice(0, -1)
+ : workerData,
+ envData: [getEnvironmentData("test"), getEnvironmentData(1)],
+});
diff --git a/cli/tests/unit_node/v8_test.ts b/cli/tests/unit_node/v8_test.ts
index ab19035962..724ac35044 100644
--- a/cli/tests/unit_node/v8_test.ts
+++ b/cli/tests/unit_node/v8_test.ts
@@ -4,10 +4,7 @@ import {
getHeapStatistics,
setFlagsFromString,
} from "node:v8";
-import {
- assertEquals,
- assertThrows,
-} from "../../../test_util/std/testing/asserts.ts";
+import { assertEquals } from "../../../test_util/std/testing/asserts.ts";
// https://github.com/nodejs/node/blob/a2bbe5ff216bc28f8dac1c36a8750025a93c3827/test/parallel/test-v8-version-tag.js#L6
Deno.test({
@@ -51,8 +48,8 @@ Deno.test({
});
Deno.test({
- name: "setFlagsFromString throws",
+ name: "setFlagsFromString",
fn() {
- assertThrows(() => setFlagsFromString("--allow_natives_syntax"));
+ setFlagsFromString("--allow_natives_syntax");
},
});
diff --git a/cli/tests/unit_node/worker_threads_test.ts b/cli/tests/unit_node/worker_threads_test.ts
new file mode 100644
index 0000000000..f53b1e6927
--- /dev/null
+++ b/cli/tests/unit_node/worker_threads_test.ts
@@ -0,0 +1,194 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+import {
+ assert,
+ assertEquals,
+ assertObjectMatch,
+} from "../../../test_util/std/testing/asserts.ts";
+import { fromFileUrl, relative } from "../../../test_util/std/path/mod.ts";
+import * as workerThreads from "node:worker_threads";
+import { EventEmitter, once } from "node:events";
+
+Deno.test("[node/worker_threads] BroadcastChannel is exported", () => {
+ assertEquals(workerThreads.BroadcastChannel, BroadcastChannel);
+});
+
+Deno.test("[node/worker_threads] MessageChannel are MessagePort are exported", () => {
+ assertEquals(workerThreads.MessageChannel, MessageChannel);
+ assertEquals(workerThreads.MessagePort, MessagePort);
+});
+
+Deno.test({
+ name: "[worker_threads] isMainThread",
+ fn() {
+ assertEquals(workerThreads.isMainThread, true);
+ },
+});
+
+Deno.test({
+ name: "[worker_threads] threadId",
+ fn() {
+ assertEquals(workerThreads.threadId, 0);
+ },
+});
+
+Deno.test({
+ name: "[worker_threads] resourceLimits",
+ fn() {
+ assertObjectMatch(workerThreads.resourceLimits, {});
+ },
+});
+
+Deno.test({
+ name: "[worker_threads] parentPort",
+ fn() {
+ assertEquals(workerThreads.parentPort, null);
+ },
+});
+
+Deno.test({
+ name: "[worker_threads] workerData",
+ fn() {
+ assertEquals(workerThreads.workerData, null);
+ },
+});
+
+Deno.test({
+ name: "[worker_threads] setEnvironmentData / getEnvironmentData",
+ fn() {
+ workerThreads.setEnvironmentData("test", "test");
+ assertEquals(workerThreads.getEnvironmentData("test"), "test");
+ },
+});
+
+Deno.test({
+ name: "[worker_threads] Worker threadId",
+ async fn() {
+ const worker = new workerThreads.Worker(
+ new URL("./testdata/worker_threads.mjs", import.meta.url),
+ );
+ worker.postMessage("Hello, how are you my thread?");
+ await once(worker, "message");
+ const message = await once(worker, "message");
+ assertEquals(message[0].threadId, 1);
+ worker.terminate();
+
+ const worker1 = new workerThreads.Worker(
+ new URL("./testdata/worker_threads.mjs", import.meta.url),
+ );
+ worker1.postMessage("Hello, how are you my thread?");
+ await once(worker1, "message");
+ assertEquals((await once(worker1, "message"))[0].threadId, 2);
+ worker1.terminate();
+ },
+});
+
+Deno.test({
+ name: "[worker_threads] Worker basics",
+ async fn() {
+ workerThreads.setEnvironmentData("test", "test");
+ workerThreads.setEnvironmentData(1, {
+ test: "random",
+ random: "test",
+ });
+ const { port1 } = new MessageChannel();
+ const worker = new workerThreads.Worker(
+ new URL("./testdata/worker_threads.mjs", import.meta.url),
+ {
+ workerData: ["hey", true, false, 2, port1],
+ // deno-lint-ignore no-explicit-any
+ transferList: [port1 as any],
+ },
+ );
+ worker.postMessage("Hello, how are you my thread?");
+ assertEquals((await once(worker, "message"))[0], "I'm fine!");
+ const data = (await once(worker, "message"))[0];
+ // data.threadId can be 1 when this test is runned individually
+ if (data.threadId === 1) data.threadId = 3;
+ assertObjectMatch(data, {
+ isMainThread: false,
+ threadId: 3,
+ workerData: ["hey", true, false, 2],
+ envData: ["test", { test: "random", random: "test" }],
+ });
+ worker.terminate();
+ },
+ sanitizeResources: false,
+});
+
+Deno.test({
+ name: "[worker_threads] Worker eval",
+ async fn() {
+ const worker = new workerThreads.Worker(
+ `
+ import { parentPort } from "node:worker_threads";
+ parentPort.postMessage("It works!");
+ `,
+ {
+ eval: true,
+ },
+ );
+ assertEquals((await once(worker, "message"))[0], "It works!");
+ worker.terminate();
+ },
+});
+
+Deno.test({
+ name: "[worker_threads] inheritences",
+ async fn() {
+ const worker = new workerThreads.Worker(
+ `
+ import { EventEmitter } from "node:events";
+ import { parentPort } from "node:worker_threads";
+ parentPort.postMessage(parentPort instanceof EventTarget);
+ await new Promise(resolve => setTimeout(resolve, 100));
+ parentPort.postMessage(parentPort instanceof EventEmitter);
+ `,
+ {
+ eval: true,
+ },
+ );
+ assertEquals((await once(worker, "message"))[0], true);
+ assertEquals((await once(worker, "message"))[0], false);
+ assert(worker instanceof EventEmitter);
+ assert(!(worker instanceof EventTarget));
+ worker.terminate();
+ },
+});
+
+Deno.test({
+ name: "[worker_threads] Worker workerData",
+ async fn() {
+ const worker = new workerThreads.Worker(
+ new URL("./testdata/worker_threads.mjs", import.meta.url),
+ {
+ workerData: null,
+ },
+ );
+ worker.postMessage("Hello, how are you my thread?");
+ await once(worker, "message");
+ assertEquals((await once(worker, "message"))[0].workerData, null);
+ worker.terminate();
+
+ const worker1 = new workerThreads.Worker(
+ new URL("./testdata/worker_threads.mjs", import.meta.url),
+ );
+ worker1.postMessage("Hello, how are you my thread?");
+ await once(worker1, "message");
+ assertEquals((await once(worker1, "message"))[0].workerData, undefined);
+ worker1.terminate();
+ },
+});
+
+Deno.test({
+ name: "[worker_threads] Worker with relative path",
+ async fn() {
+ const worker = new workerThreads.Worker(relative(
+ Deno.cwd(),
+ fromFileUrl(new URL("./testdata/worker_threads.mjs", import.meta.url)),
+ ));
+ worker.postMessage("Hello, how are you my thread?");
+ assertEquals((await once(worker, "message"))[0], "I'm fine!");
+ worker.terminate();
+ },
+});
diff --git a/cli/tools/bench.rs b/cli/tools/bench.rs
index 0b6ef8bb1d..a7b75d8be8 100644
--- a/cli/tools/bench.rs
+++ b/cli/tools/bench.rs
@@ -2,12 +2,12 @@
use crate::args::BenchOptions;
use crate::args::CliOptions;
-use crate::args::TypeCheckMode;
use crate::colors;
use crate::display::write_json_to_stdout;
+use crate::factory::CliFactory;
use crate::graph_util::graph_valid_with_cli_options;
+use crate::module_loader::ModuleLoadPreparer;
use crate::ops;
-use crate::proc_state::ProcState;
use crate::tools::test::format_test_error;
use crate::tools::test::TestFilter;
use crate::util::file_watcher;
@@ -15,7 +15,7 @@ use crate::util::file_watcher::ResolutionResult;
use crate::util::fs::collect_specifiers;
use crate::util::path::is_supported_ext;
use crate::version::get_user_agent;
-use crate::worker::create_custom_worker;
+use crate::worker::CliMainWorkerFactory;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
@@ -26,17 +26,18 @@ use deno_core::futures::FutureExt;
use deno_core::futures::StreamExt;
use deno_core::located_script_name;
use deno_core::serde_v8;
+use deno_core::task::spawn;
+use deno_core::task::spawn_blocking;
use deno_core::v8;
use deno_core::ModuleSpecifier;
use deno_runtime::permissions::Permissions;
use deno_runtime::permissions::PermissionsContainer;
-use deno_runtime::tokio_util::run_local;
+use deno_runtime::tokio_util::create_and_run_current_thread;
use indexmap::IndexMap;
use indexmap::IndexSet;
use log::Level;
use serde::Deserialize;
use serde::Serialize;
-use std::cell::RefCell;
use std::collections::HashSet;
use std::path::Path;
use std::path::PathBuf;
@@ -48,6 +49,7 @@ use tokio::sync::mpsc::UnboundedSender;
struct BenchSpecifierOptions {
filter: TestFilter,
json: bool,
+ log_level: Option,
}
#[derive(Debug, Clone, Eq, PartialEq, Deserialize)]
@@ -417,40 +419,38 @@ impl BenchReporter for ConsoleReporter {
/// Type check a collection of module and document specifiers.
async fn check_specifiers(
- ps: &ProcState,
- permissions: Permissions,
+ cli_options: &CliOptions,
+ module_load_preparer: &ModuleLoadPreparer,
specifiers: Vec,
) -> Result<(), AnyError> {
- let lib = ps.options.ts_type_lib_window();
- ps.module_load_preparer
+ let lib = cli_options.ts_type_lib_window();
+ module_load_preparer
.prepare_module_load(
specifiers,
false,
lib,
PermissionsContainer::allow_all(),
- PermissionsContainer::new(permissions),
)
.await?;
-
Ok(())
}
/// Run a single specifier as an executable bench module.
async fn bench_specifier(
- ps: ProcState,
+ worker_factory: Arc,
permissions: Permissions,
specifier: ModuleSpecifier,
sender: UnboundedSender,
filter: TestFilter,
) -> Result<(), AnyError> {
- let mut worker = create_custom_worker(
- &ps,
- specifier.clone(),
- PermissionsContainer::new(permissions),
- vec![ops::bench::deno_bench::init_ops(sender.clone())],
- Default::default(),
- )
- .await?;
+ let mut worker = worker_factory
+ .create_custom_worker(
+ specifier.clone(),
+ PermissionsContainer::new(permissions),
+ vec![ops::bench::deno_bench::init_ops(sender.clone())],
+ Default::default(),
+ )
+ .await?;
// We execute the main module as a side module so that import.meta.main is not set.
worker.execute_side_module_possibly_with_npm().await?;
@@ -492,53 +492,46 @@ async fn bench_specifier(
}))?;
for (desc, function) in benchmarks {
sender.send(BenchEvent::Wait(desc.id))?;
- let promise = {
- let scope = &mut worker.js_runtime.handle_scope();
- let cb = function.open(scope);
- let this = v8::undefined(scope).into();
- let promise = cb.call(scope, this, &[]).unwrap();
- v8::Global::new(scope, promise)
- };
- let result = worker.js_runtime.resolve_value(promise).await?;
+ let result = worker.js_runtime.call_and_await(&function).await?;
let scope = &mut worker.js_runtime.handle_scope();
let result = v8::Local::new(scope, result);
let result = serde_v8::from_v8::(scope, result)?;
sender.send(BenchEvent::Result(desc.id, result))?;
}
- loop {
- if !worker.dispatch_beforeunload_event(located_script_name!())? {
- break;
- }
- worker.run_event_loop(false).await?;
- }
+ // Ignore `defaultPrevented` of the `beforeunload` event. We don't allow the
+ // event loop to continue beyond what's needed to await results.
+ worker.dispatch_beforeunload_event(located_script_name!())?;
worker.dispatch_unload_event(located_script_name!())?;
Ok(())
}
/// Test a collection of specifiers with test modes concurrently.
async fn bench_specifiers(
- ps: &ProcState,
+ worker_factory: Arc,
permissions: &Permissions,
specifiers: Vec,
options: BenchSpecifierOptions,
) -> Result<(), AnyError> {
- let log_level = ps.options.log_level();
-
let (sender, mut receiver) = unbounded_channel::();
-
+ let log_level = options.log_level;
let option_for_handles = options.clone();
let join_handles = specifiers.into_iter().map(move |specifier| {
- let ps = ps.clone();
+ let worker_factory = worker_factory.clone();
let permissions = permissions.clone();
let specifier = specifier;
let sender = sender.clone();
let options = option_for_handles.clone();
- tokio::task::spawn_blocking(move || {
- let future =
- bench_specifier(ps, permissions, specifier, sender, options.filter);
- run_local(future)
+ spawn_blocking(move || {
+ let future = bench_specifier(
+ worker_factory,
+ permissions,
+ specifier,
+ sender,
+ options.filter,
+ );
+ create_and_run_current_thread(future)
})
});
@@ -547,7 +540,7 @@ async fn bench_specifiers(
.collect::, tokio::task::JoinError>>>();
let handler = {
- tokio::task::spawn(async move {
+ spawn(async move {
let mut used_only = false;
let mut report = BenchReport::new();
let mut reporter =
@@ -640,12 +633,13 @@ pub async fn run_benchmarks(
cli_options: CliOptions,
bench_options: BenchOptions,
) -> Result<(), AnyError> {
- let ps = ProcState::from_cli_options(Arc::new(cli_options)).await?;
+ let factory = CliFactory::from_cli_options(Arc::new(cli_options));
+ let cli_options = factory.cli_options();
// Various bench files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable.
let permissions =
- Permissions::from_options(&ps.options.permissions_options())?;
+ Permissions::from_options(&cli_options.permissions_options())?;
let specifiers =
collect_specifiers(&bench_options.files, is_supported_bench_path)?;
@@ -654,19 +648,28 @@ pub async fn run_benchmarks(
return Err(generic_error("No bench modules found"));
}
- check_specifiers(&ps, permissions.clone(), specifiers.clone()).await?;
+ check_specifiers(
+ cli_options,
+ factory.module_load_preparer().await?,
+ specifiers.clone(),
+ )
+ .await?;
if bench_options.no_run {
return Ok(());
}
+ let log_level = cli_options.log_level();
+ let worker_factory =
+ Arc::new(factory.create_cli_main_worker_factory().await?);
bench_specifiers(
- &ps,
+ worker_factory,
&permissions,
specifiers,
BenchSpecifierOptions {
filter: TestFilter::from_flag(&bench_options.filter),
json: bench_options.json,
+ log_level,
},
)
.await?;
@@ -679,22 +682,25 @@ pub async fn run_benchmarks_with_watch(
cli_options: CliOptions,
bench_options: BenchOptions,
) -> Result<(), AnyError> {
- let ps = ProcState::from_cli_options(Arc::new(cli_options)).await?;
+ let factory = CliFactory::from_cli_options(Arc::new(cli_options));
+ let cli_options = factory.cli_options();
+ let module_graph_builder = factory.module_graph_builder().await?;
+ let file_watcher = factory.file_watcher()?;
+ let module_load_preparer = factory.module_load_preparer().await?;
// Various bench files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable.
let permissions =
- Permissions::from_options(&ps.options.permissions_options())?;
- let no_check = ps.options.type_check_mode() == TypeCheckMode::None;
-
- let ps = RefCell::new(ps);
+ Permissions::from_options(&cli_options.permissions_options())?;
+ let graph_kind = cli_options.type_check_mode().as_graph_kind();
let resolver = |changed: Option>| {
let paths_to_watch = bench_options.files.include.clone();
let paths_to_watch_clone = paths_to_watch.clone();
let files_changed = changed.is_some();
let bench_options = &bench_options;
- let ps = ps.borrow().clone();
+ let module_graph_builder = module_graph_builder.clone();
+ let cli_options = cli_options.clone();
async move {
let bench_modules =
@@ -706,11 +712,10 @@ pub async fn run_benchmarks_with_watch(
} else {
bench_modules.clone()
};
- let graph = ps
- .module_graph_builder
- .create_graph(bench_modules.clone())
+ let graph = module_graph_builder
+ .create_graph(graph_kind, bench_modules.clone())
.await?;
- graph_valid_with_cli_options(&graph, &bench_modules, &ps.options)?;
+ graph_valid_with_cli_options(&graph, &bench_modules, &cli_options)?;
// TODO(@kitsonk) - This should be totally derivable from the graph.
for specifier in bench_modules {
@@ -720,32 +725,19 @@ pub async fn run_benchmarks_with_watch(
// This needs to be accessible to skip getting dependencies if they're already there,
// otherwise this will cause a stack overflow with circular dependencies
output: &mut HashSet<&'a ModuleSpecifier>,
- no_check: bool,
) {
if let Some(module) = maybe_module.and_then(|m| m.esm()) {
for dep in module.dependencies.values() {
if let Some(specifier) = &dep.get_code() {
if !output.contains(specifier) {
output.insert(specifier);
- get_dependencies(
- graph,
- graph.get(specifier),
- output,
- no_check,
- );
+ get_dependencies(graph, graph.get(specifier), output);
}
}
- if !no_check {
- if let Some(specifier) = &dep.get_type() {
- if !output.contains(specifier) {
- output.insert(specifier);
- get_dependencies(
- graph,
- graph.get(specifier),
- output,
- no_check,
- );
- }
+ if let Some(specifier) = &dep.get_type() {
+ if !output.contains(specifier) {
+ output.insert(specifier);
+ get_dependencies(graph, graph.get(specifier), output);
}
}
}
@@ -755,7 +747,7 @@ pub async fn run_benchmarks_with_watch(
// This bench module and all it's dependencies
let mut modules = HashSet::new();
modules.insert(&specifier);
- get_dependencies(&graph, graph.get(&specifier), &mut modules, no_check);
+ get_dependencies(&graph, graph.get(&specifier), &mut modules);
paths_to_watch.extend(
modules
@@ -800,32 +792,40 @@ pub async fn run_benchmarks_with_watch(
})
};
+ let create_cli_main_worker_factory =
+ factory.create_cli_main_worker_factory_func().await?;
let operation = |modules_to_reload: Vec| {
let permissions = &permissions;
let bench_options = &bench_options;
- ps.borrow_mut().reset_for_file_watcher();
- let ps = ps.borrow().clone();
+ file_watcher.reset();
+ let module_load_preparer = module_load_preparer.clone();
+ let cli_options = cli_options.clone();
+ let create_cli_main_worker_factory = create_cli_main_worker_factory.clone();
async move {
+ let worker_factory = Arc::new(create_cli_main_worker_factory());
let specifiers =
collect_specifiers(&bench_options.files, is_supported_bench_path)?
.into_iter()
.filter(|specifier| modules_to_reload.contains(specifier))
.collect::>();
- check_specifiers(&ps, permissions.clone(), specifiers.clone()).await?;
+ check_specifiers(&cli_options, &module_load_preparer, specifiers.clone())
+ .await?;
if bench_options.no_run {
return Ok(());
}
+ let log_level = cli_options.log_level();
bench_specifiers(
- &ps,
+ worker_factory,
permissions,
specifiers,
BenchSpecifierOptions {
filter: TestFilter::from_flag(&bench_options.filter),
json: bench_options.json,
+ log_level,
},
)
.await?;
@@ -834,7 +834,7 @@ pub async fn run_benchmarks_with_watch(
}
};
- let clear_screen = !ps.borrow().options.no_clear_screen();
+ let clear_screen = !cli_options.no_clear_screen();
file_watcher::watch_func(
resolver,
operation,
@@ -931,7 +931,10 @@ mod mitata {
sysctl.arg("-n");
sysctl.arg("machdep.cpu.brand_string");
return std::str::from_utf8(
- &sysctl.output().map_or(Vec::from("unknown"), |x| x.stdout),
+ &sysctl
+ .output()
+ .map(|x| x.stdout)
+ .unwrap_or(Vec::from("unknown")),
)
.unwrap()
.trim()
@@ -1138,13 +1141,13 @@ mod mitata {
} else {
if options.avg {
s.push_str(&format!(
- "{:>23}",
+ "{:>30}",
format!("{}/iter", colors::yellow(fmt_duration(stats.avg)))
));
}
if options.min_max {
s.push_str(&format!(
- "{:>42}",
+ "{:>50}",
format!(
"({} … {})",
colors::cyan(fmt_duration(stats.min)),
@@ -1154,7 +1157,7 @@ mod mitata {
}
if options.percentiles {
s.push_str(&format!(
- " {:>18} {:>18} {:>18}",
+ " {:>22} {:>22} {:>22}",
colors::magenta(fmt_duration(stats.p75)),
colors::magenta(fmt_duration(stats.p99)),
colors::magenta(fmt_duration(stats.p995))
diff --git a/cli/tools/bundle.rs b/cli/tools/bundle.rs
index 26d170d7e2..f38948776d 100644
--- a/cli/tools/bundle.rs
+++ b/cli/tools/bundle.rs
@@ -12,9 +12,8 @@ use crate::args::BundleFlags;
use crate::args::CliOptions;
use crate::args::Flags;
use crate::args::TsConfigType;
-use crate::args::TypeCheckMode;
+use crate::factory::CliFactory;
use crate::graph_util::error_for_any_npm_specifier;
-use crate::proc_state::ProcState;
use crate::util;
use crate::util::display;
use crate::util::file_watcher::ResolutionResult;
@@ -40,9 +39,11 @@ pub async fn bundle(
let module_specifier = &module_specifier;
async move {
log::debug!(">>>>> bundle START");
- let ps = ProcState::from_cli_options(cli_options).await?;
- let graph = ps
- .module_graph_builder
+ let factory = CliFactory::from_cli_options(cli_options);
+ let module_graph_builder = factory.module_graph_builder().await?;
+ let cli_options = factory.cli_options();
+
+ let graph = module_graph_builder
.create_graph_and_maybe_check(vec![module_specifier.clone()])
.await?;
@@ -58,15 +59,14 @@ pub async fn bundle(
})
.collect();
- if let Ok(Some(import_map_path)) = ps
- .options
+ if let Ok(Some(import_map_path)) = cli_options
.resolve_import_map_specifier()
.map(|ms| ms.and_then(|ref s| s.to_file_path().ok()))
{
paths_to_watch.push(import_map_path);
}
- Ok((paths_to_watch, graph, ps))
+ Ok((paths_to_watch, graph, cli_options.clone()))
}
.map(move |result| match result {
Ok((paths_to_watch, graph, ps)) => ResolutionResult::Restart {
@@ -80,49 +80,50 @@ pub async fn bundle(
})
};
- let operation = |(ps, graph): (ProcState, Arc)| {
- let out_file = &bundle_flags.out_file;
- async move {
- // at the moment, we don't support npm specifiers in deno bundle, so show an error
- error_for_any_npm_specifier(&graph)?;
+ let operation =
+ |(cli_options, graph): (Arc, Arc)| {
+ let out_file = &bundle_flags.out_file;
+ async move {
+ // at the moment, we don't support npm specifiers in deno bundle, so show an error
+ error_for_any_npm_specifier(&graph)?;
- let bundle_output = bundle_module_graph(graph.as_ref(), &ps)?;
- log::debug!(">>>>> bundle END");
+ let bundle_output = bundle_module_graph(graph.as_ref(), &cli_options)?;
+ log::debug!(">>>>> bundle END");
- if let Some(out_file) = out_file {
- let output_bytes = bundle_output.code.as_bytes();
- let output_len = output_bytes.len();
- util::fs::write_file(out_file, output_bytes, 0o644)?;
- log::info!(
- "{} {:?} ({})",
- colors::green("Emit"),
- out_file,
- colors::gray(display::human_size(output_len as f64))
- );
- if let Some(bundle_map) = bundle_output.maybe_map {
- let map_bytes = bundle_map.as_bytes();
- let map_len = map_bytes.len();
- let ext = if let Some(curr_ext) = out_file.extension() {
- format!("{}.map", curr_ext.to_string_lossy())
- } else {
- "map".to_string()
- };
- let map_out_file = out_file.with_extension(ext);
- util::fs::write_file(&map_out_file, map_bytes, 0o644)?;
+ if let Some(out_file) = out_file {
+ let output_bytes = bundle_output.code.as_bytes();
+ let output_len = output_bytes.len();
+ util::fs::write_file(out_file, output_bytes, 0o644)?;
log::info!(
"{} {:?} ({})",
colors::green("Emit"),
- map_out_file,
- colors::gray(display::human_size(map_len as f64))
+ out_file,
+ colors::gray(display::human_size(output_len as f64))
);
+ if let Some(bundle_map) = bundle_output.maybe_map {
+ let map_bytes = bundle_map.as_bytes();
+ let map_len = map_bytes.len();
+ let ext = if let Some(curr_ext) = out_file.extension() {
+ format!("{}.map", curr_ext.to_string_lossy())
+ } else {
+ "map".to_string()
+ };
+ let map_out_file = out_file.with_extension(ext);
+ util::fs::write_file(&map_out_file, map_bytes, 0o644)?;
+ log::info!(
+ "{} {:?} ({})",
+ colors::green("Emit"),
+ map_out_file,
+ colors::gray(display::human_size(map_len as f64))
+ );
+ }
+ } else {
+ println!("{}", bundle_output.code);
}
- } else {
- println!("{}", bundle_output.code);
- }
- Ok(())
- }
- };
+ Ok(())
+ }
+ };
if cli_options.watch_paths().is_some() {
util::file_watcher::watch_func(
@@ -149,14 +150,13 @@ pub async fn bundle(
fn bundle_module_graph(
graph: &deno_graph::ModuleGraph,
- ps: &ProcState,
+ cli_options: &CliOptions,
) -> Result {
log::info!("{} {}", colors::green("Bundle"), graph.roots[0]);
- let ts_config_result = ps
- .options
- .resolve_ts_config_for_emit(TsConfigType::Bundle)?;
- if ps.options.type_check_mode() == TypeCheckMode::None {
+ let ts_config_result =
+ cli_options.resolve_ts_config_for_emit(TsConfigType::Bundle)?;
+ if !cli_options.type_check_mode().is_true() {
if let Some(ignored_options) = ts_config_result.maybe_ignored_options {
log::warn!("{}", ignored_options);
}
diff --git a/cli/tools/check.rs b/cli/tools/check.rs
index c7f4042233..4464802e6e 100644
--- a/cli/tools/check.rs
+++ b/cli/tools/check.rs
@@ -9,6 +9,7 @@ use deno_core::error::AnyError;
use deno_graph::Module;
use deno_graph::ModuleGraph;
use deno_runtime::colors;
+use deno_runtime::deno_node::NodeResolver;
use once_cell::sync::Lazy;
use regex::Regex;
@@ -18,11 +19,9 @@ use crate::args::TsConfigType;
use crate::args::TsTypeLib;
use crate::args::TypeCheckMode;
use crate::cache::Caches;
-use crate::cache::DenoDir;
use crate::cache::FastInsecureHasher;
use crate::cache::TypeCheckCache;
-use crate::node::CliNodeResolver;
-use crate::npm::NpmPackageResolver;
+use crate::npm::CliNpmResolver;
use crate::tsc;
use crate::version;
@@ -39,23 +38,20 @@ pub struct CheckOptions {
}
pub struct TypeChecker {
- deno_dir: DenoDir,
caches: Arc,
cli_options: Arc,
- node_resolver: Arc,
- npm_resolver: Arc,
+ node_resolver: Arc,
+ npm_resolver: Arc,
}
impl TypeChecker {
pub fn new(
- deno_dir: DenoDir,
caches: Arc,
cli_options: Arc,
- node_resolver: Arc,
- npm_resolver: Arc,
+ node_resolver: Arc,
+ npm_resolver: Arc,
) -> Self {
Self {
- deno_dir,
caches,
cli_options,
node_resolver,
@@ -95,8 +91,7 @@ impl TypeChecker {
let ts_config = ts_config_result.ts_config;
let type_check_mode = self.cli_options.type_check_mode();
let debug = self.cli_options.log_level() == Some(log::Level::Debug);
- let cache =
- TypeCheckCache::new(self.caches.type_checking_cache_db(&self.deno_dir));
+ let cache = TypeCheckCache::new(self.caches.type_checking_cache_db());
let check_js = ts_config.get_check_js();
let check_hash = match get_check_hash(&graph, type_check_mode, &ts_config) {
CheckHashResult::NoFiles => return Ok(()),
diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs
new file mode 100644
index 0000000000..c53ae4e028
--- /dev/null
+++ b/cli/tools/compile.rs
@@ -0,0 +1,271 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use crate::args::CompileFlags;
+use crate::args::Flags;
+use crate::factory::CliFactory;
+use crate::standalone::is_standalone_binary;
+use crate::util::path::path_has_trailing_slash;
+use deno_core::anyhow::bail;
+use deno_core::anyhow::Context;
+use deno_core::error::generic_error;
+use deno_core::error::AnyError;
+use deno_core::resolve_url_or_path;
+use deno_graph::GraphKind;
+use deno_runtime::colors;
+use std::path::Path;
+use std::path::PathBuf;
+use std::sync::Arc;
+
+use super::installer::infer_name_from_url;
+
+pub async fn compile(
+ flags: Flags,
+ compile_flags: CompileFlags,
+) -> Result<(), AnyError> {
+ let factory = CliFactory::from_flags(flags).await?;
+ let cli_options = factory.cli_options();
+ let module_graph_builder = factory.module_graph_builder().await?;
+ let parsed_source_cache = factory.parsed_source_cache()?;
+ let binary_writer = factory.create_compile_binary_writer().await?;
+ let module_specifier = cli_options.resolve_main_module()?;
+ let module_roots = {
+ let mut vec = Vec::with_capacity(compile_flags.include.len() + 1);
+ vec.push(module_specifier.clone());
+ for side_module in &compile_flags.include {
+ vec.push(resolve_url_or_path(side_module, cli_options.initial_cwd())?);
+ }
+ vec
+ };
+
+ let output_path = resolve_compile_executable_output_path(
+ &compile_flags,
+ cli_options.initial_cwd(),
+ )
+ .await?;
+
+ let graph = Arc::try_unwrap(
+ module_graph_builder
+ .create_graph_and_maybe_check(module_roots.clone())
+ .await?,
+ )
+ .unwrap();
+ let graph = if cli_options.type_check_mode().is_true() {
+ // In this case, the previous graph creation did type checking, which will
+ // create a module graph with types information in it. We don't want to
+ // store that in the eszip so create a code only module graph from scratch.
+ module_graph_builder
+ .create_graph(GraphKind::CodeOnly, module_roots)
+ .await?
+ } else {
+ graph
+ };
+
+ let parser = parsed_source_cache.as_capturing_parser();
+ let eszip = eszip::EszipV2::from_graph(graph, &parser, Default::default())?;
+
+ log::info!(
+ "{} {} to {}",
+ colors::green("Compile"),
+ module_specifier.to_string(),
+ output_path.display(),
+ );
+ validate_output_path(&output_path)?;
+
+ let mut file = std::fs::File::create(&output_path)?;
+ binary_writer
+ .write_bin(
+ &mut file,
+ eszip,
+ &module_specifier,
+ &compile_flags,
+ cli_options,
+ )
+ .await
+ .with_context(|| format!("Writing {}", output_path.display()))?;
+ drop(file);
+
+ // set it as executable
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::PermissionsExt;
+ let perms = std::fs::Permissions::from_mode(0o777);
+ std::fs::set_permissions(output_path, perms)?;
+ }
+
+ Ok(())
+}
+
+/// This function writes out a final binary to specified path. If output path
+/// is not already standalone binary it will return error instead.
+fn validate_output_path(output_path: &Path) -> Result<(), AnyError> {
+ if output_path.exists() {
+ // If the output is a directory, throw error
+ if output_path.is_dir() {
+ bail!(
+ concat!(
+ "Could not compile to file '{}' because a directory exists with ",
+ "the same name. You can use the `--output ` flag to ",
+ "provide an alternative name."
+ ),
+ output_path.display()
+ );
+ }
+
+ // Make sure we don't overwrite any file not created by Deno compiler because
+ // this filename is chosen automatically in some cases.
+ if !is_standalone_binary(output_path) {
+ bail!(
+ concat!(
+ "Could not compile to file '{}' because the file already exists ",
+ "and cannot be overwritten. Please delete the existing file or ",
+ "use the `--output ` flag to provide an alternative name."
+ ),
+ output_path.display()
+ );
+ }
+
+ // Remove file if it was indeed a deno compiled binary, to avoid corruption
+ // (see https://github.com/denoland/deno/issues/10310)
+ std::fs::remove_file(output_path)?;
+ } else {
+ let output_base = &output_path.parent().unwrap();
+ if output_base.exists() && output_base.is_file() {
+ bail!(
+ concat!(
+ "Could not compile to file '{}' because its parent directory ",
+ "is an existing file. You can use the `--output ` flag to ",
+ "provide an alternative name.",
+ ),
+ output_base.display(),
+ );
+ }
+ std::fs::create_dir_all(output_base)?;
+ }
+
+ Ok(())
+}
+
+async fn resolve_compile_executable_output_path(
+ compile_flags: &CompileFlags,
+ current_dir: &Path,
+) -> Result {
+ let module_specifier =
+ resolve_url_or_path(&compile_flags.source_file, current_dir)?;
+
+ let mut output = compile_flags.output.clone();
+
+ if let Some(out) = output.as_ref() {
+ if path_has_trailing_slash(out) {
+ if let Some(infer_file_name) = infer_name_from_url(&module_specifier)
+ .await
+ .map(PathBuf::from)
+ {
+ output = Some(out.join(infer_file_name));
+ }
+ } else {
+ output = Some(out.to_path_buf());
+ }
+ }
+
+ if output.is_none() {
+ output = infer_name_from_url(&module_specifier)
+ .await
+ .map(PathBuf::from)
+ }
+
+ output.ok_or_else(|| generic_error(
+ "An executable name was not provided. One could not be inferred from the URL. Aborting.",
+ )).map(|output| {
+ get_os_specific_filepath(output, &compile_flags.target)
+ })
+}
+
+fn get_os_specific_filepath(
+ output: PathBuf,
+ target: &Option,
+) -> PathBuf {
+ let is_windows = match target {
+ Some(target) => target.contains("windows"),
+ None => cfg!(windows),
+ };
+ if is_windows && output.extension().unwrap_or_default() != "exe" {
+ if let Some(ext) = output.extension() {
+ // keep version in my-exe-0.1.0 -> my-exe-0.1.0.exe
+ output.with_extension(format!("{}.exe", ext.to_string_lossy()))
+ } else {
+ output.with_extension("exe")
+ }
+ } else {
+ output
+ }
+}
+
+#[cfg(test)]
+mod test {
+ pub use super::*;
+
+ #[tokio::test]
+ async fn resolve_compile_executable_output_path_target_linux() {
+ let path = resolve_compile_executable_output_path(
+ &CompileFlags {
+ source_file: "mod.ts".to_string(),
+ output: Some(PathBuf::from("./file")),
+ args: Vec::new(),
+ target: Some("x86_64-unknown-linux-gnu".to_string()),
+ include: vec![],
+ },
+ &std::env::current_dir().unwrap(),
+ )
+ .await
+ .unwrap();
+
+ // no extension, no matter what the operating system is
+ // because the target was specified as linux
+ // https://github.com/denoland/deno/issues/9667
+ assert_eq!(path.file_name().unwrap(), "file");
+ }
+
+ #[tokio::test]
+ async fn resolve_compile_executable_output_path_target_windows() {
+ let path = resolve_compile_executable_output_path(
+ &CompileFlags {
+ source_file: "mod.ts".to_string(),
+ output: Some(PathBuf::from("./file")),
+ args: Vec::new(),
+ target: Some("x86_64-pc-windows-msvc".to_string()),
+ include: vec![],
+ },
+ &std::env::current_dir().unwrap(),
+ )
+ .await
+ .unwrap();
+ assert_eq!(path.file_name().unwrap(), "file.exe");
+ }
+
+ #[test]
+ fn test_os_specific_file_path() {
+ fn run_test(path: &str, target: Option<&str>, expected: &str) {
+ assert_eq!(
+ get_os_specific_filepath(
+ PathBuf::from(path),
+ &target.map(|s| s.to_string())
+ ),
+ PathBuf::from(expected)
+ );
+ }
+
+ if cfg!(windows) {
+ run_test("C:\\my-exe", None, "C:\\my-exe.exe");
+ run_test("C:\\my-exe.exe", None, "C:\\my-exe.exe");
+ run_test("C:\\my-exe-0.1.2", None, "C:\\my-exe-0.1.2.exe");
+ } else {
+ run_test("my-exe", Some("linux"), "my-exe");
+ run_test("my-exe-0.1.2", Some("linux"), "my-exe-0.1.2");
+ }
+
+ run_test("C:\\my-exe", Some("windows"), "C:\\my-exe.exe");
+ run_test("C:\\my-exe.exe", Some("windows"), "C:\\my-exe.exe");
+ run_test("C:\\my-exe.0.1.2", Some("windows"), "C:\\my-exe.0.1.2.exe");
+ run_test("my-exe-0.1.2", Some("linux"), "my-exe-0.1.2");
+ }
+}
diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs
index 0297782436..223bac3167 100644
--- a/cli/tools/coverage/mod.rs
+++ b/cli/tools/coverage/mod.rs
@@ -4,8 +4,9 @@ use crate::args::CoverageFlags;
use crate::args::FileFlags;
use crate::args::Flags;
use crate::colors;
-use crate::proc_state::ProcState;
+use crate::factory::CliFactory;
use crate::tools::fmt::format_json;
+use crate::tools::test::is_supported_test_path;
use crate::util::fs::FileCollector;
use crate::util::text_encoding::source_map_from_code;
@@ -27,6 +28,7 @@ use std::io::BufWriter;
use std::io::Error;
use std::io::Write;
use std::io::{self};
+use std::path::Path;
use std::path::PathBuf;
use text_lines::TextLines;
use uuid::Uuid;
@@ -602,7 +604,8 @@ fn filter_coverages(
|| e.url.starts_with(npm_root_dir)
|| e.url.ends_with("__anonymous__")
|| e.url.ends_with("$deno$test.js")
- || e.url.ends_with(".snap");
+ || e.url.ends_with(".snap")
+ || is_supported_test_path(Path::new(e.url.as_str()));
let is_included = include.iter().any(|p| p.is_match(&e.url));
let is_excluded = exclude.iter().any(|p| p.is_match(&e.url));
@@ -620,8 +623,11 @@ pub async fn cover_files(
return Err(generic_error("No matching coverage profiles found"));
}
- let ps = ProcState::from_flags(flags).await?;
- let root_dir_url = ps.npm_resolver.root_dir_url();
+ let factory = CliFactory::from_flags(flags).await?;
+ let root_dir_url = factory.npm_resolver().await?.root_dir_url();
+ let file_fetcher = factory.file_fetcher()?;
+ let cli_options = factory.cli_options();
+ let emitter = factory.emitter()?;
let script_coverages = collect_coverages(coverage_flags.files)?;
let script_coverages = filter_coverages(
@@ -664,13 +670,13 @@ pub async fn cover_files(
for script_coverage in script_coverages {
let module_specifier = deno_core::resolve_url_or_path(
&script_coverage.url,
- ps.options.initial_cwd(),
+ cli_options.initial_cwd(),
)?;
let maybe_file = if module_specifier.scheme() == "file" {
- ps.file_fetcher.get_source(&module_specifier)
+ file_fetcher.get_source(&module_specifier)
} else {
- ps.file_fetcher
+ file_fetcher
.fetch_cached(&module_specifier, 10)
.with_context(|| {
format!("Failed to fetch \"{module_specifier}\" from cache.")
@@ -697,7 +703,7 @@ pub async fn cover_files(
| MediaType::Mts
| MediaType::Cts
| MediaType::Tsx => {
- match ps.emitter.maybed_cached_emit(&file.specifier, &file.source) {
+ match emitter.maybed_cached_emit(&file.specifier, &file.source) {
Some(code) => code.into(),
None => {
return Err(anyhow!(
diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs
index a07ba175aa..87fa253151 100644
--- a/cli/tools/doc.rs
+++ b/cli/tools/doc.rs
@@ -6,9 +6,9 @@ use crate::args::Flags;
use crate::colors;
use crate::display::write_json_to_stdout;
use crate::display::write_to_stdout_ignore_sigpipe;
+use crate::factory::CliFactory;
use crate::file_fetcher::File;
use crate::graph_util::graph_lock_or_exit;
-use crate::proc_state::ProcState;
use crate::tsc::get_types_declaration_file_text;
use deno_ast::MediaType;
use deno_core::anyhow::bail;
@@ -16,6 +16,7 @@ use deno_core::error::AnyError;
use deno_core::resolve_path;
use deno_core::resolve_url_or_path;
use deno_doc as doc;
+use deno_graph::GraphKind;
use deno_graph::ModuleSpecifier;
use std::path::PathBuf;
@@ -23,13 +24,14 @@ pub async fn print_docs(
flags: Flags,
doc_flags: DocFlags,
) -> Result<(), AnyError> {
- let ps = ProcState::from_flags(flags).await?;
+ let factory = CliFactory::from_flags(flags).await?;
+ let cli_options = factory.cli_options();
let mut doc_nodes = match doc_flags.source_file {
DocSourceFileFlag::Builtin => {
let source_file_specifier =
ModuleSpecifier::parse("internal://lib.deno.d.ts").unwrap();
- let content = get_types_declaration_file_text(ps.options.unstable());
+ let content = get_types_declaration_file_text(cli_options.unstable());
let mut loader = deno_graph::source::MemoryLoader::new(
vec![(
source_file_specifier.to_string(),
@@ -42,7 +44,7 @@ pub async fn print_docs(
Vec::new(),
);
let analyzer = deno_graph::CapturingModuleAnalyzer::default();
- let mut graph = deno_graph::ModuleGraph::default();
+ let mut graph = deno_graph::ModuleGraph::new(GraphKind::TypesOnly);
graph
.build(
vec![source_file_specifier.clone()],
@@ -61,13 +63,18 @@ pub async fn print_docs(
doc_parser.parse_module(&source_file_specifier)?.definitions
}
DocSourceFileFlag::Path(source_file) => {
+ let file_fetcher = factory.file_fetcher()?;
+ let module_graph_builder = factory.module_graph_builder().await?;
+ let maybe_lockfile = factory.maybe_lockfile();
+ let parsed_source_cache = factory.parsed_source_cache()?;
+
let module_specifier =
- resolve_url_or_path(&source_file, ps.options.initial_cwd())?;
+ resolve_url_or_path(&source_file, cli_options.initial_cwd())?;
// If the root module has external types, the module graph won't redirect it,
// so instead create a dummy file which exports everything from the actual file being documented.
let root_specifier =
- resolve_path("./$deno$doc.ts", ps.options.initial_cwd()).unwrap();
+ resolve_path("./$deno$doc.ts", cli_options.initial_cwd()).unwrap();
let root = File {
local: PathBuf::from("./$deno$doc.ts"),
maybe_types: None,
@@ -78,21 +85,20 @@ pub async fn print_docs(
};
// Save our fake file into file fetcher cache.
- ps.file_fetcher.insert_cached(root);
+ file_fetcher.insert_cached(root);
- let graph = ps
- .module_graph_builder
- .create_graph(vec![root_specifier.clone()])
+ let graph = module_graph_builder
+ .create_graph(GraphKind::TypesOnly, vec![root_specifier.clone()])
.await?;
- if let Some(lockfile) = &ps.lockfile {
+ if let Some(lockfile) = maybe_lockfile {
graph_lock_or_exit(&graph, &mut lockfile.lock());
}
let doc_parser = doc::DocParser::new(
graph,
doc_flags.private,
- ps.parsed_source_cache.as_capturing_parser(),
+ parsed_source_cache.as_capturing_parser(),
);
doc_parser.parse_with_reexports(&root_specifier)?
}
diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs
index 41accacba9..f2fec93023 100644
--- a/cli/tools/fmt.rs
+++ b/cli/tools/fmt.rs
@@ -12,8 +12,8 @@ use crate::args::FilesConfig;
use crate::args::FmtOptions;
use crate::args::FmtOptionsConfig;
use crate::args::ProseWrap;
-use crate::cache::Caches;
use crate::colors;
+use crate::factory::CliFactory;
use crate::util::diff::diff;
use crate::util::file_watcher;
use crate::util::file_watcher::ResolutionResult;
@@ -28,6 +28,7 @@ use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::futures;
use deno_core::parking_lot::Mutex;
+use deno_core::task::spawn_blocking;
use log::debug;
use log::info;
use log::warn;
@@ -101,11 +102,12 @@ pub async fn format(
}
}
};
- let deno_dir = &cli_options.resolve_deno_dir()?;
- let caches = Caches::default();
+ let factory = CliFactory::from_cli_options(Arc::new(cli_options));
+ let cli_options = factory.cli_options();
+ let caches = factory.caches()?;
let operation = |(paths, fmt_options): (Vec, FmtOptionsConfig)| async {
let incremental_cache = Arc::new(IncrementalCache::new(
- caches.fmt_incremental_cache_db(deno_dir),
+ caches.fmt_incremental_cache_db(),
&fmt_options,
&paths,
));
@@ -628,7 +630,7 @@ where
let handles = file_paths.iter().map(|file_path| {
let f = f.clone();
let file_path = file_path.clone();
- tokio::task::spawn_blocking(move || f(file_path))
+ spawn_blocking(move || f(file_path))
});
let join_results = futures::future::join_all(handles).await;
diff --git a/cli/tools/info.rs b/cli/tools/info.rs
index 69faa10fbc..95a7da7b0f 100644
--- a/cli/tools/info.rs
+++ b/cli/tools/info.rs
@@ -11,6 +11,7 @@ use deno_core::resolve_url_or_path;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_graph::Dependency;
+use deno_graph::GraphKind;
use deno_graph::Module;
use deno_graph::ModuleError;
use deno_graph::ModuleGraph;
@@ -27,57 +28,61 @@ use deno_semver::npm::NpmPackageReqReference;
use crate::args::Flags;
use crate::args::InfoFlags;
use crate::display;
+use crate::factory::CliFactory;
use crate::graph_util::graph_lock_or_exit;
-use crate::npm::NpmPackageResolver;
-use crate::proc_state::ProcState;
+use crate::npm::CliNpmResolver;
use crate::util::checksum;
pub async fn info(flags: Flags, info_flags: InfoFlags) -> Result<(), AnyError> {
- let ps = ProcState::from_flags(flags).await?;
+ let factory = CliFactory::from_flags(flags).await?;
+ let cli_options = factory.cli_options();
if let Some(specifier) = info_flags.file {
- let specifier = resolve_url_or_path(&specifier, ps.options.initial_cwd())?;
- let mut loader = ps.module_graph_builder.create_graph_loader();
+ let module_graph_builder = factory.module_graph_builder().await?;
+ let npm_resolver = factory.npm_resolver().await?;
+ let maybe_lockfile = factory.maybe_lockfile();
+ let specifier = resolve_url_or_path(&specifier, cli_options.initial_cwd())?;
+ let mut loader = module_graph_builder.create_graph_loader();
loader.enable_loading_cache_info(); // for displaying the cache information
- let graph = ps
- .module_graph_builder
- .create_graph_with_loader(vec![specifier], &mut loader)
+ let graph = module_graph_builder
+ .create_graph_with_loader(GraphKind::All, vec![specifier], &mut loader)
.await?;
- if let Some(lockfile) = &ps.lockfile {
+ if let Some(lockfile) = maybe_lockfile {
graph_lock_or_exit(&graph, &mut lockfile.lock());
}
if info_flags.json {
let mut json_graph = json!(graph);
- add_npm_packages_to_json(&mut json_graph, &ps.npm_resolver);
+ add_npm_packages_to_json(&mut json_graph, npm_resolver);
display::write_json_to_stdout(&json_graph)?;
} else {
let mut output = String::new();
- GraphDisplayContext::write(&graph, &ps.npm_resolver, &mut output)?;
+ GraphDisplayContext::write(&graph, npm_resolver, &mut output)?;
display::write_to_stdout_ignore_sigpipe(output.as_bytes())?;
}
} else {
// If it was just "deno info" print location of caches and exit
print_cache_info(
- &ps,
+ &factory,
info_flags.json,
- ps.options.location_flag().as_ref(),
+ cli_options.location_flag().as_ref(),
)?;
}
Ok(())
}
fn print_cache_info(
- state: &ProcState,
+ factory: &CliFactory,
json: bool,
location: Option<&deno_core::url::Url>,
) -> Result<(), AnyError> {
- let deno_dir = &state.dir.root_path_for_display();
- let modules_cache = &state.file_fetcher.get_http_cache_location();
- let npm_cache = &state.npm_cache.as_readonly().get_cache_location();
- let typescript_cache = &state.dir.gen_cache.location;
- let registry_cache = &state.dir.registries_folder_path();
- let mut origin_dir = state.dir.origin_data_folder_path();
+ let dir = factory.deno_dir()?;
+ let modules_cache = factory.file_fetcher()?.get_http_cache_location();
+ let npm_cache = factory.npm_cache()?.as_readonly().get_cache_location();
+ let typescript_cache = &dir.gen_cache.location;
+ let registry_cache = dir.registries_folder_path();
+ let mut origin_dir = dir.origin_data_folder_path();
+ let deno_dir = dir.root_path_for_display().to_string();
if let Some(location) = &location {
origin_dir =
@@ -88,7 +93,7 @@ fn print_cache_info(
if json {
let mut output = json!({
- "denoDir": deno_dir.to_string(),
+ "denoDir": deno_dir,
"modulesCache": modules_cache,
"npmCache": npm_cache,
"typescriptCache": typescript_cache,
@@ -141,7 +146,7 @@ fn print_cache_info(
fn add_npm_packages_to_json(
json: &mut serde_json::Value,
- npm_resolver: &NpmPackageResolver,
+ npm_resolver: &CliNpmResolver,
) {
// ideally deno_graph could handle this, but for now we just modify the json here
let snapshot = npm_resolver.snapshot();
@@ -166,10 +171,8 @@ fn add_npm_packages_to_json(
});
if let Some(pkg) = maybe_package {
if let Some(module) = module.as_object_mut() {
- module.insert(
- "npmPackage".to_string(),
- pkg.pkg_id.as_serialized().into(),
- );
+ module
+ .insert("npmPackage".to_string(), pkg.id.as_serialized().into());
}
}
} else {
@@ -202,7 +205,7 @@ fn add_npm_packages_to_json(
{
dep.insert(
"npmPackage".to_string(),
- pkg.pkg_id.as_serialized().into(),
+ pkg.id.as_serialized().into(),
);
}
}
@@ -213,16 +216,14 @@ fn add_npm_packages_to_json(
}
}
- let mut sorted_packages = snapshot.all_packages();
- sorted_packages.sort_by(|a, b| a.pkg_id.cmp(&b.pkg_id));
+ let mut sorted_packages =
+ snapshot.all_packages_for_every_system().collect::>();
+ sorted_packages.sort_by(|a, b| a.id.cmp(&b.id));
let mut json_packages = serde_json::Map::with_capacity(sorted_packages.len());
for pkg in sorted_packages {
let mut kv = serde_json::Map::new();
- kv.insert("name".to_string(), pkg.pkg_id.nv.name.to_string().into());
- kv.insert(
- "version".to_string(),
- pkg.pkg_id.nv.version.to_string().into(),
- );
+ kv.insert("name".to_string(), pkg.id.nv.name.to_string().into());
+ kv.insert("version".to_string(), pkg.id.nv.version.to_string().into());
let mut deps = pkg.dependencies.values().collect::>();
deps.sort();
let deps = deps
@@ -231,7 +232,7 @@ fn add_npm_packages_to_json(
.collect::>();
kv.insert("dependencies".to_string(), deps.into());
- json_packages.insert(pkg.pkg_id.as_serialized(), kv.into());
+ json_packages.insert(pkg.id.as_serialized(), kv.into());
}
json.insert("npmPackages".to_string(), json_packages.into());
@@ -318,7 +319,7 @@ struct NpmInfo {
impl NpmInfo {
pub fn build<'a>(
graph: &'a ModuleGraph,
- npm_resolver: &'a NpmPackageResolver,
+ npm_resolver: &'a CliNpmResolver,
npm_snapshot: &'a NpmResolutionSnapshot,
) -> Self {
let mut info = NpmInfo::default();
@@ -330,8 +331,8 @@ impl NpmInfo {
if let Module::Npm(module) = module {
let nv = &module.nv_reference.nv;
if let Ok(package) = npm_snapshot.resolve_package_from_deno_module(nv) {
- info.resolved_ids.insert(nv.clone(), package.pkg_id.clone());
- if !info.packages.contains_key(&package.pkg_id) {
+ info.resolved_ids.insert(nv.clone(), package.id.clone());
+ if !info.packages.contains_key(&package.id) {
info.fill_package_info(package, npm_resolver, npm_snapshot);
}
}
@@ -344,14 +345,12 @@ impl NpmInfo {
fn fill_package_info<'a>(
&mut self,
package: &NpmResolutionPackage,
- npm_resolver: &'a NpmPackageResolver,
+ npm_resolver: &'a CliNpmResolver,
npm_snapshot: &'a NpmResolutionSnapshot,
) {
- self
- .packages
- .insert(package.pkg_id.clone(), package.clone());
- if let Ok(size) = npm_resolver.package_size(&package.pkg_id) {
- self.package_sizes.insert(package.pkg_id.clone(), size);
+ self.packages.insert(package.id.clone(), package.clone());
+ if let Ok(size) = npm_resolver.package_size(&package.id) {
+ self.package_sizes.insert(package.id.clone(), size);
}
for id in package.dependencies.values() {
if !self.packages.contains_key(id) {
@@ -380,7 +379,7 @@ struct GraphDisplayContext<'a> {
impl<'a> GraphDisplayContext<'a> {
pub fn write(
graph: &'a ModuleGraph,
- npm_resolver: &'a NpmPackageResolver,
+ npm_resolver: &'a CliNpmResolver,
writer: &mut TWrite,
) -> fmt::Result {
let npm_snapshot = npm_resolver.snapshot();
@@ -531,7 +530,7 @@ impl<'a> GraphDisplayContext<'a> {
None => Specifier(module.specifier().clone()),
};
let was_seen = !self.seen.insert(match &package_or_specifier {
- Package(package) => package.pkg_id.as_serialized(),
+ Package(package) => package.id.as_serialized(),
Specifier(specifier) => specifier.to_string(),
});
let header_text = if was_seen {
@@ -549,7 +548,7 @@ impl<'a> GraphDisplayContext<'a> {
};
let maybe_size = match &package_or_specifier {
Package(package) => {
- self.npm_info.package_sizes.get(&package.pkg_id).copied()
+ self.npm_info.package_sizes.get(&package.id).copied()
}
Specifier(_) => match module {
Module::Esm(module) => Some(module.size() as u64),
@@ -603,7 +602,7 @@ impl<'a> GraphDisplayContext<'a> {
));
if let Some(package) = self.npm_info.packages.get(dep_id) {
if !package.dependencies.is_empty() {
- let was_seen = !self.seen.insert(package.pkg_id.as_serialized());
+ let was_seen = !self.seen.insert(package.id.as_serialized());
if was_seen {
child.text = format!("{} {}", child.text, colors::gray("*"));
} else {
diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs
index 461bb1a50a..07606d5f8d 100644
--- a/cli/tools/installer.rs
+++ b/cli/tools/installer.rs
@@ -6,8 +6,8 @@ use crate::args::ConfigFlag;
use crate::args::Flags;
use crate::args::InstallFlags;
use crate::args::TypeCheckMode;
+use crate::factory::CliFactory;
use crate::http_util::HttpClient;
-use crate::proc_state::ProcState;
use crate::util::fs::canonicalize_path_maybe_not_exists;
use deno_core::anyhow::Context;
@@ -133,7 +133,7 @@ pub async fn infer_name_from_url(url: &Url) -> Option {
let mut url = url.clone();
if url.path() == "/" {
- let client = HttpClient::new(None, None).unwrap();
+ let client = HttpClient::new(None, None);
if let Ok(res) = client.get_redirected_response(url.clone()).await {
url = res.url().clone();
}
@@ -233,9 +233,10 @@ pub async fn install_command(
install_flags: InstallFlags,
) -> Result<(), AnyError> {
// ensure the module is cached
- ProcState::from_flags(flags.clone())
+ CliFactory::from_flags(flags.clone())
+ .await?
+ .module_load_preparer()
.await?
- .module_load_preparer
.load_and_type_check_files(&[install_flags.module_url.clone()])
.await?;
diff --git a/cli/tools/lint.rs b/cli/tools/lint.rs
index eae2f1032d..40c37ce773 100644
--- a/cli/tools/lint.rs
+++ b/cli/tools/lint.rs
@@ -11,8 +11,8 @@ use crate::args::FilesConfig;
use crate::args::LintOptions;
use crate::args::LintReporterKind;
use crate::args::LintRulesConfig;
-use crate::cache::Caches;
use crate::colors;
+use crate::factory::CliFactory;
use crate::tools::fmt::run_parallelized;
use crate::util::file_watcher;
use crate::util::file_watcher::ResolutionResult;
@@ -98,11 +98,12 @@ pub async fn lint(
};
let has_error = Arc::new(AtomicBool::new(false));
- let deno_dir = cli_options.resolve_deno_dir()?;
- let caches = Caches::default();
+ let factory = CliFactory::from_cli_options(Arc::new(cli_options));
+ let cli_options = factory.cli_options();
+ let caches = factory.caches()?;
let operation = |paths: Vec| async {
let incremental_cache = Arc::new(IncrementalCache::new(
- caches.lint_incremental_cache_db(&deno_dir),
+ caches.lint_incremental_cache_db(),
// use a hash of the rule names in order to bust the cache
&{
// ensure this is stable by sorting it
diff --git a/cli/tools/mod.rs b/cli/tools/mod.rs
index cf29435a7c..c4a8306ab9 100644
--- a/cli/tools/mod.rs
+++ b/cli/tools/mod.rs
@@ -3,6 +3,7 @@
pub mod bench;
pub mod bundle;
pub mod check;
+pub mod compile;
pub mod coverage;
pub mod doc;
pub mod fmt;
@@ -12,7 +13,6 @@ pub mod installer;
pub mod lint;
pub mod repl;
pub mod run;
-pub mod standalone;
pub mod task;
pub mod test;
pub mod upgrade;
diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs
index a6cc716373..34acb8a4e3 100644
--- a/cli/tools/repl/mod.rs
+++ b/cli/tools/repl/mod.rs
@@ -1,11 +1,14 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+use crate::args::CliOptions;
use crate::args::Flags;
use crate::args::ReplFlags;
use crate::colors;
-use crate::proc_state::ProcState;
-use crate::worker::create_main_worker;
+use crate::factory::CliFactory;
+use crate::file_fetcher::FileFetcher;
use deno_core::error::AnyError;
+use deno_core::futures::StreamExt;
+use deno_core::task::spawn_blocking;
use deno_runtime::permissions::Permissions;
use deno_runtime::permissions::PermissionsContainer;
use rustyline::error::ReadlineError;
@@ -29,8 +32,11 @@ async fn read_line_and_poll(
message_handler: &mut RustylineSyncMessageHandler,
editor: ReplEditor,
) -> Result {
- let mut line_fut = tokio::task::spawn_blocking(move || editor.readline());
+ #![allow(clippy::await_holding_refcell_ref)]
+ let mut line_fut = spawn_blocking(move || editor.readline());
let mut poll_worker = true;
+ let notifications_rc = repl_session.notifications.clone();
+ let mut notifications = notifications_rc.borrow_mut();
loop {
tokio::select! {
@@ -56,7 +62,20 @@ async fn read_line_and_poll(
}
poll_worker = true;
- },
+ }
+ message = notifications.next() => {
+ if let Some(message) = message {
+ let method = message.get("method").unwrap().as_str().unwrap();
+ if method == "Runtime.exceptionThrown" {
+ let params = message.get("params").unwrap().as_object().unwrap();
+ let exception_details = params.get("exceptionDetails").unwrap().as_object().unwrap();
+ let text = exception_details.get("text").unwrap().as_str().unwrap();
+ let exception = exception_details.get("exception").unwrap().as_object().unwrap();
+ let description = exception.get("description").and_then(|d| d.as_str()).unwrap_or("undefined");
+ println!("{text} {description}");
+ }
+ }
+ }
_ = repl_session.run_event_loop(), if poll_worker => {
poll_worker = false;
}
@@ -65,14 +84,14 @@ async fn read_line_and_poll(
}
async fn read_eval_file(
- ps: &ProcState,
+ cli_options: &CliOptions,
+ file_fetcher: &FileFetcher,
eval_file: &str,
) -> Result {
let specifier =
- deno_core::resolve_url_or_path(eval_file, ps.options.initial_cwd())?;
+ deno_core::resolve_url_or_path(eval_file, cli_options.initial_cwd())?;
- let file = ps
- .file_fetcher
+ let file = file_fetcher
.fetch(&specifier, PermissionsContainer::allow_all())
.await?;
@@ -80,19 +99,29 @@ async fn read_eval_file(
}
pub async fn run(flags: Flags, repl_flags: ReplFlags) -> Result {
- let ps = ProcState::from_flags(flags).await?;
- let main_module = ps.options.resolve_main_module()?;
- let mut worker = create_main_worker(
- &ps,
- main_module,
- PermissionsContainer::new(Permissions::from_options(
- &ps.options.permissions_options(),
- )?),
- )
- .await?;
+ let factory = CliFactory::from_flags(flags).await?;
+ let cli_options = factory.cli_options();
+ let main_module = cli_options.resolve_main_module()?;
+ let permissions = PermissionsContainer::new(Permissions::from_options(
+ &cli_options.permissions_options(),
+ )?);
+ let npm_resolver = factory.npm_resolver().await?.clone();
+ let resolver = factory.resolver().await?.clone();
+ let file_fetcher = factory.file_fetcher()?;
+ let worker_factory = factory.create_cli_main_worker_factory().await?;
+ let history_file_path = factory
+ .deno_dir()
+ .ok()
+ .and_then(|dir| dir.repl_history_file_path());
+
+ let mut worker = worker_factory
+ .create_main_worker(main_module, permissions)
+ .await?;
worker.setup_repl().await?;
let worker = worker.into_main_worker();
- let mut repl_session = ReplSession::initialize(ps.clone(), worker).await?;
+ let mut repl_session =
+ ReplSession::initialize(cli_options, npm_resolver, resolver, worker)
+ .await?;
let mut rustyline_channel = rustyline_channel();
let helper = EditorHelper {
@@ -100,12 +129,11 @@ pub async fn run(flags: Flags, repl_flags: ReplFlags) -> Result {
sync_sender: rustyline_channel.0,
};
- let history_file_path = ps.dir.repl_history_file_path();
let editor = ReplEditor::new(helper, history_file_path)?;
if let Some(eval_files) = repl_flags.eval_files {
for eval_file in eval_files {
- match read_eval_file(&ps, &eval_file).await {
+ match read_eval_file(cli_options, file_fetcher, &eval_file).await {
Ok(eval_source) => {
let output = repl_session
.evaluate_line_and_get_output(&eval_source)
@@ -132,7 +160,7 @@ pub async fn run(flags: Flags, repl_flags: ReplFlags) -> Result {
// Doing this manually, instead of using `log::info!` because these messages
// are supposed to go to stdout, not stderr.
- if !ps.options.is_quiet() {
+ if !cli_options.is_quiet() {
println!("Deno {}", crate::version::deno());
println!("exit using ctrl+d, ctrl+c, or close()");
if repl_flags.is_default_command {
diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs
index 7fc251362e..4a30c93c44 100644
--- a/cli/tools/repl/session.rs
+++ b/cli/tools/repl/session.rs
@@ -1,8 +1,14 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+use std::cell::RefCell;
+use std::rc::Rc;
+use std::sync::Arc;
+
+use crate::args::CliOptions;
use crate::colors;
use crate::lsp::ReplLanguageServer;
-use crate::ProcState;
+use crate::npm::CliNpmResolver;
+use crate::resolver::CliGraphResolver;
use deno_ast::swc::ast as swc_ast;
use deno_ast::swc::visit::noop_visit_type;
@@ -117,22 +123,23 @@ struct TsEvaluateResponse {
}
pub struct ReplSession {
- proc_state: ProcState,
+ has_node_modules_dir: bool,
+ npm_resolver: Arc,
+ resolver: Arc,
pub worker: MainWorker,
session: LocalInspectorSession,
pub context_id: u64,
pub language_server: ReplLanguageServer,
+ pub notifications: Rc>>,
has_initialized_node_runtime: bool,
referrer: ModuleSpecifier,
- // FIXME(bartlomieju): this field should be used to listen
- // for "exceptionThrown" notifications
- #[allow(dead_code)]
- notification_rx: UnboundedReceiver,
}
impl ReplSession {
pub async fn initialize(
- proc_state: ProcState,
+ cli_options: &CliOptions,
+ npm_resolver: Arc,
+ resolver: Arc,
mut worker: MainWorker,
) -> Result {
let language_server = ReplLanguageServer::new_initialized().await?;
@@ -171,21 +178,21 @@ impl ReplSession {
}
assert_ne!(context_id, 0);
- let referrer = deno_core::resolve_path(
- "./$deno$repl.ts",
- proc_state.options.initial_cwd(),
- )
- .unwrap();
+ let referrer =
+ deno_core::resolve_path("./$deno$repl.ts", cli_options.initial_cwd())
+ .unwrap();
let mut repl_session = ReplSession {
- proc_state,
+ has_node_modules_dir: cli_options.has_node_modules_dir(),
+ npm_resolver,
+ resolver,
worker,
session,
context_id,
language_server,
has_initialized_node_runtime: false,
referrer,
- notification_rx,
+ notifications: Rc::new(RefCell::new(notification_rx)),
};
// inject prelude
@@ -251,9 +258,15 @@ impl ReplSession {
Ok(if let Some(exception_details) = exception_details {
session.set_last_thrown_error(&result).await?;
let description = match exception_details.exception {
- Some(exception) => exception
- .description
- .unwrap_or_else(|| "Unknown exception".to_string()),
+ Some(exception) => {
+ if let Some(description) = exception.description {
+ description
+ } else if let Some(value) = exception.value {
+ value.to_string()
+ } else {
+ "undefined".to_string()
+ }
+ }
None => "Unknown exception".to_string(),
};
EvaluationOutput::Error(format!(
@@ -487,7 +500,6 @@ impl ReplSession {
.iter()
.flat_map(|i| {
self
- .proc_state
.resolver
.resolve(i, &self.referrer)
.ok()
@@ -506,22 +518,17 @@ impl ReplSession {
if !self.has_initialized_node_runtime {
deno_node::initialize_runtime(
&mut self.worker.js_runtime,
- self.proc_state.options.has_node_modules_dir(),
+ self.has_node_modules_dir,
None,
)?;
self.has_initialized_node_runtime = true;
}
- self
- .proc_state
- .npm_resolver
- .add_package_reqs(npm_imports)
- .await?;
+ self.npm_resolver.add_package_reqs(&npm_imports).await?;
// prevent messages in the repl about @types/node not being cached
if has_node_specifier {
self
- .proc_state
.npm_resolver
.inject_synthetic_types_node_package()
.await?;
diff --git a/cli/tools/run.rs b/cli/tools/run.rs
index 7f4b5c8f74..4805ea704d 100644
--- a/cli/tools/run.rs
+++ b/cli/tools/run.rs
@@ -10,10 +10,10 @@ use deno_runtime::permissions::PermissionsContainer;
use crate::args::EvalFlags;
use crate::args::Flags;
+use crate::factory::CliFactory;
+use crate::factory::CliFactoryBuilder;
use crate::file_fetcher::File;
-use crate::proc_state::ProcState;
use crate::util;
-use crate::worker::create_main_worker;
pub async fn run_script(flags: Flags) -> Result {
if !flags.has_permission() && flags.has_permission_in_argv() {
@@ -32,41 +32,47 @@ To grant permissions, set them before the script argument. For example:
}
// TODO(bartlomieju): actually I think it will also fail if there's an import
- // map specified and bare specifier is used on the command line - this should
- // probably call `ProcState::resolve` instead
- let ps = ProcState::from_flags(flags).await?;
+ // map specified and bare specifier is used on the command line
+ let factory = CliFactory::from_flags(flags).await?;
+ let deno_dir = factory.deno_dir()?;
+ let http_client = factory.http_client();
+ let cli_options = factory.cli_options();
// Run a background task that checks for available upgrades. If an earlier
// run of this background task found a new version of Deno.
super::upgrade::check_for_upgrades(
- ps.http_client.clone(),
- ps.dir.upgrade_check_file_path(),
+ http_client.clone(),
+ deno_dir.upgrade_check_file_path(),
);
- let main_module = ps.options.resolve_main_module()?;
+ let main_module = cli_options.resolve_main_module()?;
+
+ maybe_npm_install(&factory).await?;
let permissions = PermissionsContainer::new(Permissions::from_options(
- &ps.options.permissions_options(),
+ &cli_options.permissions_options(),
)?);
- let mut worker = create_main_worker(&ps, main_module, permissions).await?;
+ let worker_factory = factory.create_cli_main_worker_factory().await?;
+ let mut worker = worker_factory
+ .create_main_worker(main_module, permissions)
+ .await?;
let exit_code = worker.run().await?;
Ok(exit_code)
}
pub async fn run_from_stdin(flags: Flags) -> Result {
- let ps = ProcState::from_flags(flags).await?;
- let main_module = ps.options.resolve_main_module()?;
+ let factory = CliFactory::from_flags(flags).await?;
+ let cli_options = factory.cli_options();
+ let main_module = cli_options.resolve_main_module()?;
- let mut worker = create_main_worker(
- &ps,
- main_module.clone(),
- PermissionsContainer::new(Permissions::from_options(
- &ps.options.permissions_options(),
- )?),
- )
- .await?;
+ maybe_npm_install(&factory).await?;
+ let file_fetcher = factory.file_fetcher()?;
+ let worker_factory = factory.create_cli_main_worker_factory().await?;
+ let permissions = PermissionsContainer::new(Permissions::from_options(
+ &cli_options.permissions_options(),
+ )?);
let mut source = Vec::new();
std::io::stdin().read_to_end(&mut source)?;
// Create a dummy source file.
@@ -75,13 +81,16 @@ pub async fn run_from_stdin(flags: Flags) -> Result {
maybe_types: None,
media_type: MediaType::TypeScript,
source: String::from_utf8(source)?.into(),
- specifier: main_module,
+ specifier: main_module.clone(),
maybe_headers: None,
};
// Save our fake file into file fetcher cache
// to allow module access by TS compiler
- ps.file_fetcher.insert_cached(source_file);
+ file_fetcher.insert_cached(source_file);
+ let mut worker = worker_factory
+ .create_main_worker(main_module, permissions)
+ .await?;
let exit_code = worker.run().await?;
Ok(exit_code)
}
@@ -90,19 +99,30 @@ pub async fn run_from_stdin(flags: Flags) -> Result {
// code properly.
async fn run_with_watch(flags: Flags) -> Result {
let (sender, receiver) = tokio::sync::mpsc::unbounded_channel();
- let mut ps =
- ProcState::from_flags_for_file_watcher(flags, sender.clone()).await?;
- let clear_screen = !ps.options.no_clear_screen();
- let main_module = ps.options.resolve_main_module()?;
+ let factory = CliFactoryBuilder::new()
+ .with_watcher(sender.clone())
+ .build_from_flags(flags)
+ .await?;
+ let file_watcher = factory.file_watcher()?;
+ let cli_options = factory.cli_options();
+ let clear_screen = !cli_options.no_clear_screen();
+ let main_module = cli_options.resolve_main_module()?;
+ maybe_npm_install(&factory).await?;
+
+ let create_cli_main_worker_factory =
+ factory.create_cli_main_worker_factory_func().await?;
let operation = |main_module: ModuleSpecifier| {
- ps.reset_for_file_watcher();
- let ps = ps.clone();
+ file_watcher.reset();
+ let permissions = PermissionsContainer::new(Permissions::from_options(
+ &cli_options.permissions_options(),
+ )?);
+ let create_cli_main_worker_factory = create_cli_main_worker_factory.clone();
+
Ok(async move {
- let permissions = PermissionsContainer::new(Permissions::from_options(
- &ps.options.permissions_options(),
- )?);
- let worker = create_main_worker(&ps, main_module, permissions).await?;
+ let worker = create_cli_main_worker_factory()
+ .create_main_worker(main_module, permissions)
+ .await?;
worker.run_for_watcher().await?;
Ok(())
@@ -127,13 +147,13 @@ pub async fn eval_command(
flags: Flags,
eval_flags: EvalFlags,
) -> Result {
- let ps = ProcState::from_flags(flags).await?;
- let main_module = ps.options.resolve_main_module()?;
- let permissions = PermissionsContainer::new(Permissions::from_options(
- &ps.options.permissions_options(),
- )?);
- let mut worker =
- create_main_worker(&ps, main_module.clone(), permissions).await?;
+ let factory = CliFactory::from_flags(flags).await?;
+ let cli_options = factory.cli_options();
+ let file_fetcher = factory.file_fetcher()?;
+ let main_module = cli_options.resolve_main_module()?;
+
+ maybe_npm_install(&factory).await?;
+
// Create a dummy source file.
let source_code = if eval_flags.print {
format!("console.log({})", eval_flags.code)
@@ -147,13 +167,34 @@ pub async fn eval_command(
maybe_types: None,
media_type: MediaType::Unknown,
source: String::from_utf8(source_code)?.into(),
- specifier: main_module,
+ specifier: main_module.clone(),
maybe_headers: None,
};
// Save our fake file into file fetcher cache
// to allow module access by TS compiler.
- ps.file_fetcher.insert_cached(file);
+ file_fetcher.insert_cached(file);
+
+ let permissions = PermissionsContainer::new(Permissions::from_options(
+ &cli_options.permissions_options(),
+ )?);
+ let worker_factory = factory.create_cli_main_worker_factory().await?;
+ let mut worker = worker_factory
+ .create_main_worker(main_module, permissions)
+ .await?;
let exit_code = worker.run().await?;
Ok(exit_code)
}
+
+async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> {
+ // ensure an "npm install" is done if the user has explicitly
+ // opted into using a node_modules directory
+ if factory.cli_options().node_modules_dir_enablement() == Some(true) {
+ factory
+ .package_json_deps_installer()
+ .await?
+ .ensure_top_level_install()
+ .await?;
+ }
+ Ok(())
+}
diff --git a/cli/tools/standalone.rs b/cli/tools/standalone.rs
deleted file mode 100644
index fab3266ea4..0000000000
--- a/cli/tools/standalone.rs
+++ /dev/null
@@ -1,418 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-use crate::args::CaData;
-use crate::args::CompileFlags;
-use crate::args::Flags;
-use crate::cache::DenoDir;
-use crate::graph_util::error_for_any_npm_specifier;
-use crate::http_util::HttpClient;
-use crate::standalone::Metadata;
-use crate::standalone::MAGIC_TRAILER;
-use crate::util::path::path_has_trailing_slash;
-use crate::util::progress_bar::ProgressBar;
-use crate::util::progress_bar::ProgressBarStyle;
-use crate::ProcState;
-use deno_core::anyhow::bail;
-use deno_core::anyhow::Context;
-use deno_core::error::generic_error;
-use deno_core::error::AnyError;
-use deno_core::resolve_url_or_path;
-use deno_core::serde_json;
-use deno_graph::ModuleSpecifier;
-use deno_runtime::colors;
-use std::env;
-use std::fs;
-use std::fs::File;
-use std::io::Read;
-use std::io::Seek;
-use std::io::SeekFrom;
-use std::io::Write;
-use std::path::Path;
-use std::path::PathBuf;
-use std::sync::Arc;
-
-use super::installer::infer_name_from_url;
-
-pub async fn compile(
- flags: Flags,
- compile_flags: CompileFlags,
-) -> Result<(), AnyError> {
- let ps = ProcState::from_flags(flags).await?;
- let module_specifier = ps.options.resolve_main_module()?;
- let module_roots = {
- let mut vec = Vec::with_capacity(compile_flags.include.len() + 1);
- vec.push(module_specifier.clone());
- for side_module in &compile_flags.include {
- vec.push(resolve_url_or_path(side_module, ps.options.initial_cwd())?);
- }
- vec
- };
- let deno_dir = &ps.dir;
-
- let output_path = resolve_compile_executable_output_path(
- &compile_flags,
- ps.options.initial_cwd(),
- )
- .await?;
-
- let graph = Arc::try_unwrap(
- ps.module_graph_builder
- .create_graph_and_maybe_check(module_roots)
- .await?,
- )
- .unwrap();
-
- // at the moment, we don't support npm specifiers in deno_compile, so show an error
- error_for_any_npm_specifier(&graph)?;
-
- let parser = ps.parsed_source_cache.as_capturing_parser();
- let eszip = eszip::EszipV2::from_graph(graph, &parser, Default::default())?;
-
- log::info!(
- "{} {}",
- colors::green("Compile"),
- module_specifier.to_string()
- );
-
- // Select base binary based on target
- let original_binary =
- get_base_binary(&ps.http_client, deno_dir, compile_flags.target.clone())
- .await?;
-
- let final_bin = create_standalone_binary(
- original_binary,
- eszip,
- module_specifier,
- &compile_flags,
- ps,
- )
- .await?;
-
- log::info!("{} {}", colors::green("Emit"), output_path.display());
-
- write_standalone_binary(output_path, final_bin).await?;
- Ok(())
-}
-
-async fn get_base_binary(
- client: &HttpClient,
- deno_dir: &DenoDir,
- target: Option,
-) -> Result, AnyError> {
- if target.is_none() {
- let path = std::env::current_exe()?;
- return Ok(tokio::fs::read(path).await?);
- }
-
- let target = target.unwrap_or_else(|| env!("TARGET").to_string());
- let binary_name = format!("deno-{target}.zip");
-
- let binary_path_suffix = if crate::version::is_canary() {
- format!("canary/{}/{}", crate::version::GIT_COMMIT_HASH, binary_name)
- } else {
- format!("release/v{}/{}", env!("CARGO_PKG_VERSION"), binary_name)
- };
-
- let download_directory = deno_dir.dl_folder_path();
- let binary_path = download_directory.join(&binary_path_suffix);
-
- if !binary_path.exists() {
- download_base_binary(client, &download_directory, &binary_path_suffix)
- .await?;
- }
-
- let archive_data = tokio::fs::read(binary_path).await?;
- let temp_dir = tempfile::TempDir::new()?;
- let base_binary_path = crate::tools::upgrade::unpack_into_dir(
- archive_data,
- target.contains("windows"),
- &temp_dir,
- )?;
- let base_binary = tokio::fs::read(base_binary_path).await?;
- drop(temp_dir); // delete the temp dir
- Ok(base_binary)
-}
-
-async fn download_base_binary(
- client: &HttpClient,
- output_directory: &Path,
- binary_path_suffix: &str,
-) -> Result<(), AnyError> {
- let download_url = format!("https://dl.deno.land/{binary_path_suffix}");
- let maybe_bytes = {
- let progress_bars = ProgressBar::new(ProgressBarStyle::DownloadBars);
- let progress = progress_bars.update(&download_url);
-
- client
- .download_with_progress(download_url, &progress)
- .await?
- };
- let bytes = match maybe_bytes {
- Some(bytes) => bytes,
- None => {
- log::info!("Download could not be found, aborting");
- std::process::exit(1)
- }
- };
-
- std::fs::create_dir_all(output_directory)?;
- let output_path = output_directory.join(binary_path_suffix);
- std::fs::create_dir_all(output_path.parent().unwrap())?;
- tokio::fs::write(output_path, bytes).await?;
- Ok(())
-}
-
-/// This functions creates a standalone deno binary by appending a bundle
-/// and magic trailer to the currently executing binary.
-async fn create_standalone_binary(
- mut original_bin: Vec,
- eszip: eszip::EszipV2,
- entrypoint: ModuleSpecifier,
- compile_flags: &CompileFlags,
- ps: ProcState,
-) -> Result, AnyError> {
- let mut eszip_archive = eszip.into_bytes();
-
- let ca_data = match ps.options.ca_data() {
- Some(CaData::File(ca_file)) => {
- Some(fs::read(ca_file).with_context(|| format!("Reading: {ca_file}"))?)
- }
- Some(CaData::Bytes(bytes)) => Some(bytes.clone()),
- None => None,
- };
- let maybe_import_map = ps
- .options
- .resolve_import_map(&ps.file_fetcher)
- .await?
- .map(|import_map| (import_map.base_url().clone(), import_map.to_json()));
- let metadata = Metadata {
- argv: compile_flags.args.clone(),
- unstable: ps.options.unstable(),
- seed: ps.options.seed(),
- location: ps.options.location_flag().clone(),
- permissions: ps.options.permissions_options(),
- v8_flags: ps.options.v8_flags().clone(),
- unsafely_ignore_certificate_errors: ps
- .options
- .unsafely_ignore_certificate_errors()
- .clone(),
- log_level: ps.options.log_level(),
- ca_stores: ps.options.ca_stores().clone(),
- ca_data,
- entrypoint,
- maybe_import_map,
- };
- let mut metadata = serde_json::to_string(&metadata)?.as_bytes().to_vec();
-
- let eszip_pos = original_bin.len();
- let metadata_pos = eszip_pos + eszip_archive.len();
- let mut trailer = MAGIC_TRAILER.to_vec();
- trailer.write_all(&eszip_pos.to_be_bytes())?;
- trailer.write_all(&metadata_pos.to_be_bytes())?;
-
- let mut final_bin = Vec::with_capacity(
- original_bin.len() + eszip_archive.len() + trailer.len(),
- );
- final_bin.append(&mut original_bin);
- final_bin.append(&mut eszip_archive);
- final_bin.append(&mut metadata);
- final_bin.append(&mut trailer);
-
- Ok(final_bin)
-}
-
-/// This function writes out a final binary to specified path. If output path
-/// is not already standalone binary it will return error instead.
-async fn write_standalone_binary(
- output_path: PathBuf,
- final_bin: Vec,
-) -> Result<(), AnyError> {
- if output_path.exists() {
- // If the output is a directory, throw error
- if output_path.is_dir() {
- bail!(
- concat!(
- "Could not compile to file '{}' because a directory exists with ",
- "the same name. You can use the `--output ` flag to ",
- "provide an alternative name."
- ),
- output_path.display()
- );
- }
-
- // Make sure we don't overwrite any file not created by Deno compiler.
- // Check for magic trailer in last 24 bytes.
- let mut has_trailer = false;
- let mut output_file = File::open(&output_path)?;
- // This seek may fail because the file is too small to possibly be
- // `deno compile` output.
- if output_file.seek(SeekFrom::End(-24)).is_ok() {
- let mut trailer = [0; 24];
- output_file.read_exact(&mut trailer)?;
- let (magic_trailer, _) = trailer.split_at(8);
- has_trailer = magic_trailer == MAGIC_TRAILER;
- }
- if !has_trailer {
- bail!(
- concat!(
- "Could not compile to file '{}' because the file already exists ",
- "and cannot be overwritten. Please delete the existing file or ",
- "use the `--output ` flag to ",
- "provide an alternative name.",
- ),
- output_base.display(),
- );
- }
- tokio::fs::create_dir_all(output_base).await?;
- }
-
- tokio::fs::write(&output_path, final_bin).await?;
- #[cfg(unix)]
- {
- use std::os::unix::fs::PermissionsExt;
- let perms = std::fs::Permissions::from_mode(0o777);
- tokio::fs::set_permissions(output_path, perms).await?;
- }
-
- Ok(())
-}
-
-async fn resolve_compile_executable_output_path(
- compile_flags: &CompileFlags,
- current_dir: &Path,
-) -> Result {
- let module_specifier =
- resolve_url_or_path(&compile_flags.source_file, current_dir)?;
-
- let mut output = compile_flags.output.clone();
-
- if let Some(out) = output.as_ref() {
- if path_has_trailing_slash(out) {
- if let Some(infer_file_name) = infer_name_from_url(&module_specifier)
- .await
- .map(PathBuf::from)
- {
- output = Some(out.join(infer_file_name));
- }
- } else {
- output = Some(out.to_path_buf());
- }
- }
-
- if output.is_none() {
- output = infer_name_from_url(&module_specifier)
- .await
- .map(PathBuf::from)
- }
-
- output.ok_or_else(|| generic_error(
- "An executable name was not provided. One could not be inferred from the URL. Aborting.",
- )).map(|output| {
- get_os_specific_filepath(output, &compile_flags.target)
- })
-}
-
-fn get_os_specific_filepath(
- output: PathBuf,
- target: &Option,
-) -> PathBuf {
- let is_windows = match target {
- Some(target) => target.contains("windows"),
- None => cfg!(windows),
- };
- if is_windows && output.extension().unwrap_or_default() != "exe" {
- if let Some(ext) = output.extension() {
- // keep version in my-exe-0.1.0 -> my-exe-0.1.0.exe
- output.with_extension(format!("{}.exe", ext.to_string_lossy()))
- } else {
- output.with_extension("exe")
- }
- } else {
- output
- }
-}
-
-#[cfg(test)]
-mod test {
- pub use super::*;
-
- #[tokio::test]
- async fn resolve_compile_executable_output_path_target_linux() {
- let path = resolve_compile_executable_output_path(
- &CompileFlags {
- source_file: "mod.ts".to_string(),
- output: Some(PathBuf::from("./file")),
- args: Vec::new(),
- target: Some("x86_64-unknown-linux-gnu".to_string()),
- include: vec![],
- },
- &std::env::current_dir().unwrap(),
- )
- .await
- .unwrap();
-
- // no extension, no matter what the operating system is
- // because the target was specified as linux
- // https://github.com/denoland/deno/issues/9667
- assert_eq!(path.file_name().unwrap(), "file");
- }
-
- #[tokio::test]
- async fn resolve_compile_executable_output_path_target_windows() {
- let path = resolve_compile_executable_output_path(
- &CompileFlags {
- source_file: "mod.ts".to_string(),
- output: Some(PathBuf::from("./file")),
- args: Vec::new(),
- target: Some("x86_64-pc-windows-msvc".to_string()),
- include: vec![],
- },
- &std::env::current_dir().unwrap(),
- )
- .await
- .unwrap();
- assert_eq!(path.file_name().unwrap(), "file.exe");
- }
-
- #[test]
- fn test_os_specific_file_path() {
- fn run_test(path: &str, target: Option<&str>, expected: &str) {
- assert_eq!(
- get_os_specific_filepath(
- PathBuf::from(path),
- &target.map(|s| s.to_string())
- ),
- PathBuf::from(expected)
- );
- }
-
- if cfg!(windows) {
- run_test("C:\\my-exe", None, "C:\\my-exe.exe");
- run_test("C:\\my-exe.exe", None, "C:\\my-exe.exe");
- run_test("C:\\my-exe-0.1.2", None, "C:\\my-exe-0.1.2.exe");
- } else {
- run_test("my-exe", Some("linux"), "my-exe");
- run_test("my-exe-0.1.2", Some("linux"), "my-exe-0.1.2");
- }
-
- run_test("C:\\my-exe", Some("windows"), "C:\\my-exe.exe");
- run_test("C:\\my-exe.exe", Some("windows"), "C:\\my-exe.exe");
- run_test("C:\\my-exe.0.1.2", Some("windows"), "C:\\my-exe.0.1.2.exe");
- run_test("my-exe-0.1.2", Some("linux"), "my-exe-0.1.2");
- }
-}
diff --git a/cli/tools/task.rs b/cli/tools/task.rs
index c64e2a77cd..7dd7e7bc4d 100644
--- a/cli/tools/task.rs
+++ b/cli/tools/task.rs
@@ -4,15 +4,15 @@ use crate::args::CliOptions;
use crate::args::Flags;
use crate::args::TaskFlags;
use crate::colors;
-use crate::node::CliNodeResolver;
-use crate::npm::NpmPackageResolver;
-use crate::proc_state::ProcState;
+use crate::factory::CliFactory;
+use crate::npm::CliNpmResolver;
use crate::util::fs::canonicalize_path;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::futures;
use deno_core::futures::future::LocalBoxFuture;
+use deno_runtime::deno_node::NodeResolver;
use deno_semver::npm::NpmPackageNv;
use deno_task_shell::ExecuteResult;
use deno_task_shell::ShellCommand;
@@ -21,14 +21,16 @@ use indexmap::IndexMap;
use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;
+use tokio::task::LocalSet;
pub async fn execute_script(
flags: Flags,
task_flags: TaskFlags,
) -> Result {
- let ps = ProcState::from_flags(flags).await?;
- let tasks_config = ps.options.resolve_tasks_config()?;
- let maybe_package_json = ps.options.maybe_package_json();
+ let factory = CliFactory::from_flags(flags).await?;
+ let cli_options = factory.cli_options();
+ let tasks_config = cli_options.resolve_tasks_config()?;
+ let maybe_package_json = cli_options.maybe_package_json();
let package_json_scripts = maybe_package_json
.as_ref()
.and_then(|p| p.scripts.clone())
@@ -43,7 +45,7 @@ pub async fn execute_script(
};
if let Some(script) = tasks_config.get(task_name) {
- let config_file_url = ps.options.maybe_config_file_specifier().unwrap();
+ let config_file_url = cli_options.maybe_config_file_specifier().unwrap();
let config_file_path = if config_file_url.scheme() == "file" {
config_file_url.to_file_path().unwrap()
} else {
@@ -53,17 +55,24 @@ pub async fn execute_script(
Some(path) => canonicalize_path(&PathBuf::from(path))?,
None => config_file_path.parent().unwrap().to_owned(),
};
- let script = get_script_with_args(script, &ps.options);
+ let script = get_script_with_args(script, cli_options);
output_task(task_name, &script);
let seq_list = deno_task_shell::parser::parse(&script)
.with_context(|| format!("Error parsing script '{task_name}'."))?;
let env_vars = collect_env_vars();
- let exit_code =
- deno_task_shell::execute(seq_list, env_vars, &cwd, Default::default())
- .await;
+ let local = LocalSet::new();
+ let future =
+ deno_task_shell::execute(seq_list, env_vars, &cwd, Default::default());
+ let exit_code = local.run_until(future).await;
Ok(exit_code)
- } else if let Some(script) = package_json_scripts.get(task_name) {
- if let Some(package_deps) = ps.package_json_deps_installer.package_deps() {
+ } else if package_json_scripts.contains_key(task_name) {
+ let package_json_deps_provider = factory.package_json_deps_provider();
+ let package_json_deps_installer =
+ factory.package_json_deps_installer().await?;
+ let npm_resolver = factory.npm_resolver().await?;
+ let node_resolver = factory.node_resolver().await?;
+
+ if let Some(package_deps) = package_json_deps_provider.deps() {
for (key, value) in package_deps {
if let Err(err) = value {
log::info!(
@@ -75,13 +84,14 @@ pub async fn execute_script(
}
}
}
- ps.package_json_deps_installer
+
+ package_json_deps_installer
.ensure_top_level_install()
.await?;
- ps.npm_resolver.resolve_pending().await?;
+ npm_resolver.resolve_pending().await?;
log::info!(
- "{} Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in the upcoming release.",
+ "{} Currently only basic package.json `scripts` are supported. Programs like `rimraf` or `cross-env` will not work correctly. This will be fixed in an upcoming release.",
colors::yellow("Warning"),
);
@@ -95,16 +105,34 @@ pub async fn execute_script(
.unwrap()
.to_owned(),
};
- let script = get_script_with_args(script, &ps.options);
- output_task(task_name, &script);
- let seq_list = deno_task_shell::parser::parse(&script)
- .with_context(|| format!("Error parsing script '{task_name}'."))?;
- let npx_commands =
- resolve_npm_commands(&ps.npm_resolver, &ps.node_resolver)?;
- let env_vars = collect_env_vars();
- let exit_code =
- deno_task_shell::execute(seq_list, env_vars, &cwd, npx_commands).await;
- Ok(exit_code)
+
+ // At this point we already checked if the task name exists in package.json.
+ // We can therefore check for "pre" and "post" scripts too, since we're only
+ // dealing with package.json here and not deno.json
+ let task_names = vec![
+ format!("pre{}", task_name),
+ task_name.clone(),
+ format!("post{}", task_name),
+ ];
+ for task_name in task_names {
+ if let Some(script) = package_json_scripts.get(&task_name) {
+ let script = get_script_with_args(script, cli_options);
+ output_task(&task_name, &script);
+ let seq_list = deno_task_shell::parser::parse(&script)
+ .with_context(|| format!("Error parsing script '{task_name}'."))?;
+ let npx_commands = resolve_npm_commands(npm_resolver, node_resolver)?;
+ let env_vars = collect_env_vars();
+ let local = LocalSet::new();
+ let future =
+ deno_task_shell::execute(seq_list, env_vars, &cwd, npx_commands);
+ let exit_code = local.run_until(future).await;
+ if exit_code > 0 {
+ return Ok(exit_code);
+ }
+ }
+ }
+
+ Ok(0)
} else {
eprintln!("Task not found: {task_name}");
print_available_tasks(&tasks_config, &package_json_scripts);
@@ -234,8 +262,8 @@ impl ShellCommand for NpmPackageBinCommand {
}
fn resolve_npm_commands(
- npm_resolver: &NpmPackageResolver,
- node_resolver: &CliNodeResolver,
+ npm_resolver: &CliNpmResolver,
+ node_resolver: &NodeResolver,
) -> Result>, AnyError> {
let mut result = HashMap::new();
let snapshot = npm_resolver.snapshot();
diff --git a/cli/tools/test.rs b/cli/tools/test.rs
index 977073ab73..6f32d69e49 100644
--- a/cli/tools/test.rs
+++ b/cli/tools/test.rs
@@ -3,13 +3,14 @@
use crate::args::CliOptions;
use crate::args::FilesConfig;
use crate::args::TestOptions;
-use crate::args::TypeCheckMode;
use crate::colors;
use crate::display;
+use crate::factory::CliFactory;
use crate::file_fetcher::File;
+use crate::file_fetcher::FileFetcher;
use crate::graph_util::graph_valid_with_cli_options;
+use crate::module_loader::ModuleLoadPreparer;
use crate::ops;
-use crate::proc_state::ProcState;
use crate::util::checksum;
use crate::util::file_watcher;
use crate::util::file_watcher::ResolutionResult;
@@ -17,7 +18,7 @@ use crate::util::fs::collect_specifiers;
use crate::util::path::get_extension;
use crate::util::path::is_supported_ext;
use crate::util::path::mapped_specifier_for_tsc;
-use crate::worker::create_custom_worker;
+use crate::worker::CliMainWorkerFactory;
use deno_ast::swc::common::comments::CommentKind;
use deno_ast::MediaType;
@@ -27,11 +28,14 @@ use deno_core::error::AnyError;
use deno_core::error::JsError;
use deno_core::futures::future;
use deno_core::futures::stream;
+use deno_core::futures::task::noop_waker;
use deno_core::futures::FutureExt;
use deno_core::futures::StreamExt;
use deno_core::located_script_name;
use deno_core::parking_lot::Mutex;
use deno_core::serde_v8;
+use deno_core::task::spawn;
+use deno_core::task::spawn_blocking;
use deno_core::url::Url;
use deno_core::v8;
use deno_core::ModuleSpecifier;
@@ -40,7 +44,7 @@ use deno_runtime::deno_io::StdioPipe;
use deno_runtime::fmt_errors::format_js_error;
use deno_runtime::permissions::Permissions;
use deno_runtime::permissions::PermissionsContainer;
-use deno_runtime::tokio_util::run_local;
+use deno_runtime::tokio_util::create_and_run_current_thread;
use indexmap::IndexMap;
use indexmap::IndexSet;
use log::Level;
@@ -49,7 +53,6 @@ use rand::seq::SliceRandom;
use rand::SeedableRng;
use regex::Regex;
use serde::Deserialize;
-use std::cell::RefCell;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::HashMap;
@@ -64,6 +67,7 @@ use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
+use std::task::Context;
use std::time::Duration;
use std::time::Instant;
use std::time::SystemTime;
@@ -336,10 +340,18 @@ pub struct TestSummary {
}
#[derive(Debug, Clone)]
-struct TestSpecifierOptions {
+struct TestSpecifiersOptions {
concurrent_jobs: NonZeroUsize,
fail_fast: Option,
- filter: TestFilter,
+ log_level: Option,
+ specifier: TestSpecifierOptions,
+}
+
+#[derive(Debug, Clone)]
+pub struct TestSpecifierOptions {
+ pub shuffle: Option,
+ pub filter: TestFilter,
+ pub trace_ops: bool,
}
impl TestSummary {
@@ -907,30 +919,30 @@ pub fn format_test_error(js_error: &JsError) -> String {
/// Test a single specifier as documentation containing test programs, an executable test module or
/// both.
pub async fn test_specifier(
- ps: &ProcState,
+ worker_factory: Arc,
permissions: Permissions,
specifier: ModuleSpecifier,
mut sender: TestEventSender,
fail_fast_tracker: FailFastTracker,
- filter: TestFilter,
+ options: TestSpecifierOptions,
) -> Result<(), AnyError> {
if fail_fast_tracker.should_stop() {
return Ok(());
}
let stdout = StdioPipe::File(sender.stdout());
let stderr = StdioPipe::File(sender.stderr());
- let mut worker = create_custom_worker(
- ps,
- specifier.clone(),
- PermissionsContainer::new(permissions),
- vec![ops::testing::deno_test::init_ops(sender.clone())],
- Stdio {
- stdin: StdioPipe::Inherit,
- stdout,
- stderr,
- },
- )
- .await?;
+ let mut worker = worker_factory
+ .create_custom_worker(
+ specifier.clone(),
+ PermissionsContainer::new(permissions),
+ vec![ops::testing::deno_test::init_ops(sender.clone())],
+ Stdio {
+ stdin: StdioPipe::Inherit,
+ stdout,
+ stderr,
+ },
+ )
+ .await?;
let mut coverage_collector = worker.maybe_setup_coverage_collector().await?;
@@ -951,7 +963,7 @@ pub async fn test_specifier(
}
let mut worker = worker.into_main_worker();
- if ps.options.trace_ops() {
+ if options.trace_ops {
worker.js_runtime.execute_script_static(
located_script_name!(),
"Deno[Deno.internal].core.enableOpCallTracing();",
@@ -971,9 +983,9 @@ pub async fn test_specifier(
let tests = if used_only { only } else { no_only };
let mut tests = tests
.into_iter()
- .filter(|(d, _)| filter.includes(&d.name))
+ .filter(|(d, _)| options.filter.includes(&d.name))
.collect::>();
- if let Some(seed) = ps.options.shuffle_tests() {
+ if let Some(seed) = options.shuffle {
tests.shuffle(&mut SmallRng::seed_from_u64(seed));
}
sender.send(TestEvent::Plan(TestPlan {
@@ -996,15 +1008,23 @@ pub async fn test_specifier(
continue;
}
sender.send(TestEvent::Wait(desc.id))?;
+
+ // TODO(bartlomieju): this is a nasty (beautiful) hack, that was required
+ // when switching `JsRuntime` from `FuturesUnordered` to `JoinSet`. With
+ // `JoinSet` all pending ops are immediately polled and that caused a problem
+ // when some async ops were fired and canceled before running tests (giving
+ // false positives in the ops sanitizer). We should probably rewrite sanitizers
+ // to be done in Rust instead of in JS (40_testing.js).
+ {
+ // Poll event loop once, this will allow all ops that are already resolved,
+ // but haven't responded to settle.
+ let waker = noop_waker();
+ let mut cx = Context::from_waker(&waker);
+ let _ = worker.js_runtime.poll_event_loop(&mut cx, false);
+ }
+
let earlier = SystemTime::now();
- let promise = {
- let scope = &mut worker.js_runtime.handle_scope();
- let cb = function.open(scope);
- let this = v8::undefined(scope).into();
- let promise = cb.call(scope, this, &[]).unwrap();
- v8::Global::new(scope, promise)
- };
- let result = match worker.js_runtime.resolve_value(promise).await {
+ let result = match worker.js_runtime.call_and_await(&function).await {
Ok(r) => r,
Err(error) => {
if error.is::() {
@@ -1031,12 +1051,9 @@ pub async fn test_specifier(
sender.send(TestEvent::Result(desc.id, result, elapsed as u64))?;
}
- loop {
- if !worker.dispatch_beforeunload_event(located_script_name!())? {
- break;
- }
- worker.run_event_loop(false).await?;
- }
+ // Ignore `defaultPrevented` of the `beforeunload` event. We don't allow the
+ // event loop to continue beyond what's needed to await results.
+ worker.dispatch_beforeunload_event(located_script_name!())?;
worker.dispatch_unload_event(located_script_name!())?;
if let Some(coverage_collector) = coverage_collector.as_mut() {
@@ -1199,13 +1216,13 @@ fn extract_files_from_fenced_blocks(
}
async fn fetch_inline_files(
- ps: &ProcState,
+ file_fetcher: &FileFetcher,
specifiers: Vec,
) -> Result, AnyError> {
let mut files = Vec::new();
for specifier in specifiers {
let fetch_permissions = PermissionsContainer::allow_all();
- let file = ps.file_fetcher.fetch(&specifier, fetch_permissions).await?;
+ let file = file_fetcher.fetch(&specifier, fetch_permissions).await?;
let inline_files = if file.media_type == MediaType::Unknown {
extract_files_from_fenced_blocks(
@@ -1229,13 +1246,14 @@ async fn fetch_inline_files(
/// Type check a collection of module and document specifiers.
pub async fn check_specifiers(
- ps: &ProcState,
- permissions: Permissions,
+ cli_options: &CliOptions,
+ file_fetcher: &FileFetcher,
+ module_load_preparer: &ModuleLoadPreparer,
specifiers: Vec<(ModuleSpecifier, TestMode)>,
) -> Result<(), AnyError> {
- let lib = ps.options.ts_type_lib_window();
+ let lib = cli_options.ts_type_lib_window();
let inline_files = fetch_inline_files(
- ps,
+ file_fetcher,
specifiers
.iter()
.filter_map(|(specifier, mode)| {
@@ -1256,16 +1274,15 @@ pub async fn check_specifiers(
.collect();
for file in inline_files {
- ps.file_fetcher.insert_cached(file);
+ file_fetcher.insert_cached(file);
}
- ps.module_load_preparer
+ module_load_preparer
.prepare_module_load(
specifiers,
false,
lib,
PermissionsContainer::new(Permissions::allow_all()),
- PermissionsContainer::new(permissions.clone()),
)
.await?;
}
@@ -1281,13 +1298,12 @@ pub async fn check_specifiers(
})
.collect();
- ps.module_load_preparer
+ module_load_preparer
.prepare_module_load(
module_specifiers,
false,
lib,
PermissionsContainer::allow_all(),
- PermissionsContainer::new(permissions),
)
.await?;
@@ -1298,13 +1314,12 @@ static HAS_TEST_RUN_SIGINT_HANDLER: AtomicBool = AtomicBool::new(false);
/// Test a collection of specifiers with test modes concurrently.
async fn test_specifiers(
- ps: &ProcState,
+ worker_factory: Arc,
permissions: &Permissions,
specifiers: Vec,
- options: TestSpecifierOptions,
+ options: TestSpecifiersOptions,
) -> Result<(), AnyError> {
- let log_level = ps.options.log_level();
- let specifiers = if let Some(seed) = ps.options.shuffle_tests() {
+ let specifiers = if let Some(seed) = options.specifier.shuffle {
let mut rng = SmallRng::seed_from_u64(seed);
let mut specifiers = specifiers;
specifiers.sort();
@@ -1319,26 +1334,26 @@ async fn test_specifiers(
let concurrent_jobs = options.concurrent_jobs;
let sender_ = sender.downgrade();
- let sigint_handler_handle = tokio::task::spawn(async move {
+ let sigint_handler_handle = spawn(async move {
signal::ctrl_c().await.unwrap();
sender_.upgrade().map(|s| s.send(TestEvent::Sigint).ok());
});
HAS_TEST_RUN_SIGINT_HANDLER.store(true, Ordering::Relaxed);
let join_handles = specifiers.into_iter().map(move |specifier| {
- let ps = ps.clone();
+ let worker_factory = worker_factory.clone();
let permissions = permissions.clone();
let sender = sender.clone();
- let options = options.clone();
let fail_fast_tracker = FailFastTracker::new(options.fail_fast);
- tokio::task::spawn_blocking(move || {
- run_local(test_specifier(
- &ps,
+ let specifier_options = options.specifier.clone();
+ spawn_blocking(move || {
+ create_and_run_current_thread(test_specifier(
+ worker_factory,
permissions,
specifier,
sender.clone(),
fail_fast_tracker,
- options.filter,
+ specifier_options,
))
})
});
@@ -1349,11 +1364,11 @@ async fn test_specifiers(
let mut reporter = Box::new(PrettyTestReporter::new(
concurrent_jobs.get() > 1,
- log_level != Some(Level::Error),
+ options.log_level != Some(Level::Error),
));
let handler = {
- tokio::task::spawn(async move {
+ spawn(async move {
let earlier = Instant::now();
let mut tests = IndexMap::new();
let mut test_steps = IndexMap::new();
@@ -1518,7 +1533,7 @@ async fn test_specifiers(
}
/// Checks if the path has a basename and extension Deno supports for tests.
-fn is_supported_test_path(path: &Path) -> bool {
+pub(crate) fn is_supported_test_path(path: &Path) -> bool {
if let Some(name) = path.file_stem() {
let basename = name.to_string_lossy();
(basename.ends_with("_test")
@@ -1604,15 +1619,14 @@ fn collect_specifiers_with_test_mode(
/// cannot be run, and therefore need to be marked as `TestMode::Documentation`
/// as well.
async fn fetch_specifiers_with_test_mode(
- ps: &ProcState,
+ file_fetcher: &FileFetcher,
files: &FilesConfig,
doc: &bool,
) -> Result, AnyError> {
let mut specifiers_with_mode = collect_specifiers_with_test_mode(files, doc)?;
for (specifier, mode) in &mut specifiers_with_mode {
- let file = ps
- .file_fetcher
+ let file = file_fetcher
.fetch(specifier, PermissionsContainer::allow_all())
.await?;
@@ -1630,15 +1644,19 @@ pub async fn run_tests(
cli_options: CliOptions,
test_options: TestOptions,
) -> Result<(), AnyError> {
- let ps = ProcState::from_cli_options(Arc::new(cli_options)).await?;
+ let factory = CliFactory::from_cli_options(Arc::new(cli_options));
+ let cli_options = factory.cli_options();
+ let file_fetcher = factory.file_fetcher()?;
+ let module_load_preparer = factory.module_load_preparer().await?;
// Various test files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable.
let permissions =
- Permissions::from_options(&ps.options.permissions_options())?;
+ Permissions::from_options(&cli_options.permissions_options())?;
+ let log_level = cli_options.log_level();
let specifiers_with_mode = fetch_specifiers_with_test_mode(
- &ps,
+ file_fetcher,
&test_options.files,
&test_options.doc,
)
@@ -1648,15 +1666,23 @@ pub async fn run_tests(
return Err(generic_error("No test modules found"));
}
- check_specifiers(&ps, permissions.clone(), specifiers_with_mode.clone())
- .await?;
+ check_specifiers(
+ cli_options,
+ file_fetcher,
+ module_load_preparer,
+ specifiers_with_mode.clone(),
+ )
+ .await?;
if test_options.no_run {
return Ok(());
}
+ let worker_factory =
+ Arc::new(factory.create_cli_main_worker_factory().await?);
+
test_specifiers(
- &ps,
+ worker_factory,
&permissions,
specifiers_with_mode
.into_iter()
@@ -1665,10 +1691,15 @@ pub async fn run_tests(
_ => Some(s),
})
.collect(),
- TestSpecifierOptions {
+ TestSpecifiersOptions {
concurrent_jobs: test_options.concurrent_jobs,
fail_fast: test_options.fail_fast,
- filter: TestFilter::from_flag(&test_options.filter),
+ log_level,
+ specifier: TestSpecifierOptions {
+ filter: TestFilter::from_flag(&test_options.filter),
+ shuffle: test_options.shuffle,
+ trace_ops: test_options.trace_ops,
+ },
},
)
.await?;
@@ -1680,22 +1711,27 @@ pub async fn run_tests_with_watch(
cli_options: CliOptions,
test_options: TestOptions,
) -> Result<(), AnyError> {
- let ps = ProcState::from_cli_options(Arc::new(cli_options)).await?;
+ let factory = CliFactory::from_cli_options(Arc::new(cli_options));
+ let cli_options = factory.cli_options();
+ let module_graph_builder = factory.module_graph_builder().await?;
+ let module_load_preparer = factory.module_load_preparer().await?;
+ let file_fetcher = factory.file_fetcher()?;
+ let file_watcher = factory.file_watcher()?;
// Various test files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable.
let permissions =
- Permissions::from_options(&ps.options.permissions_options())?;
- let no_check = ps.options.type_check_mode() == TypeCheckMode::None;
-
- let ps = RefCell::new(ps);
+ Permissions::from_options(&cli_options.permissions_options())?;
+ let graph_kind = cli_options.type_check_mode().as_graph_kind();
+ let log_level = cli_options.log_level();
let resolver = |changed: Option>| {
let paths_to_watch = test_options.files.include.clone();
let paths_to_watch_clone = paths_to_watch.clone();
let files_changed = changed.is_some();
let test_options = &test_options;
- let ps = ps.borrow().clone();
+ let cli_options = cli_options.clone();
+ let module_graph_builder = module_graph_builder.clone();
async move {
let test_modules = if test_options.doc {
@@ -1710,11 +1746,10 @@ pub async fn run_tests_with_watch(
} else {
test_modules.clone()
};
- let graph = ps
- .module_graph_builder
- .create_graph(test_modules.clone())
+ let graph = module_graph_builder
+ .create_graph(graph_kind, test_modules.clone())
.await?;
- graph_valid_with_cli_options(&graph, &test_modules, &ps.options)?;
+ graph_valid_with_cli_options(&graph, &test_modules, &cli_options)?;
// TODO(@kitsonk) - This should be totally derivable from the graph.
for specifier in test_modules {
@@ -1724,32 +1759,19 @@ pub async fn run_tests_with_watch(
// This needs to be accessible to skip getting dependencies if they're already there,
// otherwise this will cause a stack overflow with circular dependencies
output: &mut HashSet<&'a ModuleSpecifier>,
- no_check: bool,
) {
if let Some(module) = maybe_module.and_then(|m| m.esm()) {
for dep in module.dependencies.values() {
if let Some(specifier) = &dep.get_code() {
if !output.contains(specifier) {
output.insert(specifier);
- get_dependencies(
- graph,
- graph.get(specifier),
- output,
- no_check,
- );
+ get_dependencies(graph, graph.get(specifier), output);
}
}
- if !no_check {
- if let Some(specifier) = &dep.get_type() {
- if !output.contains(specifier) {
- output.insert(specifier);
- get_dependencies(
- graph,
- graph.get(specifier),
- output,
- no_check,
- );
- }
+ if let Some(specifier) = &dep.get_type() {
+ if !output.contains(specifier) {
+ output.insert(specifier);
+ get_dependencies(graph, graph.get(specifier), output);
}
}
}
@@ -1759,7 +1781,7 @@ pub async fn run_tests_with_watch(
// This test module and all it's dependencies
let mut modules = HashSet::new();
modules.insert(&specifier);
- get_dependencies(&graph, graph.get(&specifier), &mut modules, no_check);
+ get_dependencies(&graph, graph.get(&specifier), &mut modules);
paths_to_watch.extend(
modules
@@ -1804,15 +1826,21 @@ pub async fn run_tests_with_watch(
})
};
+ let create_cli_main_worker_factory =
+ factory.create_cli_main_worker_factory_func().await?;
let operation = |modules_to_reload: Vec| {
let permissions = &permissions;
let test_options = &test_options;
- ps.borrow_mut().reset_for_file_watcher();
- let ps = ps.borrow().clone();
+ file_watcher.reset();
+ let cli_options = cli_options.clone();
+ let file_fetcher = file_fetcher.clone();
+ let module_load_preparer = module_load_preparer.clone();
+ let create_cli_main_worker_factory = create_cli_main_worker_factory.clone();
async move {
+ let worker_factory = Arc::new(create_cli_main_worker_factory());
let specifiers_with_mode = fetch_specifiers_with_test_mode(
- &ps,
+ &file_fetcher,
&test_options.files,
&test_options.doc,
)
@@ -1821,15 +1849,20 @@ pub async fn run_tests_with_watch(
.filter(|(specifier, _)| modules_to_reload.contains(specifier))
.collect::>();
- check_specifiers(&ps, permissions.clone(), specifiers_with_mode.clone())
- .await?;
+ check_specifiers(
+ &cli_options,
+ &file_fetcher,
+ &module_load_preparer,
+ specifiers_with_mode.clone(),
+ )
+ .await?;
if test_options.no_run {
return Ok(());
}
test_specifiers(
- &ps,
+ worker_factory,
permissions,
specifiers_with_mode
.into_iter()
@@ -1838,10 +1871,15 @@ pub async fn run_tests_with_watch(
_ => Some(s),
})
.collect(),
- TestSpecifierOptions {
+ TestSpecifiersOptions {
concurrent_jobs: test_options.concurrent_jobs,
fail_fast: test_options.fail_fast,
- filter: TestFilter::from_flag(&test_options.filter),
+ log_level,
+ specifier: TestSpecifierOptions {
+ filter: TestFilter::from_flag(&test_options.filter),
+ shuffle: test_options.shuffle,
+ trace_ops: test_options.trace_ops,
+ },
},
)
.await?;
@@ -1854,7 +1892,7 @@ pub async fn run_tests_with_watch(
// run, a process-scoped basic exit handler is required due to a tokio
// limitation where it doesn't unbind its own handler for the entire process
// once a user adds one.
- tokio::task::spawn(async move {
+ spawn(async move {
loop {
signal::ctrl_c().await.unwrap();
if !HAS_TEST_RUN_SIGINT_HANDLER.load(Ordering::Relaxed) {
@@ -1863,7 +1901,7 @@ pub async fn run_tests_with_watch(
}
});
- let clear_screen = !ps.borrow().options.no_clear_screen();
+ let clear_screen = !cli_options.no_clear_screen();
file_watcher::watch_func(
resolver,
operation,
@@ -2037,7 +2075,7 @@ fn start_output_redirect_thread(
sender: UnboundedSender,
flush_state: Arc>>>,
) {
- tokio::task::spawn_blocking(move || loop {
+ spawn_blocking(move || loop {
let mut buffer = [0; 512];
let size = match pipe_reader.read(&mut buffer) {
Ok(0) | Err(_) => break,
diff --git a/cli/tools/upgrade.rs b/cli/tools/upgrade.rs
index f16923bf83..b371731c31 100644
--- a/cli/tools/upgrade.rs
+++ b/cli/tools/upgrade.rs
@@ -5,8 +5,8 @@
use crate::args::Flags;
use crate::args::UpgradeFlags;
use crate::colors;
+use crate::factory::CliFactory;
use crate::http_util::HttpClient;
-use crate::proc_state::ProcState;
use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
use crate::util::time;
@@ -17,6 +17,7 @@ use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::futures::future::BoxFuture;
use deno_core::futures::FutureExt;
+use deno_core::task::spawn;
use deno_semver::Version;
use once_cell::sync::Lazy;
use std::borrow::Cow;
@@ -26,6 +27,7 @@ use std::ops::Sub;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
+use std::sync::Arc;
use std::time::Duration;
static ARCHIVE_NAME: Lazy =
@@ -50,13 +52,13 @@ trait UpdateCheckerEnvironment: Clone + Send + Sync {
#[derive(Clone)]
struct RealUpdateCheckerEnvironment {
- http_client: HttpClient,
+ http_client: Arc,
cache_file_path: PathBuf,
current_time: chrono::DateTime,
}
impl RealUpdateCheckerEnvironment {
- pub fn new(http_client: HttpClient, cache_file_path: PathBuf) -> Self {
+ pub fn new(http_client: Arc, cache_file_path: PathBuf) -> Self {
Self {
http_client,
cache_file_path,
@@ -183,7 +185,10 @@ fn print_release_notes(current_version: &str, new_version: &str) {
}
}
-pub fn check_for_upgrades(http_client: HttpClient, cache_file_path: PathBuf) {
+pub fn check_for_upgrades(
+ http_client: Arc,
+ cache_file_path: PathBuf,
+) {
if env::var("DENO_NO_UPDATE_CHECK").is_ok() {
return;
}
@@ -194,7 +199,7 @@ pub fn check_for_upgrades(http_client: HttpClient, cache_file_path: PathBuf) {
if update_checker.should_check_for_new_version() {
let env = update_checker.env.clone();
// do this asynchronously on a separate task
- tokio::spawn(async move {
+ spawn(async move {
// Sleep for a small amount of time to not unnecessarily impact startup
// time.
tokio::time::sleep(UPGRADE_CHECK_FETCH_DELAY).await;
@@ -226,7 +231,6 @@ pub fn check_for_upgrades(http_client: HttpClient, cache_file_path: PathBuf) {
"{}",
colors::italic_gray("Run `deno upgrade` to install it.")
);
- print_release_notes(version::deno(), &upgrade_version);
}
update_checker.store_prompted();
@@ -263,7 +267,8 @@ pub async fn upgrade(
flags: Flags,
upgrade_flags: UpgradeFlags,
) -> Result<(), AnyError> {
- let ps = ProcState::from_flags(flags).await?;
+ let factory = CliFactory::from_flags(flags).await?;
+ let client = factory.http_client();
let current_exe_path = std::env::current_exe()?;
let metadata = fs::metadata(¤t_exe_path)?;
let permissions = metadata.permissions();
@@ -285,11 +290,13 @@ pub async fn upgrade(
), current_exe_path.display());
}
- let client = &ps.http_client;
-
let install_version = match upgrade_flags.version {
Some(passed_version) => {
let re_hash = lazy_regex::regex!("^[0-9a-f]{40}$");
+ let passed_version = passed_version
+ .strip_prefix('v')
+ .unwrap_or(&passed_version)
+ .to_string();
if upgrade_flags.canary && !re_hash.is_match(&passed_version) {
bail!("Invalid commit hash passed");
@@ -313,9 +320,9 @@ pub async fn upgrade(
{
log::info!("Version {} is already installed", crate::version::deno());
return Ok(());
- } else {
- passed_version
}
+
+ passed_version
}
None => {
let latest_version = if upgrade_flags.canary {
@@ -327,7 +334,7 @@ pub async fn upgrade(
};
let current_is_most_recent = if upgrade_flags.canary {
- let latest_hash = latest_version.clone();
+ let latest_hash = &latest_version;
crate::version::GIT_COMMIT_HASH == latest_hash
} else if !crate::version::is_canary() {
let current = Version::parse_standard(crate::version::deno()).unwrap();
@@ -359,7 +366,7 @@ pub async fn upgrade(
let download_url = if upgrade_flags.canary {
if env!("TARGET") == "aarch64-apple-darwin" {
- bail!("Canary builds are not available for M1");
+ bail!("Canary builds are not available for M1/M2");
}
format!(
diff --git a/cli/tools/vendor/build.rs b/cli/tools/vendor/build.rs
index f9df8f0786..11a1fb50e6 100644
--- a/cli/tools/vendor/build.rs
+++ b/cli/tools/vendor/build.rs
@@ -378,6 +378,54 @@ mod test {
);
}
+ #[tokio::test]
+ async fn remote_redirect_entrypoint() {
+ let mut builder = VendorTestBuilder::with_default_setup();
+ let output = builder
+ .with_loader(|loader| {
+ loader
+ .add(
+ "/mod.ts",
+ concat!(
+ "import * as test from 'https://x.nest.land/Yenv@1.0.0/mod.ts';\n",
+ "console.log(test)",
+ ),
+ )
+ .add_redirect("https://x.nest.land/Yenv@1.0.0/mod.ts", "https://arweave.net/VFtWNW3QZ-7__v7c7kck22eFI24OuK1DFzyQHKoZ9AE/mod.ts")
+ .add(
+ "https://arweave.net/VFtWNW3QZ-7__v7c7kck22eFI24OuK1DFzyQHKoZ9AE/mod.ts",
+ "export * from './src/mod.ts'",
+ )
+ .add(
+ "https://arweave.net/VFtWNW3QZ-7__v7c7kck22eFI24OuK1DFzyQHKoZ9AE/src/mod.ts",
+ "export class Test {}",
+ );
+ })
+ .build()
+ .await
+ .unwrap();
+
+ assert_eq!(
+ output.import_map,
+ Some(json!({
+ "imports": {
+ "https://x.nest.land/Yenv@1.0.0/mod.ts": "./arweave.net/VFtWNW3QZ-7__v7c7kck22eFI24OuK1DFzyQHKoZ9AE/mod.ts",
+ "https://arweave.net/": "./arweave.net/"
+ },
+ }))
+ );
+ assert_eq!(
+ output.files,
+ to_file_vec(&[
+ ("/vendor/arweave.net/VFtWNW3QZ-7__v7c7kck22eFI24OuK1DFzyQHKoZ9AE/mod.ts", "export * from './src/mod.ts'"),
+ (
+ "/vendor/arweave.net/VFtWNW3QZ-7__v7c7kck22eFI24OuK1DFzyQHKoZ9AE/src/mod.ts",
+ "export class Test {}",
+ ),
+ ]),
+ );
+ }
+
#[tokio::test]
async fn same_target_filename_specifiers() {
let mut builder = VendorTestBuilder::with_default_setup();
diff --git a/cli/tools/vendor/import_map.rs b/cli/tools/vendor/import_map.rs
index 916eb55c58..dbda81a3a0 100644
--- a/cli/tools/vendor/import_map.rs
+++ b/cli/tools/vendor/import_map.rs
@@ -304,7 +304,7 @@ fn handle_dep_specifier(
referrer,
mappings,
)
- } else {
+ } else if specifier.scheme() == "file" {
handle_local_dep_specifier(
text,
unresolved_specifier,
@@ -326,15 +326,16 @@ fn handle_remote_dep_specifier(
) {
if is_remote_specifier_text(text) {
let base_specifier = mappings.base_specifier(specifier);
- if !text.starts_with(base_specifier.as_str()) {
- panic!("Expected {text} to start with {base_specifier}");
- }
-
- let sub_path = &text[base_specifier.as_str().len()..];
- let relative_text =
- mappings.relative_specifier_text(base_specifier, specifier);
- let expected_sub_path = relative_text.trim_start_matches("./");
- if expected_sub_path != sub_path {
+ if text.starts_with(base_specifier.as_str()) {
+ let sub_path = &text[base_specifier.as_str().len()..];
+ let relative_text =
+ mappings.relative_specifier_text(base_specifier, specifier);
+ let expected_sub_path = relative_text.trim_start_matches("./");
+ if expected_sub_path != sub_path {
+ import_map.imports.add(text.to_string(), specifier);
+ }
+ } else {
+ // it's probably a redirect. Add it explicitly to the import map
import_map.imports.add(text.to_string(), specifier);
}
} else {
diff --git a/cli/tools/vendor/mod.rs b/cli/tools/vendor/mod.rs
index 225c3e6a81..61ada605c5 100644
--- a/cli/tools/vendor/mod.rs
+++ b/cli/tools/vendor/mod.rs
@@ -5,18 +5,21 @@ use std::path::PathBuf;
use std::sync::Arc;
use deno_ast::ModuleSpecifier;
+use deno_ast::TextChange;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::resolve_url_or_path;
+use deno_graph::GraphKind;
use log::warn;
use crate::args::CliOptions;
+use crate::args::ConfigFile;
use crate::args::Flags;
use crate::args::FmtOptionsConfig;
use crate::args::VendorFlags;
+use crate::factory::CliFactory;
use crate::graph_util::ModuleGraphBuilder;
-use crate::proc_state::ProcState;
use crate::tools::fmt::format_json;
use crate::util::fs::canonicalize_path;
use crate::util::fs::resolve_from_cwd;
@@ -43,19 +46,23 @@ pub async fn vendor(
let output_dir = resolve_from_cwd(&raw_output_dir)?;
validate_output_dir(&output_dir, &vendor_flags)?;
validate_options(&mut cli_options, &output_dir)?;
- let ps = ProcState::from_cli_options(Arc::new(cli_options)).await?;
+ let factory = CliFactory::from_cli_options(Arc::new(cli_options));
+ let cli_options = factory.cli_options();
let graph = create_graph(
- &ps.module_graph_builder,
+ factory.module_graph_builder().await?,
&vendor_flags,
- ps.options.initial_cwd(),
+ cli_options.initial_cwd(),
)
.await?;
+ let npm_package_count = graph.npm_packages.len();
+ let try_add_node_modules_dir = npm_package_count > 0
+ && cli_options.node_modules_dir_enablement().unwrap_or(true);
let vendored_count = build::build(
graph,
- &ps.parsed_source_cache,
+ factory.parsed_source_cache()?,
&output_dir,
- ps.maybe_import_map.as_deref(),
- ps.lockfile.clone(),
+ factory.maybe_import_map().await?.as_deref(),
+ factory.maybe_lockfile().clone(),
&build::RealVendorEnvironment,
)?;
@@ -69,9 +76,48 @@ pub async fn vendor(
},
raw_output_dir.display(),
);
+
+ let try_add_import_map = vendored_count > 0;
+ let modified_result = maybe_update_config_file(
+ &output_dir,
+ cli_options,
+ try_add_import_map,
+ try_add_node_modules_dir,
+ );
+
+ // cache the node_modules folder when it's been added to the config file
+ if modified_result.added_node_modules_dir {
+ let node_modules_path = cli_options.node_modules_dir_path().or_else(|| {
+ cli_options
+ .maybe_config_file_specifier()
+ .filter(|c| c.scheme() == "file")
+ .and_then(|c| c.to_file_path().ok())
+ .map(|config_path| config_path.parent().unwrap().join("node_modules"))
+ });
+ if let Some(node_modules_path) = node_modules_path {
+ factory
+ .create_node_modules_npm_fs_resolver(node_modules_path)
+ .await?
+ .cache_packages()
+ .await?;
+ }
+ log::info!(
+ concat!(
+ "Vendored {} npm {} into node_modules directory. Set `nodeModulesDir: false` ",
+ "in the Deno configuration file to disable vendoring npm packages in the future.",
+ ),
+ npm_package_count,
+ if npm_package_count == 1 {
+ "package"
+ } else {
+ "packages"
+ },
+ );
+ }
+
if vendored_count > 0 {
let import_map_path = raw_output_dir.join("import_map.json");
- if maybe_update_config_file(&output_dir, &ps.options) {
+ if modified_result.updated_import_map {
log::info!(
concat!(
"\nUpdated your local Deno configuration file with a reference to the ",
@@ -153,107 +199,156 @@ fn validate_options(
Ok(())
}
-fn maybe_update_config_file(output_dir: &Path, options: &CliOptions) -> bool {
+fn maybe_update_config_file(
+ output_dir: &Path,
+ options: &CliOptions,
+ try_add_import_map: bool,
+ try_add_node_modules_dir: bool,
+) -> ModifiedResult {
assert!(output_dir.is_absolute());
- let config_file_specifier = match options.maybe_config_file_specifier() {
- Some(f) => f,
- None => return false,
+ let config_file = match options.maybe_config_file() {
+ Some(config_file) => config_file,
+ None => return ModifiedResult::default(),
};
+ if config_file.specifier.scheme() != "file" {
+ return ModifiedResult::default();
+ }
- let fmt_config = options
- .maybe_config_file()
- .as_ref()
- .and_then(|config| config.to_fmt_config().ok())
+ let fmt_config = config_file
+ .to_fmt_config()
+ .ok()
.unwrap_or_default()
.unwrap_or_default();
let result = update_config_file(
- &config_file_specifier,
- &ModuleSpecifier::from_file_path(output_dir.join("import_map.json"))
- .unwrap(),
+ config_file,
&fmt_config.options,
+ if try_add_import_map {
+ Some(
+ ModuleSpecifier::from_file_path(output_dir.join("import_map.json"))
+ .unwrap(),
+ )
+ } else {
+ None
+ },
+ try_add_node_modules_dir,
);
match result {
- Ok(()) => true,
+ Ok(modified_result) => modified_result,
Err(err) => {
warn!("Error updating config file. {:#}", err);
- false
+ ModifiedResult::default()
}
}
}
fn update_config_file(
- config_specifier: &ModuleSpecifier,
- import_map_specifier: &ModuleSpecifier,
+ config_file: &ConfigFile,
fmt_options: &FmtOptionsConfig,
-) -> Result<(), AnyError> {
- if config_specifier.scheme() != "file" {
- return Ok(());
- }
-
- let config_path = specifier_to_file_path(config_specifier)?;
+ import_map_specifier: Option,
+ try_add_node_modules_dir: bool,
+) -> Result {
+ let config_path = specifier_to_file_path(&config_file.specifier)?;
let config_text = std::fs::read_to_string(&config_path)?;
- let relative_text =
- match relative_specifier(config_specifier, import_map_specifier) {
- Some(text) => text,
- None => return Ok(()), // ignore
- };
- if let Some(new_text) =
- update_config_text(&config_text, &relative_text, fmt_options)
- {
+ let import_map_specifier =
+ import_map_specifier.and_then(|import_map_specifier| {
+ relative_specifier(&config_file.specifier, &import_map_specifier)
+ });
+ let modified_result = update_config_text(
+ &config_text,
+ fmt_options,
+ import_map_specifier.as_deref(),
+ try_add_node_modules_dir,
+ )?;
+ if let Some(new_text) = &modified_result.new_text {
std::fs::write(config_path, new_text)?;
}
+ Ok(modified_result)
+}
- Ok(())
+#[derive(Default)]
+struct ModifiedResult {
+ updated_import_map: bool,
+ added_node_modules_dir: bool,
+ new_text: Option,
}
fn update_config_text(
text: &str,
- import_map_specifier: &str,
fmt_options: &FmtOptionsConfig,
-) -> Option {
+ import_map_specifier: Option<&str>,
+ try_add_node_modules_dir: bool,
+) -> Result {
use jsonc_parser::ast::ObjectProp;
use jsonc_parser::ast::Value;
let ast =
- jsonc_parser::parse_to_ast(text, &Default::default(), &Default::default())
- .ok()?;
+ jsonc_parser::parse_to_ast(text, &Default::default(), &Default::default())?;
let obj = match ast.value {
Some(Value::Object(obj)) => obj,
- _ => return None, // shouldn't happen, so ignore
+ _ => bail!("Failed updating config file due to no object."),
};
- let import_map_specifier = import_map_specifier.replace('\"', "\\\"");
+ let mut modified_result = ModifiedResult::default();
+ let mut text_changes = Vec::new();
+ let mut should_format = false;
- match obj.get("importMap") {
- Some(ObjectProp {
- value: Value::StringLit(lit),
- ..
- }) => Some(format!(
- "{}{}{}",
- &text[..lit.range.start + 1],
- import_map_specifier,
- &text[lit.range.end - 1..],
- )),
- None => {
- // insert it crudely at a position that won't cause any issues
- // with comments and format after to make it look nice
+ if try_add_node_modules_dir {
+ // Only modify the nodeModulesDir property if it's not set
+ // as this allows people to opt-out of this when vendoring
+ // by specifying `nodeModulesDir: false`
+ if obj.get("nodeModulesDir").is_none() {
let insert_position = obj.range.end - 1;
- let insert_text = format!(
- r#"{}"importMap": "{}""#,
- if obj.properties.is_empty() { "" } else { "," },
- import_map_specifier
- );
- let new_text = format!(
- "{}{}{}",
- &text[..insert_position],
- insert_text,
- &text[insert_position..],
- );
- format_json(&new_text, fmt_options)
- .ok()
- .map(|formatted_text| formatted_text.unwrap_or(new_text))
+ text_changes.push(TextChange {
+ range: insert_position..insert_position,
+ new_text: r#""nodeModulesDir": true"#.to_string(),
+ });
+ should_format = true;
+ modified_result.added_node_modules_dir = true;
}
- // shouldn't happen, so ignore
- Some(_) => None,
}
+
+ if let Some(import_map_specifier) = import_map_specifier {
+ let import_map_specifier = import_map_specifier.replace('\"', "\\\"");
+ match obj.get("importMap") {
+ Some(ObjectProp {
+ value: Value::StringLit(lit),
+ ..
+ }) => {
+ text_changes.push(TextChange {
+ range: lit.range.start..lit.range.end,
+ new_text: format!("\"{}\"", import_map_specifier),
+ });
+ modified_result.updated_import_map = true;
+ }
+ None => {
+ // insert it crudely at a position that won't cause any issues
+ // with comments and format after to make it look nice
+ let insert_position = obj.range.end - 1;
+ text_changes.push(TextChange {
+ range: insert_position..insert_position,
+ new_text: format!(r#""importMap": "{}""#, import_map_specifier),
+ });
+ should_format = true;
+ modified_result.updated_import_map = true;
+ }
+ // shouldn't happen
+ Some(_) => {
+ bail!("Failed updating importMap in config file due to invalid type.")
+ }
+ }
+ }
+
+ if text_changes.is_empty() {
+ return Ok(modified_result);
+ }
+
+ let new_text = deno_ast::apply_text_changes(text, text_changes);
+ modified_result.new_text = if should_format {
+ format_json(&new_text, fmt_options)
+ .ok()
+ .map(|formatted_text| formatted_text.unwrap_or(new_text))
+ } else {
+ Some(new_text)
+ };
+ Ok(modified_result)
}
fn is_dir_empty(dir_path: &Path) -> Result {
@@ -277,7 +372,9 @@ async fn create_graph(
.map(|p| resolve_url_or_path(p, initial_cwd))
.collect::, _>>()?;
- module_graph_builder.create_graph(entry_points).await
+ module_graph_builder
+ .create_graph(GraphKind::All, entry_points)
+ .await
}
#[cfg(test)]
@@ -287,36 +384,94 @@ mod internal_test {
#[test]
fn update_config_text_no_existing_props_add_prop() {
- let text = update_config_text(
+ let result = update_config_text(
"{\n}",
- "./vendor/import_map.json",
&Default::default(),
+ Some("./vendor/import_map.json"),
+ false,
)
.unwrap();
+ assert!(result.updated_import_map);
+ assert!(!result.added_node_modules_dir);
assert_eq!(
- text,
+ result.new_text.unwrap(),
r#"{
"importMap": "./vendor/import_map.json"
}
+"#
+ );
+
+ let result = update_config_text(
+ "{\n}",
+ &Default::default(),
+ Some("./vendor/import_map.json"),
+ true,
+ )
+ .unwrap();
+ assert!(result.updated_import_map);
+ assert!(result.added_node_modules_dir);
+ assert_eq!(
+ result.new_text.unwrap(),
+ r#"{
+ "nodeModulesDir": true,
+ "importMap": "./vendor/import_map.json"
+}
+"#
+ );
+
+ let result =
+ update_config_text("{\n}", &Default::default(), None, true).unwrap();
+ assert!(!result.updated_import_map);
+ assert!(result.added_node_modules_dir);
+ assert_eq!(
+ result.new_text.unwrap(),
+ r#"{
+ "nodeModulesDir": true
+}
"#
);
}
#[test]
fn update_config_text_existing_props_add_prop() {
- let text = update_config_text(
+ let result = update_config_text(
r#"{
"tasks": {
"task1": "other"
}
}
"#,
- "./vendor/import_map.json",
&Default::default(),
+ Some("./vendor/import_map.json"),
+ false,
)
.unwrap();
assert_eq!(
- text,
+ result.new_text.unwrap(),
+ r#"{
+ "tasks": {
+ "task1": "other"
+ },
+ "importMap": "./vendor/import_map.json"
+}
+"#
+ );
+
+ // trailing comma
+ let result = update_config_text(
+ r#"{
+ "tasks": {
+ "task1": "other"
+ },
+}
+"#,
+ &Default::default(),
+ Some("./vendor/import_map.json"),
+ false,
+ )
+ .unwrap();
+ assert_eq!(
+ result.new_text.unwrap(),
r#"{
"tasks": {
"task1": "other"
@@ -329,21 +484,54 @@ mod internal_test {
#[test]
fn update_config_text_update_prop() {
- let text = update_config_text(
+ let result = update_config_text(
r#"{
"importMap": "./local.json"
}
"#,
- "./vendor/import_map.json",
&Default::default(),
+ Some("./vendor/import_map.json"),
+ false,
)
.unwrap();
assert_eq!(
- text,
+ result.new_text.unwrap(),
r#"{
"importMap": "./vendor/import_map.json"
}
"#
);
}
+
+ #[test]
+ fn no_update_node_modules_dir() {
+ // will not update if this is already set (even if it's false)
+ let result = update_config_text(
+ r#"{
+ "nodeModulesDir": false
+}
+"#,
+ &Default::default(),
+ None,
+ true,
+ )
+ .unwrap();
+ assert!(!result.added_node_modules_dir);
+ assert!(!result.updated_import_map);
+ assert_eq!(result.new_text, None);
+
+ let result = update_config_text(
+ r#"{
+ "nodeModulesDir": true
+}
+"#,
+ &Default::default(),
+ None,
+ true,
+ )
+ .unwrap();
+ assert!(!result.added_node_modules_dir);
+ assert!(!result.updated_import_map);
+ assert_eq!(result.new_text, None);
+ }
}
diff --git a/cli/tools/vendor/specifiers.rs b/cli/tools/vendor/specifiers.rs
index 7418bcb8b5..bb7e0317a8 100644
--- a/cli/tools/vendor/specifiers.rs
+++ b/cli/tools/vendor/specifiers.rs
@@ -65,7 +65,7 @@ pub fn make_url_relative(
}
pub fn is_remote_specifier(specifier: &ModuleSpecifier) -> bool {
- specifier.scheme().to_lowercase().starts_with("http")
+ matches!(specifier.scheme().to_lowercase().as_str(), "http" | "https")
}
pub fn is_remote_specifier_text(text: &str) -> bool {
diff --git a/cli/tools/vendor/test.rs b/cli/tools/vendor/test.rs
index 774ff0d583..08b6d8355b 100644
--- a/cli/tools/vendor/test.rs
+++ b/cli/tools/vendor/test.rs
@@ -16,13 +16,13 @@ use deno_core::serde_json;
use deno_graph::source::LoadFuture;
use deno_graph::source::LoadResponse;
use deno_graph::source::Loader;
+use deno_graph::GraphKind;
use deno_graph::ModuleGraph;
use import_map::ImportMap;
use crate::cache::ParsedSourceCache;
use crate::npm::CliNpmRegistryApi;
use crate::npm::NpmResolution;
-use crate::npm::PackageJsonDepsInstaller;
use crate::resolver::CliGraphResolver;
use super::build::VendorEnvironment;
@@ -270,21 +270,17 @@ async fn build_test_graph(
None,
None,
));
- let deps_installer = Arc::new(PackageJsonDepsInstaller::new(
- npm_registry_api.clone(),
- npm_resolution.clone(),
- None,
- ));
CliGraphResolver::new(
None,
Some(Arc::new(original_import_map)),
false,
npm_registry_api,
npm_resolution,
- deps_installer,
+ Default::default(),
+ Default::default(),
)
});
- let mut graph = ModuleGraph::default();
+ let mut graph = ModuleGraph::new(GraphKind::All);
graph
.build(
roots,
diff --git a/cli/tsc/00_typescript.js b/cli/tsc/00_typescript.js
index 63743a3724..a477f09d9e 100644
--- a/cli/tsc/00_typescript.js
+++ b/cli/tsc/00_typescript.js
@@ -35,7 +35,7 @@ var ts = (() => {
"src/compiler/corePublic.ts"() {
"use strict";
versionMajorMinor = "5.0";
- version = "5.0.3";
+ version = "5.0.4";
Comparison = /* @__PURE__ */ ((Comparison3) => {
Comparison3[Comparison3["LessThan"] = -1] = "LessThan";
Comparison3[Comparison3["EqualTo"] = 0] = "EqualTo";
@@ -17997,6 +17997,9 @@ ${lanes.join("\n")}
function moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution) {
return moduleResolution >= 3 /* Node16 */ && moduleResolution <= 99 /* NodeNext */ || moduleResolution === 100 /* Bundler */;
}
+ function shouldResolveJsRequire(compilerOptions) {
+ return !!compilerOptions.noDtsResolution || getEmitModuleResolutionKind(compilerOptions) !== 100 /* Bundler */;
+ }
function getResolvePackageJsonExports(compilerOptions) {
const moduleResolution = getEmitModuleResolutionKind(compilerOptions);
if (!moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution)) {
@@ -31698,6 +31701,12 @@ ${lanes.join("\n")}
if (languageVariant === 1 /* JSX */) {
return parseJsxElementOrSelfClosingElementOrFragment(
/*inExpressionContext*/
+ true,
+ /*topInvalidNodePosition*/
+ void 0,
+ /*openingTag*/
+ void 0,
+ /*mustBeUnary*/
true
);
}
@@ -31802,7 +31811,7 @@ ${lanes.join("\n")}
true
)), pos);
}
- function parseJsxElementOrSelfClosingElementOrFragment(inExpressionContext, topInvalidNodePosition, openingTag) {
+ function parseJsxElementOrSelfClosingElementOrFragment(inExpressionContext, topInvalidNodePosition, openingTag, mustBeUnary = false) {
const pos = getNodePos();
const opening = parseJsxOpeningOrSelfClosingElementOrOpeningFragment(inExpressionContext);
let result;
@@ -31840,7 +31849,7 @@ ${lanes.join("\n")}
Debug.assert(opening.kind === 282 /* JsxSelfClosingElement */);
result = opening;
}
- if (inExpressionContext && token() === 29 /* LessThanToken */) {
+ if (!mustBeUnary && inExpressionContext && token() === 29 /* LessThanToken */) {
const topBadPos = typeof topInvalidNodePosition === "undefined" ? result.pos : topInvalidNodePosition;
const invalidElement = tryParse(() => parseJsxElementOrSelfClosingElementOrFragment(
/*inExpressionContext*/
@@ -38075,7 +38084,8 @@ ${lanes.join("\n")}
affectsBuildInfo: true,
category: Diagnostics.Modules,
description: Diagnostics.Allow_imports_to_include_TypeScript_file_extensions_Requires_moduleResolution_bundler_and_either_noEmit_or_emitDeclarationOnly_to_be_set,
- defaultValueDescription: false
+ defaultValueDescription: false,
+ transpileOptionValue: void 0
},
{
name: "resolvePackageJsonExports",
@@ -43773,7 +43783,7 @@ ${lanes.join("\n")}
}
if (!isBindingPattern(node.name)) {
const possibleVariableDecl = node.kind === 257 /* VariableDeclaration */ ? node : node.parent.parent;
- if (isInJSFile(node) && getEmitModuleResolutionKind(options) !== 100 /* Bundler */ && isVariableDeclarationInitializedToBareOrAccessedRequire(possibleVariableDecl) && !getJSDocTypeTag(node) && !(getCombinedModifierFlags(node) & 1 /* Export */)) {
+ if (isInJSFile(node) && shouldResolveJsRequire(options) && isVariableDeclarationInitializedToBareOrAccessedRequire(possibleVariableDecl) && !getJSDocTypeTag(node) && !(getCombinedModifierFlags(node) & 1 /* Export */)) {
declareSymbolAndAddToSymbolTable(node, 2097152 /* Alias */, 2097152 /* AliasExcludes */);
} else if (isBlockOrCatchScoped(node)) {
bindBlockScopedDeclaration(node, 2 /* BlockScopedVariable */, 111551 /* BlockScopedVariableExcludes */);
@@ -47284,7 +47294,7 @@ ${lanes.join("\n")}
const hasDefaultOnly = isOnlyImportedAsDefault(specifier);
const hasSyntheticDefault = canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias, specifier);
if (!exportDefaultSymbol && !hasSyntheticDefault && !hasDefaultOnly) {
- if (hasExportAssignmentSymbol(moduleSymbol) && !(getAllowSyntheticDefaultImports(compilerOptions) || getESModuleInterop(compilerOptions))) {
+ if (hasExportAssignmentSymbol(moduleSymbol) && !allowSyntheticDefaultImports) {
const compilerOptionName = moduleKind >= 5 /* ES2015 */ ? "allowSyntheticDefaultImports" : "esModuleInterop";
const exportEqualsSymbol = moduleSymbol.exports.get("export=" /* ExportEquals */);
const exportAssignment = exportEqualsSymbol.valueDeclaration;
@@ -47452,7 +47462,7 @@ ${lanes.join("\n")}
if (!isIdentifier(name)) {
return void 0;
}
- const suppressInteropError = name.escapedText === "default" /* Default */ && !!(compilerOptions.allowSyntheticDefaultImports || getESModuleInterop(compilerOptions));
+ const suppressInteropError = name.escapedText === "default" /* Default */ && allowSyntheticDefaultImports;
const targetSymbol = resolveESModuleSymbol(
moduleSymbol,
moduleSpecifier,
@@ -52116,7 +52126,7 @@ ${lanes.join("\n")}
return;
}
let verbatimTargetName = isShorthandAmbientModuleSymbol(target) && getSomeTargetNameFromDeclarations(symbol.declarations) || unescapeLeadingUnderscores(target.escapedName);
- if (verbatimTargetName === "export=" /* ExportEquals */ && (getESModuleInterop(compilerOptions) || compilerOptions.allowSyntheticDefaultImports)) {
+ if (verbatimTargetName === "export=" /* ExportEquals */ && allowSyntheticDefaultImports) {
verbatimTargetName = "default" /* Default */;
}
const targetName = getInternalSymbolName(target, verbatimTargetName);
@@ -73215,7 +73225,7 @@ ${lanes.join("\n")}
return anyType;
}
}
- if (isInJSFile(node) && getEmitModuleResolutionKind(compilerOptions) !== 100 /* Bundler */ && isCommonJsRequire(node)) {
+ if (isInJSFile(node) && shouldResolveJsRequire(compilerOptions) && isCommonJsRequire(node)) {
return resolveExternalModuleTypeByLiteral(node.arguments[0]);
}
const returnType = getReturnTypeOfSignature(signature);
@@ -92253,11 +92263,12 @@ ${lanes.join("\n")}
return visitEachChild(node, visitor, context);
}
function visitArrayAssignmentElement(node) {
- Debug.assertNode(node, isArrayBindingOrAssignmentElement);
- if (isSpreadElement(node))
- return visitAssignmentRestElement(node);
- if (!isOmittedExpression(node))
- return visitAssignmentElement(node);
+ if (isArrayBindingOrAssignmentElement(node)) {
+ if (isSpreadElement(node))
+ return visitAssignmentRestElement(node);
+ if (!isOmittedExpression(node))
+ return visitAssignmentElement(node);
+ }
return visitEachChild(node, visitor, context);
}
function visitAssignmentProperty(node) {
@@ -117468,7 +117479,7 @@ ${lanes.join("\n")}
false
);
}
- const shouldProcessRequires = isJavaScriptFile && getEmitModuleResolutionKind(options) !== 100 /* Bundler */;
+ const shouldProcessRequires = isJavaScriptFile && shouldResolveJsRequire(options);
if (file.flags & 2097152 /* PossiblyContainsDynamicImport */ || shouldProcessRequires) {
collectDynamicImportOrRequireCalls(file);
}
@@ -118396,9 +118407,6 @@ ${lanes.join("\n")}
if (moduleKind === 2 /* AMD */ || moduleKind === 3 /* UMD */ || moduleKind === 4 /* System */) {
createDiagnosticForOptionName(Diagnostics.Option_verbatimModuleSyntax_cannot_be_used_when_module_is_set_to_UMD_AMD_or_System, "verbatimModuleSyntax");
}
- if (options.isolatedModules) {
- createRedundantOptionDiagnostic("isolatedModules", "verbatimModuleSyntax");
- }
if (options.preserveValueImports) {
createRedundantOptionDiagnostic("preserveValueImports", "verbatimModuleSyntax");
}
@@ -169808,6 +169816,7 @@ ${options.prefix}` : "\n" : options.prefix
setValueDeclaration: () => setValueDeclaration,
shouldAllowImportingTsExtension: () => shouldAllowImportingTsExtension,
shouldPreserveConstEnums: () => shouldPreserveConstEnums,
+ shouldResolveJsRequire: () => shouldResolveJsRequire,
shouldUseUriStyleNodeCoreModules: () => shouldUseUriStyleNodeCoreModules,
showModuleSpecifier: () => showModuleSpecifier,
signatureHasLiteralTypes: () => signatureHasLiteralTypes,
diff --git a/cli/tsc/compiler.d.ts b/cli/tsc/compiler.d.ts
index b59f6dca81..66c0946972 100644
--- a/cli/tsc/compiler.d.ts
+++ b/cli/tsc/compiler.d.ts
@@ -46,6 +46,8 @@ declare global {
encode(value: string): Uint8Array;
// deno-lint-ignore no-explicit-any
ops: Record any>;
+ // deno-lint-ignore no-explicit-any
+ asyncOps: Record any>;
print(msg: string, stderr: boolean): void;
registerErrorClass(
name: string,
diff --git a/cli/tsc/diagnostics.rs b/cli/tsc/diagnostics.rs
index 1e9819309e..15aadff814 100644
--- a/cli/tsc/diagnostics.rs
+++ b/cli/tsc/diagnostics.rs
@@ -26,7 +26,6 @@ const UNSTABLE_DENO_PROPS: &[&str] = &[
"listen",
"listenDatagram",
"dlopen",
- "ppid",
"removeSignalListener",
"shutdown",
"umask",
diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts
index 4d41aea436..0247eda9c5 100644
--- a/cli/tsc/dts/lib.deno.ns.d.ts
+++ b/cli/tsc/dts/lib.deno.ns.d.ts
@@ -1488,6 +1488,12 @@ declare namespace Deno {
* would resolve to `n` < `p.byteLength`. `write()` must not modify the
* slice data, even temporarily.
*
+ * This function is one of the lowest
+ * level APIs and most users should not work with this directly, but rather use
+ * [`writeAll()`](https://deno.land/std/streams/write_all.ts?s=writeAll) from
+ * [`std/streams/write_all.ts`](https://deno.land/std/streams/write_all.ts)
+ * instead.
+ *
* Implementations should not retain a reference to `p`.
*/
write(p: Uint8Array): Promise;
@@ -1559,7 +1565,7 @@ declare namespace Deno {
*
* It returns the updated offset.
*/
- seekSync(offset: number, whence: SeekMode): number;
+ seekSync(offset: number | bigint, whence: SeekMode): number;
}
/**
@@ -1832,7 +1838,7 @@ declare namespace Deno {
* // Seek 2 more bytes from the current position
* console.log(await Deno.seek(file.rid, 2, Deno.SeekMode.Current)); // "8"
* // Seek backwards 2 bytes from the end of the file
- * console.log(await Deno.seek(file.rid, -2, Deno.SeekMode.End)); // "9" (e.g. 11-2)
+ * console.log(await Deno.seek(file.rid, -2, Deno.SeekMode.End)); // "9" (i.e. 11-2)
* file.close();
* ```
*
@@ -1879,7 +1885,7 @@ declare namespace Deno {
* // Seek 2 more bytes from the current position
* console.log(Deno.seekSync(file.rid, 2, Deno.SeekMode.Current)); // "8"
* // Seek backwards 2 bytes from the end of the file
- * console.log(Deno.seekSync(file.rid, -2, Deno.SeekMode.End)); // "9" (e.g. 11-2)
+ * console.log(Deno.seekSync(file.rid, -2, Deno.SeekMode.End)); // "9" (i.e. 11-2)
* file.close();
* ```
*
@@ -1887,7 +1893,7 @@ declare namespace Deno {
*/
export function seekSync(
rid: number,
- offset: number,
+ offset: number | bigint,
whence: SeekMode,
): number;
@@ -2200,7 +2206,7 @@ declare namespace Deno {
* // Seek 2 more bytes from the current position
* console.log(await file.seek(2, Deno.SeekMode.Current)); // "8"
* // Seek backwards 2 bytes from the end of the file
- * console.log(await file.seek(-2, Deno.SeekMode.End)); // "9" (e.g. 11-2)
+ * console.log(await file.seek(-2, Deno.SeekMode.End)); // "9" (i.e. 11-2)
* ```
*/
seek(offset: number | bigint, whence: SeekMode): Promise;
@@ -2238,7 +2244,7 @@ declare namespace Deno {
* // Seek 2 more bytes from the current position
* console.log(file.seekSync(2, Deno.SeekMode.Current)); // "8"
* // Seek backwards 2 bytes from the end of the file
- * console.log(file.seekSync(-2, Deno.SeekMode.End)); // "9" (e.g. 11-2)
+ * console.log(file.seekSync(-2, Deno.SeekMode.End)); // "9" (i.e. 11-2)
* file.close();
* ```
*/
@@ -3116,6 +3122,22 @@ declare namespace Deno {
*
* _Linux/Mac OS only._ */
blocks: number | null;
+ /** True if this is info for a block device.
+ *
+ * _Linux/Mac OS only._ */
+ isBlockDevice: boolean | null;
+ /** True if this is info for a char device.
+ *
+ * _Linux/Mac OS only._ */
+ isCharDevice: boolean | null;
+ /** True if this is info for a fifo.
+ *
+ * _Linux/Mac OS only._ */
+ isFifo: boolean | null;
+ /** True if this is info for a socket.
+ *
+ * _Linux/Mac OS only._ */
+ isSocket: boolean | null;
}
/** Resolves to the absolute normalized path, with symbolic links resolved.
@@ -3691,7 +3713,10 @@ declare namespace Deno {
options?: { recursive: boolean },
): FsWatcher;
- /** Options which can be used with {@linkcode Deno.run}.
+ /**
+ * @deprecated Use {@linkcode Deno.Command} instead.
+ *
+ * Options which can be used with {@linkcode Deno.run}.
*
* @category Sub Process */
export interface RunOptions {
@@ -3749,7 +3774,10 @@ declare namespace Deno {
stdin?: "inherit" | "piped" | "null" | number;
}
- /** The status resolved from the `.status()` method of a
+ /**
+ * @deprecated Use {@linkcode Deno.Command} instead.
+ *
+ * The status resolved from the `.status()` method of a
* {@linkcode Deno.Process} instance.
*
* If `success` is `true`, then `code` will be `0`, but if `success` is
@@ -3769,6 +3797,8 @@ declare namespace Deno {
};
/**
+ * * @deprecated Use {@linkcode Deno.Command} instead.
+ *
* Represents an instance of a sub process that is returned from
* {@linkcode Deno.run} which can be used to manage the sub-process.
*
@@ -3925,7 +3955,10 @@ declare namespace Deno {
handler: () => void,
): void;
- /** Spawns new subprocess. RunOptions must contain at a minimum the `opt.cmd`,
+ /**
+ * @deprecated Use {@linkcode Deno.Command} instead.
+ *
+ * Spawns new subprocess. RunOptions must contain at a minimum the `opt.cmd`,
* an array of program arguments, the first of which is the binary.
*
* ```ts
@@ -3992,11 +4025,14 @@ declare namespace Deno {
* "console.log('Hello World')",
* ],
* stdin: "piped",
+ * stdout: "piped",
* });
* const child = command.spawn();
*
* // open a file and pipe the subprocess output to it.
- * child.stdout.pipeTo(Deno.openSync("output").writable);
+ * child.stdout.pipeTo(
+ * Deno.openSync("output", { write: true, create: true }).writable,
+ * );
*
* // manually close stdin
* child.stdin.close();
@@ -4033,6 +4069,7 @@ declare namespace Deno {
* console.assert("world\n" === new TextDecoder().decode(stderr));
* ```
*
+ * @tags allow-run
* @category Sub Process
*/
export class Command {
@@ -4203,6 +4240,14 @@ declare namespace Deno {
*
* @default {4} */
depth?: number;
+ /** The maximum length for an inspection to take up a single line.
+ *
+ * @default {80} */
+ breakLength?: number;
+ /** Whether or not to escape sequences.
+ *
+ * @default {true} */
+ escapeSequences?: boolean;
/** The maximum number of iterable entries to print.
*
* @default {100} */
diff --git a/cli/tsc/dts/lib.deno.shared_globals.d.ts b/cli/tsc/dts/lib.deno.shared_globals.d.ts
index d0b44f58af..603cc78f2e 100644
--- a/cli/tsc/dts/lib.deno.shared_globals.d.ts
+++ b/cli/tsc/dts/lib.deno.shared_globals.d.ts
@@ -11,7 +11,6 @@
///
///
///
-///
/** @category WebAssembly */
declare namespace WebAssembly {
@@ -349,7 +348,7 @@ declare namespace WebAssembly {
export function validate(bytes: BufferSource): boolean;
}
-/** Sets a timer which executes a function once after the timer expires. Returns
+/** Sets a timer which executes a function once after the delay (in milliseconds) elapses. Returns
* an id which may be used to cancel the timeout.
*
* ```ts
diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts
index 9c4bd5d2cf..27d3af4cd4 100644
--- a/cli/tsc/dts/lib.deno.unstable.d.ts
+++ b/cli/tsc/dts/lib.deno.unstable.d.ts
@@ -2,6 +2,7 @@
///
///
+///
declare namespace Deno {
export {}; // stop default export type behavior
@@ -97,6 +98,8 @@ declare namespace Deno {
/** **UNSTABLE**: New API, yet to be vetted.
*
* The native struct type for interfacing with foreign functions.
+ *
+ * @category FFI
*/
type NativeStructType = { readonly struct: readonly NativeType[] };
@@ -351,7 +354,9 @@ declare namespace Deno {
: StaticForeignSymbol;
};
+ /** @category FFI */
const brand: unique symbol;
+ /** @category FFI */
type PointerObject = { [brand]: unknown };
/** **UNSTABLE**: New API, yet to be vetted.
@@ -643,8 +648,11 @@ declare namespace Deno {
/**
* This magic code used to implement better type hints for {@linkcode Deno.dlopen}
+ *
+ * @category FFI
*/
type Cast = A extends B ? A : B;
+ /** @category FFI */
type Const = Cast<
T,
| (T extends string | number | bigint | boolean ? T : never)
@@ -813,6 +821,22 @@ declare namespace Deno {
certChain?: string;
/** PEM formatted (RSA or PKCS8) private key of client certificate. */
privateKey?: string;
+ /** Sets the maximum numer of idle connections per host allowed in the pool. */
+ poolMaxIdlePerHost?: number;
+ /** Set an optional timeout for idle sockets being kept-alive.
+ * Set to false to disable the timeout. */
+ poolIdleTimeout?: number | false;
+ /**
+ * Whether HTTP/1.1 is allowed or not.
+ *
+ * @default {true}
+ */
+ http1?: boolean;
+ /** Whether HTTP/2 is allowed or not.
+ *
+ * @default {true}
+ */
+ http2?: boolean;
}
/** **UNSTABLE**: New API, yet to be vetted.
@@ -1295,6 +1319,28 @@ declare namespace Deno {
handler: ServeHandler;
}
+ /** **UNSTABLE**: New API, yet to be vetted.
+ *
+ * @category HTTP Server
+ */
+ export interface Server {
+ /** A promise that resolves once server finishes - eg. when aborted using
+ * the signal passed to {@linkcode ServeOptions.signal}.
+ */
+ finished: Promise;
+
+ /**
+ * Make the server block the event loop from finishing.
+ *
+ * Note: the server blocks the event loop from finishing by default.
+ * This method is only meaningful after `.unref()` is called.
+ */
+ ref(): void;
+
+ /** Make the server not block the event loop from finishing. */
+ unref(): void;
+ }
+
/** **UNSTABLE**: New API, yet to be vetted.
*
* Serves HTTP requests with the given handler.
@@ -1323,8 +1369,11 @@ declare namespace Deno {
* ```ts
* const ac = new AbortController();
*
- * Deno.serve({ signal: ac.signal }, (_req) => new Response("Hello, world"))
- * .then(() => console.log("Server closed"));
+ * const server = Deno.serve(
+ * { signal: ac.signal },
+ * (_req) => new Response("Hello, world")
+ * );
+ * server.finished.then(() => console.log("Server closed"));
*
* console.log("Closing server...");
* ac.abort();
@@ -1354,10 +1403,7 @@ declare namespace Deno {
*
* @category HTTP Server
*/
- export function serve(
- handler: ServeHandler,
- options?: ServeOptions | ServeTlsOptions,
- ): Promise;
+ export function serve(handler: ServeHandler): Server;
/** **UNSTABLE**: New API, yet to be vetted.
*
* Serves HTTP requests with the given handler.
@@ -1386,8 +1432,11 @@ declare namespace Deno {
* ```ts
* const ac = new AbortController();
*
- * Deno.serve({ signal: ac.signal }, (_req) => new Response("Hello, world"))
- * .then(() => console.log("Server closed"));
+ * const server = Deno.serve(
+ * { signal: ac.signal },
+ * (_req) => new Response("Hello, world")
+ * );
+ * server.finished.then(() => console.log("Server closed"));
*
* console.log("Closing server...");
* ac.abort();
@@ -1420,7 +1469,7 @@ declare namespace Deno {
export function serve(
options: ServeOptions | ServeTlsOptions,
handler: ServeHandler,
- ): Promise;
+ ): Server;
/** **UNSTABLE**: New API, yet to be vetted.
*
* Serves HTTP requests with the given handler.
@@ -1449,8 +1498,11 @@ declare namespace Deno {
* ```ts
* const ac = new AbortController();
*
- * Deno.serve({ signal: ac.signal }, (_req) => new Response("Hello, world"))
- * .then(() => console.log("Server closed"));
+ * const server = Deno.serve(
+ * { signal: ac.signal },
+ * (_req) => new Response("Hello, world")
+ * );
+ * server.finished.then(() => console.log("Server closed"));
*
* console.log("Closing server...");
* ac.abort();
@@ -1482,7 +1534,7 @@ declare namespace Deno {
*/
export function serve(
options: ServeInit & (ServeOptions | ServeTlsOptions),
- ): Promise;
+ ): Server;
/** **UNSTABLE**: New API, yet to be vetted.
*
@@ -1512,25 +1564,6 @@ declare namespace Deno {
request: Request,
): Promise<[Deno.Conn, Uint8Array]>;
- /** **UNSTABLE**: New API, yet to be vetted.
- *
- * Allows "hijacking" the connection that the request is associated with.
- * This can be used to implement protocols that build on top of HTTP (eg.
- * {@linkcode WebSocket}).
- *
- * Unlike {@linkcode Deno.upgradeHttp} this function does not require that you
- * respond to the request with a {@linkcode Response} object. Instead this
- * function returns the underlying connection and first packet received
- * immediately, and then the caller is responsible for writing the response to
- * the connection.
- *
- * This method can only be called on requests originating the
- * {@linkcode Deno.serve} server.
- *
- * @category HTTP Server
- */
- export function upgradeHttpRaw(request: Request): [Deno.Conn, Uint8Array];
-
/** **UNSTABLE**: New API, yet to be vetted.
*
* Open a new {@linkcode Deno.Kv} connection to persist data.
@@ -1560,6 +1593,10 @@ declare namespace Deno {
* relative significance of the types can be found in documentation for the
* {@linkcode Deno.KvKeyPart} type.
*
+ * Keys have a maximum size of 2048 bytes serialized. If the size of the key
+ * exceeds this limit, an error will be thrown on the operation that this key
+ * was passed to.
+ *
* @category KV
*/
export type KvKey = readonly KvKeyPart[];
@@ -1642,7 +1679,8 @@ declare namespace Deno {
* - `sum` - Adds the given value to the existing value of the key. Both the
* value specified in the mutation, and any existing value must be of type
* `Deno.KvU64`. If the key does not exist, the value is set to the given
- * value (summed with 0).
+ * value (summed with 0). If the result of the sum overflows an unsigned
+ * 64-bit integer, the result is wrapped around.
* - `max` - Sets the value of the key to the maximum of the existing value
* and the given value. Both the value specified in the mutation, and any
* existing value must be of type `Deno.KvU64`. If the key does not exist,
@@ -1770,11 +1808,18 @@ declare namespace Deno {
batchSize?: number;
}
+ /** @category KV */
export interface KvCommitResult {
+ ok: true;
/** The versionstamp of the value committed to KV. */
versionstamp: string;
}
+ /** @category KV */
+ export interface KvCommitError {
+ ok: false;
+ }
+
/** **UNSTABLE**: New API, yet to be vetted.
*
* A check to perform as part of a {@linkcode Deno.AtomicOperation}. The check
@@ -1816,11 +1861,13 @@ declare namespace Deno {
*
* The `commit` method of an atomic operation returns a value indicating
* whether checks passed and mutations were performed. If the operation failed
- * because of a failed check, the return value will be `null`. If the
+ * because of a failed check, the return value will be a
+ * {@linkcode Deno.KvCommitError} with an `ok: false` property. If the
* operation failed for any other reason (storage error, invalid value, etc.),
* an exception will be thrown. If the operation succeeded, the return value
- * will be a {@linkcode Deno.KvCommitResult} object containing the
- * versionstamp of the value committed to KV.
+ * will be a {@linkcode Deno.KvCommitResult} object with a `ok: true` property
+ * and the versionstamp of the value committed to KV.
+
*
* @category KV
*/
@@ -1840,9 +1887,23 @@ declare namespace Deno {
*/
mutate(...mutations: KvMutation[]): this;
/**
- * Shortcut for creating a sum mutation.
+ * Shortcut for creating a `sum` mutation. This method wraps `n` in a
+ * {@linkcode Deno.KvU64}, so the value of `n` must be in the range
+ * `[0, 2^64-1]`.
*/
sum(key: KvKey, n: bigint): this;
+ /**
+ * Shortcut for creating a `min` mutation. This method wraps `n` in a
+ * {@linkcode Deno.KvU64}, so the value of `n` must be in the range
+ * `[0, 2^64-1]`.
+ */
+ min(key: KvKey, n: bigint): this;
+ /**
+ * Shortcut for creating a `max` mutation. This method wraps `n` in a
+ * {@linkcode Deno.KvU64}, so the value of `n` must be in the range
+ * `[0, 2^64-1]`.
+ */
+ max(key: KvKey, n: bigint): this;
/**
* Add to the operation a mutation that sets the value of the specified key
* to the specified value if all checks pass during the commit.
@@ -1856,17 +1917,19 @@ declare namespace Deno {
/**
* Commit the operation to the KV store. Returns a value indicating whether
* checks passed and mutations were performed. If the operation failed
- * because of a failed check, the return value will be `null`. If the
- * operation failed for any other reason (storage error, invalid value,
- * etc.), an exception will be thrown. If the operation succeeded, the
- * return value will be a {@linkcode Deno.KvCommitResult} object containing
- * the versionstamp of the value committed to KV.
+ * because of a failed check, the return value will be a {@linkcode
+ * Deno.KvCommitError} with an `ok: false` property. If the operation failed
+ * for any other reason (storage error, invalid value, etc.), an exception
+ * will be thrown. If the operation succeeded, the return value will be a
+ * {@linkcode Deno.KvCommitResult} object with a `ok: true` property and the
+ * versionstamp of the value committed to KV.
*
- * If the commit returns `null`, one may create a new atomic operation with
- * updated checks and mutations and attempt to commit it again. See the note
- * on optimistic locking in the documentation for {@linkcode Deno.AtomicOperation}.
+ * If the commit returns `ok: false`, one may create a new atomic operation
+ * with updated checks and mutations and attempt to commit it again. See the
+ * note on optimistic locking in the documentation for
+ * {@linkcode Deno.AtomicOperation}.
*/
- commit(): Promise;
+ commit(): Promise;
}
/** **UNSTABLE**: New API, yet to be vetted.
@@ -1900,7 +1963,8 @@ declare namespace Deno {
* maximum length of 64 KiB after serialization. Serialization of both keys
* and values is somewhat opaque, but one can usually assume that the
* serialization of any value is about the same length as the resulting string
- * of a JSON serialization of that same value.
+ * of a JSON serialization of that same value. If theses limits are exceeded,
+ * an exception will be thrown.
*
* @category KV
*/
@@ -2034,10 +2098,10 @@ declare namespace Deno {
/**
* Close the database connection. This will prevent any further operations
- * from being performed on the database, but will wait for any in-flight
- * operations to complete before closing the underlying database connection.
+ * from being performed on the database, and interrupt any in-flight
+ * operations immediately.
*/
- close(): Promise;
+ close(): void;
}
/** **UNSTABLE**: New API, yet to be vetted.
diff --git a/cli/tsc/dts/lib.dom.extras.d.ts b/cli/tsc/dts/lib.dom.extras.d.ts
index 2c593b2cbe..9116596a6a 100644
--- a/cli/tsc/dts/lib.dom.extras.d.ts
+++ b/cli/tsc/dts/lib.dom.extras.d.ts
@@ -23,7 +23,7 @@ declare type URLPatternInput = string | URLPatternInit;
declare interface URLPatternComponentResult {
input: string;
- groups: Record;
+ groups: Record;
}
/** `URLPatternResult` is the object returned from `URLPattern.exec`. */
diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs
index 57a4a1be83..83fd84f9dc 100644
--- a/cli/tsc/mod.rs
+++ b/cli/tsc/mod.rs
@@ -4,8 +4,6 @@ use crate::args::TsConfig;
use crate::args::TypeCheckMode;
use crate::cache::FastInsecureHasher;
use crate::node;
-use crate::node::CliNodeResolver;
-use crate::node::NodeResolution;
use crate::util::checksum;
use crate::util::path::mapped_specifier_for_tsc;
@@ -33,7 +31,10 @@ use deno_core::Snapshot;
use deno_graph::Module;
use deno_graph::ModuleGraph;
use deno_graph::ResolutionResolved;
+use deno_runtime::deno_node;
+use deno_runtime::deno_node::NodeResolution;
use deno_runtime::deno_node::NodeResolutionMode;
+use deno_runtime::deno_node::NodeResolver;
use deno_runtime::permissions::PermissionsContainer;
use deno_semver::npm::NpmPackageReqReference;
use lsp_types::Url;
@@ -116,13 +117,7 @@ pub fn get_types_declaration_file_text(unstable: bool) -> String {
}
fn get_asset_texts_from_new_runtime() -> Result, AnyError> {
- deno_core::extension!(
- deno_cli_tsc,
- ops_fn = deno_ops,
- customizer = |ext: &mut deno_core::ExtensionBuilder| {
- ext.force_op_registration();
- },
- );
+ deno_core::extension!(deno_cli_tsc, ops_fn = deno_ops);
// the assets are stored within the typescript isolate, so take them out of there
let mut runtime = JsRuntime::new(RuntimeOptions {
@@ -305,7 +300,7 @@ pub struct Request {
pub debug: bool,
pub graph: Arc,
pub hash_data: u64,
- pub maybe_node_resolver: Option>,
+ pub maybe_node_resolver: Option>,
pub maybe_tsbuildinfo: Option,
/// A vector of strings that represent the root/entry point modules for the
/// program.
@@ -329,7 +324,7 @@ struct State {
graph: Arc,
maybe_tsbuildinfo: Option,
maybe_response: Option,
- maybe_node_resolver: Option>,
+ maybe_node_resolver: Option>,
remapped_specifiers: HashMap,
root_map: HashMap,
current_dir: PathBuf,
@@ -339,7 +334,7 @@ impl State {
pub fn new(
graph: Arc,
hash_data: u64,
- maybe_node_resolver: Option>,
+ maybe_node_resolver: Option>,
maybe_tsbuildinfo: Option,
root_map: HashMap,
remapped_specifiers: HashMap,
@@ -537,7 +532,7 @@ fn op_resolve(
};
for specifier in args.specifiers {
if let Some(module_name) = specifier.strip_prefix("node:") {
- if crate::node::resolve_builtin_node_module(module_name).is_ok() {
+ if deno_node::is_builtin_node_module(module_name) {
// return itself for node: specifiers because during type checking
// we resolve to the ambient modules in the @types/node package
// rather than deno_std/node
@@ -638,7 +633,7 @@ fn resolve_graph_specifier_types(
let maybe_resolution = node_resolver.resolve_npm_reference(
&module.nv_reference,
NodeResolutionMode::Types,
- &mut PermissionsContainer::allow_all(),
+ &PermissionsContainer::allow_all(),
)?;
Ok(Some(NodeResolution::into_specifier_and_media_type(
maybe_resolution,
@@ -678,7 +673,7 @@ fn resolve_non_graph_specifier_types(
specifier,
referrer,
NodeResolutionMode::Types,
- &mut PermissionsContainer::allow_all(),
+ &PermissionsContainer::allow_all(),
)
.ok()
.flatten(),
@@ -691,7 +686,7 @@ fn resolve_non_graph_specifier_types(
let maybe_resolution = node_resolver.resolve_npm_req_reference(
&npm_ref,
NodeResolutionMode::Types,
- &mut PermissionsContainer::allow_all(),
+ &PermissionsContainer::allow_all(),
)?;
Ok(Some(NodeResolution::into_specifier_and_media_type(
maybe_resolution,
@@ -779,9 +774,6 @@ pub fn exec(request: Request) -> Result {
.unwrap(),
));
},
- customizer = |ext: &mut deno_core::ExtensionBuilder| {
- ext.force_op_registration();
- },
);
let startup_source = ascii_str!("globalThis.startup({ legacyFlag: false })");
@@ -847,6 +839,7 @@ mod tests {
use crate::args::TsConfig;
use deno_core::futures::future;
use deno_core::OpState;
+ use deno_graph::GraphKind;
use deno_graph::ModuleGraph;
use std::fs;
@@ -890,7 +883,7 @@ mod tests {
let hash_data = maybe_hash_data.unwrap_or(0);
let fixtures = test_util::testdata_path().join("tsc2");
let mut loader = MockLoader { fixtures };
- let mut graph = ModuleGraph::default();
+ let mut graph = ModuleGraph::new(GraphKind::TypesOnly);
graph
.build(vec![specifier], &mut loader, Default::default())
.await;
@@ -916,7 +909,7 @@ mod tests {
let hash_data = 123; // something random
let fixtures = test_util::testdata_path().join("tsc2");
let mut loader = MockLoader { fixtures };
- let mut graph = ModuleGraph::default();
+ let mut graph = ModuleGraph::new(GraphKind::TypesOnly);
graph
.build(vec![specifier.clone()], &mut loader, Default::default())
.await;
diff --git a/cli/util/draw_thread.rs b/cli/util/draw_thread.rs
index 028b20d00e..2fd81a78ab 100644
--- a/cli/util/draw_thread.rs
+++ b/cli/util/draw_thread.rs
@@ -2,6 +2,7 @@
use console_static_text::ConsoleStaticText;
use deno_core::parking_lot::Mutex;
+use deno_core::task::spawn_blocking;
use deno_runtime::ops::tty::ConsoleSize;
use once_cell::sync::Lazy;
use std::sync::Arc;
@@ -162,7 +163,7 @@ impl DrawThread {
internal_state.has_draw_thread = true;
let drawer_id = internal_state.drawer_id;
- tokio::task::spawn_blocking(move || {
+ spawn_blocking(move || {
let mut previous_size = console_size();
loop {
let mut delay_ms = 120;
diff --git a/cli/util/file_watcher.rs b/cli/util/file_watcher.rs
index 05415f2a63..1ad5e9ba07 100644
--- a/cli/util/file_watcher.rs
+++ b/cli/util/file_watcher.rs
@@ -304,6 +304,13 @@ where
}
loop {
+ // We may need to give the runtime a tick to settle, as cancellations may need to propagate
+ // to tasks. We choose yielding 10 times to the runtime as a decent heuristic. If watch tests
+ // start to fail, this may need to be increased.
+ for _ in 0..10 {
+ tokio::task::yield_now().await;
+ }
+
let mut watcher = new_watcher(watcher_sender.clone())?;
consume_paths_to_watch(&mut watcher, &mut paths_to_watch_receiver);
diff --git a/cli/util/fs.rs b/cli/util/fs.rs
index 7cfd0ced79..658002e3b6 100644
--- a/cli/util/fs.rs
+++ b/cli/util/fs.rs
@@ -3,6 +3,7 @@
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
pub use deno_core::normalize_path;
+use deno_core::task::spawn_blocking;
use deno_core::ModuleSpecifier;
use deno_runtime::deno_crypto::rand;
use deno_runtime::deno_node::PathClean;
@@ -81,11 +82,7 @@ pub fn write_file_2>(
/// Similar to `std::fs::canonicalize()` but strips UNC prefixes on Windows.
pub fn canonicalize_path(path: &Path) -> Result {
- let path = path.canonicalize()?;
- #[cfg(windows)]
- return Ok(strip_unc_prefix(path));
- #[cfg(not(windows))]
- return Ok(path);
+ Ok(deno_core::strip_unc_prefix(path.canonicalize()?))
}
/// Canonicalizes a path which might be non-existent by going up the
@@ -96,12 +93,19 @@ pub fn canonicalize_path(path: &Path) -> Result {
/// subsequently be created along this path by some other code.
pub fn canonicalize_path_maybe_not_exists(
path: &Path,
+) -> Result {
+ canonicalize_path_maybe_not_exists_with_fs(path, canonicalize_path)
+}
+
+pub fn canonicalize_path_maybe_not_exists_with_fs(
+ path: &Path,
+ canonicalize: impl Fn(&Path) -> Result,
) -> Result {
let path = path.to_path_buf().clean();
let mut path = path.as_path();
let mut names_stack = Vec::new();
loop {
- match canonicalize_path(path) {
+ match canonicalize(path) {
Ok(mut canonicalized_path) => {
for name in names_stack.into_iter().rev() {
canonicalized_path = canonicalized_path.join(name);
@@ -117,47 +121,6 @@ pub fn canonicalize_path_maybe_not_exists(
}
}
-#[cfg(windows)]
-fn strip_unc_prefix(path: PathBuf) -> PathBuf {
- use std::path::Component;
- use std::path::Prefix;
-
- let mut components = path.components();
- match components.next() {
- Some(Component::Prefix(prefix)) => {
- match prefix.kind() {
- // \\?\device
- Prefix::Verbatim(device) => {
- let mut path = PathBuf::new();
- path.push(format!(r"\\{}\", device.to_string_lossy()));
- path.extend(components.filter(|c| !matches!(c, Component::RootDir)));
- path
- }
- // \\?\c:\path
- Prefix::VerbatimDisk(_) => {
- let mut path = PathBuf::new();
- path.push(prefix.as_os_str().to_string_lossy().replace(r"\\?\", ""));
- path.extend(components);
- path
- }
- // \\?\UNC\hostname\share_name\path
- Prefix::VerbatimUNC(hostname, share_name) => {
- let mut path = PathBuf::new();
- path.push(format!(
- r"\\{}\{}\",
- hostname.to_string_lossy(),
- share_name.to_string_lossy()
- ));
- path.extend(components.filter(|c| !matches!(c, Component::RootDir)));
- path
- }
- _ => path,
- }
- }
- _ => path,
- }
-}
-
pub fn resolve_from_cwd(path: &Path) -> Result {
let resolved_path = if path.is_absolute() {
path.to_owned()
@@ -541,7 +504,7 @@ impl LaxSingleProcessFsFlag {
// This uses a blocking task because we use a single threaded
// runtime and this is time sensitive so we don't want it to update
// at the whims of of whatever is occurring on the runtime thread.
- tokio::task::spawn_blocking({
+ spawn_blocking({
let token = token.clone();
let last_updated_path = last_updated_path.clone();
move || {
@@ -921,41 +884,6 @@ mod tests {
assert_eq!(result, expected);
}
- #[cfg(windows)]
- #[test]
- fn test_strip_unc_prefix() {
- run_test(r"C:\", r"C:\");
- run_test(r"C:\test\file.txt", r"C:\test\file.txt");
-
- run_test(r"\\?\C:\", r"C:\");
- run_test(r"\\?\C:\test\file.txt", r"C:\test\file.txt");
-
- run_test(r"\\.\C:\", r"\\.\C:\");
- run_test(r"\\.\C:\Test\file.txt", r"\\.\C:\Test\file.txt");
-
- run_test(r"\\?\UNC\localhost\", r"\\localhost");
- run_test(r"\\?\UNC\localhost\c$\", r"\\localhost\c$");
- run_test(
- r"\\?\UNC\localhost\c$\Windows\file.txt",
- r"\\localhost\c$\Windows\file.txt",
- );
- run_test(r"\\?\UNC\wsl$\deno.json", r"\\wsl$\deno.json");
-
- run_test(r"\\?\server1", r"\\server1");
- run_test(r"\\?\server1\e$\", r"\\server1\e$\");
- run_test(
- r"\\?\server1\e$\test\file.txt",
- r"\\server1\e$\test\file.txt",
- );
-
- fn run_test(input: &str, expected: &str) {
- assert_eq!(
- strip_unc_prefix(PathBuf::from(input)),
- PathBuf::from(expected)
- );
- }
- }
-
#[tokio::test]
async fn lax_fs_lock() {
let temp_dir = TempDir::new();
diff --git a/cli/util/v8.rs b/cli/util/v8.rs
index 6afaf285e3..93a2ef83f7 100644
--- a/cli/util/v8.rs
+++ b/cli/util/v8.rs
@@ -10,17 +10,26 @@ pub fn get_v8_flags_from_env() -> Vec {
#[inline(always)]
pub fn construct_v8_flags(
+ default_v8_flags: &[String],
v8_flags: &[String],
env_v8_flags: Vec,
) -> Vec {
std::iter::once("UNUSED_BUT_NECESSARY_ARG0".to_owned())
+ .chain(default_v8_flags.iter().cloned())
.chain(env_v8_flags.into_iter())
.chain(v8_flags.iter().cloned())
.collect::>()
}
-pub fn init_v8_flags(v8_flags: &[String], env_v8_flags: Vec) {
- if v8_flags.is_empty() && env_v8_flags.is_empty() {
+pub fn init_v8_flags(
+ default_v8_flags: &[String],
+ v8_flags: &[String],
+ env_v8_flags: Vec,
+) {
+ if default_v8_flags.is_empty()
+ && v8_flags.is_empty()
+ && env_v8_flags.is_empty()
+ {
return;
}
@@ -29,7 +38,7 @@ pub fn init_v8_flags(v8_flags: &[String], env_v8_flags: Vec) {
.chain(v8_flags)
.any(|flag| flag == "-help" || flag == "--help");
// Keep in sync with `standalone.rs`.
- let v8_flags = construct_v8_flags(v8_flags, env_v8_flags);
+ let v8_flags = construct_v8_flags(default_v8_flags, v8_flags, env_v8_flags);
let unrecognized_v8_flags = deno_core::v8_set_flags(v8_flags)
.into_iter()
.skip(1)
diff --git a/cli/watcher.rs b/cli/watcher.rs
new file mode 100644
index 0000000000..f9c2c1b42d
--- /dev/null
+++ b/cli/watcher.rs
@@ -0,0 +1,99 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use crate::args::CliOptions;
+use crate::cache::ParsedSourceCache;
+use crate::graph_util::ModuleGraphContainer;
+use crate::module_loader::CjsResolutionStore;
+
+use deno_core::parking_lot::Mutex;
+use deno_core::ModuleSpecifier;
+
+use std::path::PathBuf;
+use std::sync::Arc;
+
+pub struct FileWatcher {
+ cli_options: Arc,
+ cjs_resolutions: Arc,
+ graph_container: Arc,
+ maybe_reporter: Option,
+ parsed_source_cache: Arc,
+}
+
+impl FileWatcher {
+ pub fn new(
+ cli_options: Arc,
+ cjs_resolutions: Arc,
+ graph_container: Arc,
+ maybe_reporter: Option,
+ parsed_source_cache: Arc,
+ ) -> Self {
+ Self {
+ cli_options,
+ cjs_resolutions,
+ parsed_source_cache,
+ graph_container,
+ maybe_reporter,
+ }
+ }
+ /// Reset all runtime state to its default. This should be used on file
+ /// watcher restarts.
+ pub fn reset(&self) {
+ self.cjs_resolutions.clear();
+ self.parsed_source_cache.clear();
+ self.graph_container.clear();
+
+ self.init_watcher();
+ }
+
+ // Add invariant files like the import map and explicit watch flag list to
+ // the watcher. Dedup for build_for_file_watcher and reset_for_file_watcher.
+ pub fn init_watcher(&self) {
+ let files_to_watch_sender = match &self.maybe_reporter {
+ Some(reporter) => &reporter.sender,
+ None => return,
+ };
+ if let Some(watch_paths) = self.cli_options.watch_paths() {
+ files_to_watch_sender.send(watch_paths.clone()).unwrap();
+ }
+ if let Ok(Some(import_map_path)) = self
+ .cli_options
+ .resolve_import_map_specifier()
+ .map(|ms| ms.and_then(|ref s| s.to_file_path().ok()))
+ {
+ files_to_watch_sender.send(vec![import_map_path]).unwrap();
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct FileWatcherReporter {
+ sender: tokio::sync::mpsc::UnboundedSender>,
+ file_paths: Arc>>,
+}
+
+impl FileWatcherReporter {
+ pub fn new(sender: tokio::sync::mpsc::UnboundedSender>) -> Self {
+ Self {
+ sender,
+ file_paths: Default::default(),
+ }
+ }
+}
+
+impl deno_graph::source::Reporter for FileWatcherReporter {
+ fn on_load(
+ &self,
+ specifier: &ModuleSpecifier,
+ modules_done: usize,
+ modules_total: usize,
+ ) {
+ let mut file_paths = self.file_paths.lock();
+ if specifier.scheme() == "file" {
+ file_paths.push(specifier.to_file_path().unwrap());
+ }
+
+ if modules_done == modules_total {
+ self.sender.send(file_paths.drain(..).collect()).unwrap();
+ }
+ }
+}
diff --git a/cli/worker.rs b/cli/worker.rs
index 7ee8fc8021..b8bb6e9416 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -5,15 +5,30 @@ use std::rc::Rc;
use std::sync::Arc;
use deno_ast::ModuleSpecifier;
+use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::futures::task::LocalFutureObj;
use deno_core::futures::FutureExt;
use deno_core::located_script_name;
+use deno_core::parking_lot::Mutex;
+use deno_core::url::Url;
+use deno_core::CompiledWasmModuleStore;
use deno_core::Extension;
use deno_core::ModuleId;
+use deno_core::ModuleLoader;
+use deno_core::SharedArrayBufferStore;
+use deno_core::SourceMapGetter;
+use deno_lockfile::Lockfile;
use deno_runtime::colors;
+use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel;
+use deno_runtime::deno_fs;
use deno_runtime::deno_node;
+use deno_runtime::deno_node::NodeResolution;
+use deno_runtime::deno_node::NodeResolver;
+use deno_runtime::deno_tls::RootCertStoreProvider;
+use deno_runtime::deno_web::BlobStore;
use deno_runtime::fmt_errors::format_js_error;
+use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::ops::worker_host::CreateWebWorkerCb;
use deno_runtime::ops::worker_host::WorkerEventCb;
use deno_runtime::permissions::PermissionsContainer;
@@ -22,24 +37,89 @@ use deno_runtime::web_worker::WebWorkerOptions;
use deno_runtime::worker::MainWorker;
use deno_runtime::worker::WorkerOptions;
use deno_runtime::BootstrapOptions;
+use deno_runtime::WorkerLogLevel;
use deno_semver::npm::NpmPackageReqReference;
-use crate::args::DenoSubcommand;
+use crate::args::StorageKeyResolver;
use crate::errors;
-use crate::module_loader::CliModuleLoader;
-use crate::node;
+use crate::npm::CliNpmResolver;
use crate::ops;
-use crate::proc_state::ProcState;
use crate::tools;
use crate::tools::coverage::CoverageCollector;
use crate::util::checksum;
use crate::version;
+pub trait ModuleLoaderFactory: Send + Sync {
+ fn create_for_main(
+ &self,
+ root_permissions: PermissionsContainer,
+ dynamic_permissions: PermissionsContainer,
+ ) -> Rc;
+
+ fn create_for_worker(
+ &self,
+ root_permissions: PermissionsContainer,
+ dynamic_permissions: PermissionsContainer,
+ ) -> Rc;
+
+ fn create_source_map_getter(&self) -> Option>;
+}
+
+// todo(dsherret): this is temporary and we should remove this
+// once we no longer conditionally initialize the node runtime
+pub trait HasNodeSpecifierChecker: Send + Sync {
+ fn has_node_specifier(&self) -> bool;
+}
+
+#[derive(Clone)]
+pub struct CliMainWorkerOptions {
+ pub argv: Vec,
+ pub log_level: WorkerLogLevel,
+ pub coverage_dir: Option,
+ pub enable_testing_features: bool,
+ pub has_node_modules_dir: bool,
+ pub inspect_brk: bool,
+ pub inspect_wait: bool,
+ pub is_inspecting: bool,
+ pub is_npm_main: bool,
+ pub location: Option,
+ pub maybe_binary_npm_command_name: Option,
+ pub origin_data_folder_path: Option,
+ pub seed: Option,
+ pub unsafely_ignore_certificate_errors: Option>,
+ pub unstable: bool,
+}
+
+struct SharedWorkerState {
+ options: CliMainWorkerOptions,
+ storage_key_resolver: StorageKeyResolver,
+ npm_resolver: Arc,
+ node_resolver: Arc,
+ has_node_specifier_checker: Box,
+ blob_store: BlobStore,
+ broadcast_channel: InMemoryBroadcastChannel,
+ shared_array_buffer_store: SharedArrayBufferStore,
+ compiled_wasm_module_store: CompiledWasmModuleStore,
+ module_loader_factory: Box,
+ root_cert_store_provider: Arc,
+ fs: Arc,
+ maybe_inspector_server: Option>,
+ maybe_lockfile: Option>>,
+}
+
+impl SharedWorkerState {
+ pub fn should_initialize_node_runtime(&self) -> bool {
+ self.npm_resolver.has_packages()
+ || self.has_node_specifier_checker.has_node_specifier()
+ || self.options.is_npm_main
+ }
+}
+
pub struct CliMainWorker {
main_module: ModuleSpecifier,
is_main_cjs: bool,
worker: MainWorker,
- ps: ProcState,
+ shared: Arc,
}
impl CliMainWorker {
@@ -63,7 +143,7 @@ impl CliMainWorker {
&mut self.worker.js_runtime,
&self.main_module.to_file_path().unwrap().to_string_lossy(),
true,
- self.ps.options.inspect_brk().is_some(),
+ self.shared.options.inspect_brk,
)?;
} else {
self.execute_main_module_possibly_with_npm().await?;
@@ -184,33 +264,17 @@ impl CliMainWorker {
&mut self,
id: ModuleId,
) -> Result<(), AnyError> {
- if self.ps.npm_resolver.has_packages()
- || self.ps.graph_container.graph().has_node_specifier
- {
+ if self.shared.should_initialize_node_runtime() {
self.initialize_main_module_for_node()?;
}
self.worker.evaluate_module(id).await
}
fn initialize_main_module_for_node(&mut self) -> Result<(), AnyError> {
- let mut maybe_binary_command_name = None;
-
- if let DenoSubcommand::Run(flags) = self.ps.options.sub_command() {
- if let Ok(pkg_ref) = NpmPackageReqReference::from_str(&flags.script) {
- // if the user ran a binary command, we'll need to set process.argv[0]
- // to be the name of the binary command instead of deno
- let binary_name = pkg_ref
- .sub_path
- .as_deref()
- .unwrap_or(pkg_ref.req.name.as_str());
- maybe_binary_command_name = Some(binary_name.to_string());
- }
- }
-
deno_node::initialize_runtime(
&mut self.worker.js_runtime,
- self.ps.options.has_node_modules_dir(),
- maybe_binary_command_name,
+ self.shared.options.has_node_modules_dir,
+ self.shared.options.maybe_binary_npm_command_name.as_deref(),
)?;
Ok(())
@@ -219,7 +283,7 @@ impl CliMainWorker {
pub async fn maybe_setup_coverage_collector(
&mut self,
) -> Result]