diff --git a/.dprintrc.json b/.dprintrc.json index 7450eefc5f..e284045c11 100644 --- a/.dprintrc.json +++ b/.dprintrc.json @@ -24,12 +24,9 @@ "cli/tests/inline_js_source_map*", "cli/tests/badly_formatted.md", "cli/tsc/*typescript.js", + "test_util/std", "test_util/wpt", "gh-pages", - "std/**/testdata", - "std/**/vendor", - "std/node_modules", - "std/hash/_wasm", "target", "third_party", "tools/wpt/expectation.json", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d5b8a73e5..05a850cbf7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,8 +57,7 @@ jobs: startsWith(matrix.os, 'ubuntu') && matrix.kind == 'test_release' && github.repository == 'denoland/deno' && - startsWith(github.ref, 'refs/tags/') && - !startsWith(github.ref, 'refs/tags/std/') + startsWith(github.ref, 'refs/tags/') run: | mkdir -p target/release tar --exclude=.cargo_home --exclude=".git*" --exclude=target --exclude=third_party/prebuilt -czvf target/release/deno_src.tar.gz -C .. deno @@ -112,9 +111,8 @@ jobs: runner.os != 'Windows' && matrix.kind == 'test_release' && github.repository == 'denoland/deno' && - (github.ref == 'refs/heads/master' || - startsWith(github.ref, 'refs/tags/') && - !startsWith(github.ref, 'refs/tags/std/')) + github.ref == 'refs/heads/master' || + startsWith(github.ref, 'refs/tags/') uses: google-github-actions/setup-gcloud@master with: project_id: denoland @@ -126,9 +124,8 @@ jobs: runner.os == 'Windows' && matrix.kind == 'test_release' && github.repository == 'denoland/deno' && - (github.ref == 'refs/heads/master' || - startsWith(github.ref, 'refs/tags/') && - !startsWith(github.ref, 'refs/tags/std/')) + github.ref == 'refs/heads/master' || + startsWith(github.ref, 'refs/tags/') uses: google-github-actions/setup-gcloud@master env: CLOUDSDK_PYTHON: ${{env.pythonLocation}}\python.exe @@ -288,8 +285,7 @@ jobs: runner.os != 'Windows' && matrix.kind == 'test_release' && github.repository == 'denoland/deno' && - startsWith(github.ref, 'refs/tags/') && - !startsWith(github.ref, 'refs/tags/std/') + startsWith(github.ref, 'refs/tags/') run: | gsutil cp ./target/release/*.zip gs://dl.deno.land/release/${GITHUB_REF#refs/*/}/ echo ${GITHUB_REF#refs/*/} > release-latest.txt @@ -300,8 +296,7 @@ jobs: runner.os == 'Windows' && matrix.kind == 'test_release' && github.repository == 'denoland/deno' && - startsWith(github.ref, 'refs/tags/') && - !startsWith(github.ref, 'refs/tags/std/') + startsWith(github.ref, 'refs/tags/') env: CLOUDSDK_PYTHON: ${{env.pythonLocation}}\python.exe shell: bash @@ -315,8 +310,7 @@ jobs: if: | matrix.kind == 'test_release' && github.repository == 'denoland/deno' && - startsWith(github.ref, 'refs/tags/') && - !startsWith(github.ref, 'refs/tags/std/') + startsWith(github.ref, 'refs/tags/') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.gitmodules b/.gitmodules index 4b7c999975..81a678bad6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,11 +2,11 @@ path = third_party url = https://github.com/denoland/deno_third_party.git shallow = true -[submodule "std/wasi/testdata"] - path = std/wasi/testdata - url = https://github.com/khronosproject/wasi-test-suite.git - shallow = true [submodule "test_util/wpt"] path = test_util/wpt url = https://github.com/denoland/wpt.git shallow = true +[submodule "test_util/std"] + path = test_util/std + url = https://github.com/denoland/deno_std + shallow = true diff --git a/cli/bench/deno_http_proxy.ts b/cli/bench/deno_http_proxy.ts index 9900148310..fc756f80fd 100644 --- a/cli/bench/deno_http_proxy.ts +++ b/cli/bench/deno_http_proxy.ts @@ -1,5 +1,5 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { serve, ServerRequest } from "../std/http/server.ts"; +import { serve, ServerRequest } from "../test_util/std/http/server.ts"; const addr = Deno.args[0] || "127.0.0.1:4500"; const originAddr = Deno.args[1] || "127.0.0.1:4501"; diff --git a/cli/bench/http.rs b/cli/bench/http.rs index cf70a78cc6..af7eef9426 100644 --- a/cli/bench/http.rs +++ b/cli/bench/http.rs @@ -191,7 +191,7 @@ fn deno_http(deno_exe: &str) -> Result { "--allow-net", "--reload", "--unstable", - "std/http/bench.ts", + "test_util/std/http/bench.ts", &server_addr(port), ], port, diff --git a/cli/bench/main.rs b/cli/bench/main.rs index 4f64659250..352c930106 100644 --- a/cli/bench/main.rs +++ b/cli/bench/main.rs @@ -79,7 +79,11 @@ const EXEC_TIME_BENCHMARKS: &[(&str, &[&str], Option)] = &[ ), ( "check", - &["cache", "--reload", "std/examples/chat/server_test.ts"], + &[ + "cache", + "--reload", + "test_util/std/examples/chat/server_test.ts", + ], None, ), ( @@ -88,18 +92,22 @@ const EXEC_TIME_BENCHMARKS: &[(&str, &[&str], Option)] = &[ "cache", "--reload", "--no-check", - "std/examples/chat/server_test.ts", + "test_util/std/examples/chat/server_test.ts", ], None, ), ( "bundle", - &["bundle", "std/examples/chat/server_test.ts"], + &["bundle", "test_util/std/examples/chat/server_test.ts"], None, ), ( "bundle_no_check", - &["bundle", "--no-check", "std/examples/chat/server_test.ts"], + &[ + "bundle", + "--no-check", + "test_util/std/examples/chat/server_test.ts", + ], None, ), ]; @@ -254,8 +262,8 @@ fn get_binary_sizes(target_dir: &PathBuf) -> Result> { } const BUNDLES: &[(&str, &str)] = &[ - ("file_server", "./std/http/file_server.ts"), - ("gist", "./std/examples/gist.ts"), + ("file_server", "./test_util/std/http/file_server.ts"), + ("gist", "./test_util/std/examples/gist.ts"), ]; fn bundle_benchmark(deno_exe: &PathBuf) -> Result> { let mut sizes = HashMap::::new(); diff --git a/cli/http_util.rs b/cli/http_util.rs index 4513dbfacf..5b6274309a 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -313,7 +313,7 @@ mod tests { Some( read( test_util::root_path() - .join("std/http/testdata/tls/RootCA.pem") + .join("cli/tests/tls/RootCA.pem") .to_str() .unwrap(), ) @@ -345,7 +345,7 @@ mod tests { Some( read( test_util::root_path() - .join("std/http/testdata/tls/RootCA.pem") + .join("cli/tests/tls/RootCA.pem") .to_str() .unwrap(), ) @@ -376,7 +376,7 @@ mod tests { Some( read( test_util::root_path() - .join("std/http/testdata/tls/RootCA.pem") + .join("cli/tests/tls/RootCA.pem") .to_str() .unwrap(), ) @@ -416,7 +416,7 @@ mod tests { Some( read( test_util::root_path() - .join("std/http/testdata/tls/RootCA.pem") + .join("cli/tests/tls/RootCA.pem") .to_str() .unwrap(), ) diff --git a/cli/tests/034_onload/imported.ts b/cli/tests/034_onload/imported.ts index f9a7009b81..9e1411beaf 100644 --- a/cli/tests/034_onload/imported.ts +++ b/cli/tests/034_onload/imported.ts @@ -1,4 +1,4 @@ -import { assert } from "../../../std/testing/asserts.ts"; +import { assert } from "../../../test_util/std/testing/asserts.ts"; import "./nest_imported.ts"; const handler = (e: Event): void => { diff --git a/cli/tests/034_onload/main.ts b/cli/tests/034_onload/main.ts index aca38869e1..f8c48ba7b2 100644 --- a/cli/tests/034_onload/main.ts +++ b/cli/tests/034_onload/main.ts @@ -1,4 +1,4 @@ -import { assert } from "../../../std/testing/asserts.ts"; +import { assert } from "../../../test_util/std/testing/asserts.ts"; import "./imported.ts"; assert(window.hasOwnProperty("onload")); diff --git a/cli/tests/034_onload/nest_imported.ts b/cli/tests/034_onload/nest_imported.ts index 6b4a40749b..d43b8ebe7b 100644 --- a/cli/tests/034_onload/nest_imported.ts +++ b/cli/tests/034_onload/nest_imported.ts @@ -1,4 +1,4 @@ -import { assert } from "../../../std/testing/asserts.ts"; +import { assert } from "../../../test_util/std/testing/asserts.ts"; const handler = (e: Event): void => { assert(!e.cancelable); diff --git a/cli/tests/045_proxy_client.ts b/cli/tests/045_proxy_client.ts index c9ef43e81e..316f0bf852 100644 --- a/cli/tests/045_proxy_client.ts +++ b/cli/tests/045_proxy_client.ts @@ -1,3 +1,5 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -const res = await fetch("http://localhost:4545/std/examples/colors.ts"); +const res = await fetch( + "http://localhost:4545/test_util/std/examples/colors.ts", +); console.log(`Response http: ${await res.text()}`); diff --git a/cli/tests/045_proxy_test.ts b/cli/tests/045_proxy_test.ts index 2fb45f5f00..c7ba5e967c 100644 --- a/cli/tests/045_proxy_test.ts +++ b/cli/tests/045_proxy_test.ts @@ -1,6 +1,6 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { serve, ServerRequest } from "../../std/http/server.ts"; -import { assertEquals } from "../../std/testing/asserts.ts"; +import { serve, ServerRequest } from "../../test_util/std/http/server.ts"; +import { assertEquals } from "../../test_util/std/testing/asserts.ts"; const addr = Deno.args[1] || "127.0.0.1:4555"; @@ -54,7 +54,7 @@ async function testModuleDownload(): Promise { "cache", "--reload", "--quiet", - "http://localhost:4545/std/examples/colors.ts", + "http://localhost:4545/test_util/std/examples/colors.ts", ], stdout: "piped", env: { @@ -96,7 +96,7 @@ async function testModuleDownloadNoProxy(): Promise { "cache", "--reload", "--quiet", - "http://localhost:4545/std/examples/colors.ts", + "http://localhost:4545/test_util/std/examples/colors.ts", ], stdout: "piped", env: { diff --git a/cli/tests/045_proxy_test.ts.out b/cli/tests/045_proxy_test.ts.out index c59f219a4e..4b07438ecb 100644 --- a/cli/tests/045_proxy_test.ts.out +++ b/cli/tests/045_proxy_test.ts.out @@ -1,4 +1,4 @@ Proxy server listening on [WILDCARD] -Proxy request to: http://localhost:4545/std/examples/colors.ts -Proxy request to: http://localhost:4545/std/examples/colors.ts -Proxy request to: http://localhost:4545/std/fmt/colors.ts +Proxy request to: http://localhost:4545/test_util/std/examples/colors.ts +Proxy request to: http://localhost:4545/test_util/std/examples/colors.ts +Proxy request to: http://localhost:4545/test_util/std/fmt/colors.ts diff --git a/cli/tests/compiler_api_test.ts b/cli/tests/compiler_api_test.ts index 4aac027cbb..bcee2bc43f 100644 --- a/cli/tests/compiler_api_test.ts +++ b/cli/tests/compiler_api_test.ts @@ -3,7 +3,7 @@ import { assert, assertEquals, assertThrowsAsync, -} from "../../std/testing/asserts.ts"; +} from "../../test_util/std/testing/asserts.ts"; Deno.test({ name: "Deno.emit() - sources provided", diff --git a/cli/tests/hash.ts b/cli/tests/hash.ts deleted file mode 100644 index 1215b8b95e..0000000000 --- a/cli/tests/hash.ts +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -const { args } = Deno; -import { createHash, SupportedAlgorithm } from "../../std/hash/mod.ts"; -import { Md5 } from "../../std/hash/md5.ts"; -import { Sha1 } from "../../std/hash/sha1.ts"; -import { Sha256 } from "../../std/hash/sha256.ts"; -import { Sha512 } from "../../std/hash/sha512.ts"; -// deno-lint-ignore camelcase -import { Sha3_224, Sha3_256, Sha3_384, Sha3_512 } from "../../std/hash/sha3.ts"; - -if (args.length < 3) Deno.exit(0); - -const method = args[0]; -const alg = args[1]; -const inputFile = args[2]; - -// deno-lint-ignore no-explicit-any -function getJsHash(alg: string): any { - switch (alg) { - case "md5": - return new Md5(); - case "sha1": - return new Sha1(); - case "sha224": - return new Sha256(true); - case "sha256": - return new Sha256(); - case "sha3-224": - return new Sha3_224(); - case "sha3-256": - return new Sha3_256(); - case "sha3-384": - return new Sha3_384(); - case "sha3-512": - return new Sha3_512(); - case "sha512": - return new Sha512(); - default: - return null; - } -} - -const f = Deno.openSync(inputFile, { read: true }); -const buffer = Deno.readAllSync(f); -f.close(); - -let hash = null; - -console.time("hash"); -if (method === "rust") { - hash = createHash(alg as SupportedAlgorithm); -} else if (method === "js") { - hash = getJsHash(alg); -} - -if (hash === null) { - console.log(`unknown hash: ${alg}`); - Deno.exit(1); -} - -hash.update(buffer); -hash.digest(); -console.timeEnd("hash"); diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index e144d22362..b158c9b55c 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -10,42 +10,6 @@ use std::process::Command; use tempfile::TempDir; use test_util as util; -#[test] -fn std_tests() { - let dir = TempDir::new().expect("tempdir fail"); - let status = util::deno_cmd() - .env("DENO_DIR", dir.path()) - .current_dir(util::root_path()) - .arg("test") - .arg("--unstable") - .arg("--seed=86") // Some tests rely on specific random numbers. - .arg("-A") - // .arg("-Ldebug") - .arg("std/") - .spawn() - .unwrap() - .wait() - .unwrap(); - assert!(status.success()); -} - -#[test] -fn std_lint() { - let status = util::deno_cmd() - .arg("lint") - .arg("--unstable") - .arg(format!( - "--ignore={}", - util::root_path().join("std/node/tests").to_string_lossy() - )) - .arg(util::root_path().join("std")) - .spawn() - .unwrap() - .wait() - .unwrap(); - assert!(status.success()); -} - #[test] fn js_unit_tests_lint() { let status = util::deno_cmd() @@ -4888,7 +4852,7 @@ console.log("finish"); .arg("--unstable") .arg("--output") .arg(&exe) - .arg("./std/examples/welcome.ts") + .arg("./test_util/std/examples/welcome.ts") .stdout(std::process::Stdio::piped()) .spawn() .unwrap() diff --git a/cli/tests/test_runner_test.ts b/cli/tests/test_runner_test.ts index a74c9a41a2..08b69a0a68 100644 --- a/cli/tests/test_runner_test.ts +++ b/cli/tests/test_runner_test.ts @@ -1,6 +1,6 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert } from "../../std/testing/asserts.ts"; +import { assert } from "../../test_util/std/testing/asserts.ts"; Deno.test("fail1", function () { assert(false, "fail1 assertion"); diff --git a/cli/tests/unit/blob_test.ts b/cli/tests/unit/blob_test.ts index 210d05b3b2..a3a8e06fd5 100644 --- a/cli/tests/unit/blob_test.ts +++ b/cli/tests/unit/blob_test.ts @@ -1,7 +1,7 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. import { assert, assertEquals, unitTest } from "./test_util.ts"; -import { concat } from "../../../std/bytes/mod.ts"; -import { decode } from "../../../std/encoding/utf8.ts"; +import { concat } from "../../../test_util/std/bytes/mod.ts"; +import { decode } from "../../../test_util/std/encoding/utf8.ts"; unitTest(function blobString(): void { const b1 = new Blob(["Hello World"]); diff --git a/cli/tests/unit/console_test.ts b/cli/tests/unit/console_test.ts index 64345be847..fc23b1d706 100644 --- a/cli/tests/unit/console_test.ts +++ b/cli/tests/unit/console_test.ts @@ -15,7 +15,7 @@ import { assertThrows, unitTest, } from "./test_util.ts"; -import { stripColor } from "../../../std/fmt/colors.ts"; +import { stripColor } from "../../../test_util/std/fmt/colors.ts"; const customInspect = Deno.customInspect; const { diff --git a/cli/tests/unit/test_util.ts b/cli/tests/unit/test_util.ts index 7dae86dfb4..6c3c0c6a06 100644 --- a/cli/tests/unit/test_util.ts +++ b/cli/tests/unit/test_util.ts @@ -1,9 +1,12 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "../../../std/testing/asserts.ts"; -import * as colors from "../../../std/fmt/colors.ts"; +import { + assert, + assertEquals, +} from "../../../test_util/std/testing/asserts.ts"; +import * as colors from "../../../test_util/std/fmt/colors.ts"; export { colors }; -import { resolve } from "../../../std/path/mod.ts"; +import { resolve } from "../../../test_util/std/path/mod.ts"; export { assert, assertEquals, @@ -15,10 +18,10 @@ export { assertThrowsAsync, fail, unreachable, -} from "../../../std/testing/asserts.ts"; -export { deferred } from "../../../std/async/deferred.ts"; -export { readLines } from "../../../std/io/bufio.ts"; -export { parse as parseArgs } from "../../../std/flags/mod.ts"; +} from "../../../test_util/std/testing/asserts.ts"; +export { deferred } from "../../../test_util/std/async/deferred.ts"; +export { readLines } from "../../../test_util/std/io/bufio.ts"; +export { parse as parseArgs } from "../../../test_util/std/flags/mod.ts"; export interface Permissions { read: boolean; diff --git a/cli/tests/unit/tls_test.ts b/cli/tests/unit/tls_test.ts index 3a42ce4dd4..ba1f067def 100644 --- a/cli/tests/unit/tls_test.ts +++ b/cli/tests/unit/tls_test.ts @@ -8,8 +8,8 @@ import { deferred, unitTest, } from "./test_util.ts"; -import { BufReader, BufWriter } from "../../../std/io/bufio.ts"; -import { TextProtoReader } from "../../../std/textproto/mod.ts"; +import { BufReader, BufWriter } from "../../../test_util/std/io/bufio.ts"; +import { TextProtoReader } from "../../../test_util/std/textproto/mod.ts"; const encoder = new TextEncoder(); const decoder = new TextDecoder(); diff --git a/cli/tests/websocket_test.ts b/cli/tests/websocket_test.ts index 177e5c3620..b5f8659437 100644 --- a/cli/tests/websocket_test.ts +++ b/cli/tests/websocket_test.ts @@ -4,8 +4,8 @@ import { assertEquals, assertThrows, fail, -} from "../../std/testing/asserts.ts"; -import { deferred } from "../../std/async/deferred.ts"; +} from "../../test_util/std/testing/asserts.ts"; +import { deferred } from "../../test_util/std/async/deferred.ts"; Deno.test("invalid scheme", () => { assertThrows(() => new WebSocket("foo://localhost:4242")); diff --git a/cli/tests/workers/parent_read_check_granular_worker.js b/cli/tests/workers/parent_read_check_granular_worker.js index 1a7182e17b..474b8a61bb 100644 --- a/cli/tests/workers/parent_read_check_granular_worker.js +++ b/cli/tests/workers/parent_read_check_granular_worker.js @@ -1,4 +1,4 @@ -import { fromFileUrl } from "../../../std/path/mod.ts"; +import { fromFileUrl } from "../../../test_util/std/path/mod.ts"; const worker = new Worker( new URL("./read_check_granular_worker.js", import.meta.url).href, diff --git a/cli/tests/workers/read_check_granular_worker.js b/cli/tests/workers/read_check_granular_worker.js index 4eddb7a758..d1a205391e 100644 --- a/cli/tests/workers/read_check_granular_worker.js +++ b/cli/tests/workers/read_check_granular_worker.js @@ -1,4 +1,4 @@ -import { fromFileUrl } from "../../../std/path/mod.ts"; +import { fromFileUrl } from "../../../test_util/std/path/mod.ts"; onmessage = async ({ data }) => { const { state } = await Deno.permissions.query({ diff --git a/cli/tests/workers_round_robin_bench.ts b/cli/tests/workers_round_robin_bench.ts index 461e86f915..3dee290fec 100644 --- a/cli/tests/workers_round_robin_bench.ts +++ b/cli/tests/workers_round_robin_bench.ts @@ -5,7 +5,7 @@ const data = "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n"; const workerCount = 4; const cmdsPerWorker = 400; -import { Deferred, deferred } from "../../std/async/deferred.ts"; +import { Deferred, deferred } from "../../test_util/std/async/deferred.ts"; function handleAsyncMsgFromWorker( promiseTable: Map>, diff --git a/cli/tests/workers_test.ts b/cli/tests/workers_test.ts index 1fe8b4538a..383cad8a0a 100644 --- a/cli/tests/workers_test.ts +++ b/cli/tests/workers_test.ts @@ -7,8 +7,8 @@ import { assertEquals, assertThrows, fail, -} from "../../std/testing/asserts.ts"; -import { deferred } from "../../std/async/deferred.ts"; +} from "../../test_util/std/testing/asserts.ts"; +import { deferred } from "../../test_util/std/async/deferred.ts"; Deno.test({ name: "worker terminate", diff --git a/cli/tools/test_runner.rs b/cli/tools/test_runner.rs index 172ca54eaa..e91cad0112 100644 --- a/cli/tools/test_runner.rs +++ b/cli/tools/test_runner.rs @@ -149,7 +149,12 @@ mod tests { #[test] fn supports_dirs() { - let root = test_util::root_path().join("std").join("http"); + // TODO(caspervonb) generate some fixtures in a temporary directory instead, there's no need + // for this to rely on external fixtures. + let root = test_util::root_path() + .join("test_util") + .join("std") + .join("http"); println!("root {:?}", root); let mut matched_urls = prepare_test_modules_urls(vec![".".to_string()], &root).unwrap(); diff --git a/std/README.md b/std/README.md deleted file mode 100644 index 72fcba7f0f..0000000000 --- a/std/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Deno Standard Modules - -These modules do not have external dependencies and they are reviewed by the -Deno core team. The intention is to have a standard set of high quality code -that all Deno projects can use fearlessly. - -Contributions are welcome! - -## How to use - -These modules will eventually be tagged in accordance with Deno releases but as -of today we do not yet consider them stable and so we version the standard -modules differently from the Deno runtime to reflect this. - -It is strongly recommended that you link to tagged releases to avoid unintended -updates and breaking changes. - -Don't link to / import any module whose path: - -- Has a name or parent with an underscore prefix: `_foo.ts`, `_util/bar.ts`. -- Is that of a test module or test data: `test.ts`, `foo_test.ts`, - `testdata/bar.txt`. - -Don't import any symbol with an underscore prefix: `export function _baz() {}`. - -These elements are not considered part of the public API, thus no stability is -guaranteed for them. - -## Documentation - -To browse documentation for modules: - -- Go to https://deno.land/std/. -- Navigate to any module of interest. -- Click "View Documentation". - -## Contributing - -deno_std is a loose port of [Go's standard library](https://golang.org/pkg/). -When in doubt, simply port Go's source code, documentation, and tests. There are -many times when the nature of JavaScript, TypeScript, or Deno itself justifies -diverging from Go, but if possible we want to leverage the energy that went into -building Go. We generally welcome direct ports of Go's code. - -Please ensure the copyright headers cite the code's origin. - -Follow the [style guide](https://deno.land/manual/contributing/style_guide). diff --git a/std/_util/assert.ts b/std/_util/assert.ts deleted file mode 100644 index 2588190dec..0000000000 --- a/std/_util/assert.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -export class DenoStdInternalError extends Error { - constructor(message: string) { - super(message); - this.name = "DenoStdInternalError"; - } -} - -/** Make an assertion, if not `true`, then throw. */ -export function assert(expr: unknown, msg = ""): asserts expr { - if (!expr) { - throw new DenoStdInternalError(msg); - } -} diff --git a/std/_util/assert_test.ts b/std/_util/assert_test.ts deleted file mode 100644 index 919767fd0a..0000000000 --- a/std/_util/assert_test.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, DenoStdInternalError } from "./assert.ts"; -import { assertThrows } from "../testing/asserts.ts"; - -Deno.test({ - name: "assert valid scenario", - fn(): void { - assert(true); - }, -}); - -Deno.test({ - name: "assert invalid scenario, no message", - fn(): void { - assertThrows(() => { - assert(false); - }, DenoStdInternalError); - }, -}); -Deno.test({ - name: "assert invalid scenario, with message", - fn(): void { - assertThrows( - () => { - assert(false, "Oops! Should be true"); - }, - DenoStdInternalError, - "Oops! Should be true", - ); - }, -}); diff --git a/std/_util/deep_assign.ts b/std/_util/deep_assign.ts deleted file mode 100644 index d38788add0..0000000000 --- a/std/_util/deep_assign.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert } from "./assert.ts"; - -export function deepAssign(target: T, source: U): T & U; -export function deepAssign( - target: T, - source1: U, - source2: V, -): T & U & V; -export function deepAssign( - target: T, - source1: U, - source2: V, - source3: W, -): T & U & V & W; -export function deepAssign( - // deno-lint-ignore no-explicit-any - target: Record, - // deno-lint-ignore no-explicit-any - ...sources: any[] -): // deno-lint-ignore ban-types -object | undefined { - for (let i = 0; i < sources.length; i++) { - const source = sources[i]; - if (!source || typeof source !== `object`) { - return; - } - Object.entries(source).forEach(([key, value]): void => { - if (value instanceof Date) { - target[key] = new Date(value); - return; - } - if (!value || typeof value !== `object`) { - target[key] = value; - return; - } - if (Array.isArray(value)) { - target[key] = []; - } - // value is an Object - if (typeof target[key] !== `object` || !target[key]) { - target[key] = {}; - } - assert(value); - deepAssign(target[key] as Record, value); - }); - } - return target; -} diff --git a/std/_util/deep_assign_test.ts b/std/_util/deep_assign_test.ts deleted file mode 100644 index 8ec7b3aef3..0000000000 --- a/std/_util/deep_assign_test.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "../testing/asserts.ts"; -import { deepAssign } from "./deep_assign.ts"; - -Deno.test("deepAssignTest", function (): void { - const date = new Date("1979-05-27T07:32:00Z"); - const reg = RegExp(/DENOWOWO/); - const obj1 = { deno: { bar: { deno: ["is", "not", "node"] } } }; - const obj2 = { foo: { deno: date } }; - const obj3 = { foo: { bar: "deno" }, reg: reg }; - const actual = deepAssign(obj1, obj2, obj3); - const expected = { - foo: { - deno: new Date("1979-05-27T07:32:00Z"), - bar: "deno", - }, - deno: { bar: { deno: ["is", "not", "node"] } }, - reg: RegExp(/DENOWOWO/), - }; - assert(date !== expected.foo.deno); - assert(reg !== expected.reg); - assertEquals(actual, expected); -}); diff --git a/std/_util/has_own_property.ts b/std/_util/has_own_property.ts deleted file mode 100644 index f9e4acbb28..0000000000 --- a/std/_util/has_own_property.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -/** - * Determines whether an object has a property with the specified name. - * Avoid calling prototype builtin `hasOwnProperty` for two reasons: - * - * 1. `hasOwnProperty` is defined on the object as something else: - * - * const options = { - * ending: 'utf8', - * hasOwnProperty: 'foo' - * }; - * options.hasOwnProperty('ending') // throws a TypeError - * - * 2. The object doesn't inherit from `Object.prototype`: - * - * const options = Object.create(null); - * options.ending = 'utf8'; - * options.hasOwnProperty('ending'); // throws a TypeError - * - * @param obj A Object. - * @param v A property name. - * @see https://eslint.org/docs/rules/no-prototype-builtins - */ -export function hasOwnProperty(obj: T, v: PropertyKey): boolean { - if (obj == null) { - return false; - } - return Object.prototype.hasOwnProperty.call(obj, v); -} diff --git a/std/_util/os.ts b/std/_util/os.ts deleted file mode 100644 index 3b56b6d8a1..0000000000 --- a/std/_util/os.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. - -export const osType = (() => { - if (globalThis.Deno != null) { - return Deno.build.os; - } - - // deno-lint-ignore no-explicit-any - const navigator = (globalThis as any).navigator; - if (navigator?.appVersion?.includes?.("Win") ?? false) { - return "windows"; - } - - return "linux"; -})(); - -export const isWindows = osType === "windows"; diff --git a/std/archive/README.md b/std/archive/README.md deleted file mode 100644 index d6e148322f..0000000000 --- a/std/archive/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# Usage - -## Tar - -```ts -import { Tar } from "https://deno.land/std@$STD_VERSION/archive/tar.ts"; - -const tar = new Tar(); -const content = new TextEncoder().encode("Deno.land"); -await tar.append("deno.txt", { - reader: new Deno.Buffer(content), - contentSize: content.byteLength, -}); - -// Or specifying a filePath. -await tar.append("land.txt", { - filePath: "./land.txt", -}); - -// use tar.getReader() to read the contents. - -const writer = await Deno.open("./out.tar", { write: true, create: true }); -await Deno.copy(tar.getReader(), writer); -writer.close(); -``` - -## Untar - -```ts -import { Untar } from "https://deno.land/std@$STD_VERSION/archive/tar.ts"; -import { ensureFile } from "https://deno.land/std@$STD_VERSION/fs/ensure_file.ts"; -import { ensureDir } from "https://deno.land/std@$STD_VERSION/fs/ensure_dir.ts"; - -const reader = await Deno.open("./out.tar", { read: true }); -const untar = new Untar(reader); - -for await (const entry of untar) { - console.log(entry); // metadata - /* - fileName: "archive/deno.txt", - fileMode: 33204, - mtime: 1591657305, - uid: 0, - gid: 0, - size: 24400, - type: 'file' - */ - - if (entry.type === "directory") { - await ensureDir(entry.fileName); - continue; - } - - await ensureFile(entry.fileName); - const file = await Deno.open(entry.fileName, { write: true }); - // is a reader. - await Deno.copy(entry, file); -} -reader.close(); -``` diff --git a/std/archive/tar.ts b/std/archive/tar.ts deleted file mode 100644 index f4194db3ea..0000000000 --- a/std/archive/tar.ts +++ /dev/null @@ -1,639 +0,0 @@ -/** - * Ported and modified from: https://github.com/beatgammit/tar-js and - * licensed as: - * - * (The MIT License) - * - * Copyright (c) 2011 T. Jameson Little - * Copyright (c) 2019 Jun Kato - * Copyright (c) 2018-2021 the Deno authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -import { MultiReader } from "../io/readers.ts"; -import { PartialReadError } from "../io/bufio.ts"; -import { assert } from "../_util/assert.ts"; - -type Reader = Deno.Reader; -type Seeker = Deno.Seeker; - -const recordSize = 512; -const ustar = "ustar\u000000"; - -// https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06 -// eight checksum bytes taken to be ascii spaces (decimal value 32) -const initialChecksum = 8 * 32; - -async function readBlock( - reader: Deno.Reader, - p: Uint8Array, -): Promise { - let bytesRead = 0; - while (bytesRead < p.length) { - const rr = await reader.read(p.subarray(bytesRead)); - if (rr === null) { - if (bytesRead === 0) { - return null; - } else { - throw new PartialReadError(); - } - } - bytesRead += rr; - } - return bytesRead; -} - -/** - * Simple file reader - */ -class FileReader implements Reader { - private file?: Deno.File; - - constructor(private filePath: string) {} - - public async read(p: Uint8Array): Promise { - if (!this.file) { - this.file = await Deno.open(this.filePath, { read: true }); - } - const res = await Deno.read(this.file.rid, p); - if (res === null) { - Deno.close(this.file.rid); - this.file = undefined; - } - return res; - } -} - -/** - * Remove the trailing null codes - * @param buffer - */ -function trim(buffer: Uint8Array): Uint8Array { - const index = buffer.findIndex((v): boolean => v === 0); - if (index < 0) return buffer; - return buffer.subarray(0, index); -} - -/** - * Initialize Uint8Array of the specified length filled with 0 - * @param length - */ -function clean(length: number): Uint8Array { - const buffer = new Uint8Array(length); - buffer.fill(0, 0, length - 1); - return buffer; -} - -function pad(num: number, bytes: number, base = 8): string { - const numString = num.toString(base); - return "000000000000".substr(numString.length + 12 - bytes) + numString; -} - -enum FileTypes { - "file" = 0, - "link" = 1, - "symlink" = 2, - "character-device" = 3, - "block-device" = 4, - "directory" = 5, - "fifo" = 6, - "contiguous-file" = 7, -} - -/* -struct posix_header { // byte offset - char name[100]; // 0 - char mode[8]; // 100 - char uid[8]; // 108 - char gid[8]; // 116 - char size[12]; // 124 - char mtime[12]; // 136 - char chksum[8]; // 148 - char typeflag; // 156 - char linkname[100]; // 157 - char magic[6]; // 257 - char version[2]; // 263 - char uname[32]; // 265 - char gname[32]; // 297 - char devmajor[8]; // 329 - char devminor[8]; // 337 - char prefix[155]; // 345 - // 500 -}; -*/ - -const ustarStructure: Array<{ field: string; length: number }> = [ - { - field: "fileName", - length: 100, - }, - { - field: "fileMode", - length: 8, - }, - { - field: "uid", - length: 8, - }, - { - field: "gid", - length: 8, - }, - { - field: "fileSize", - length: 12, - }, - { - field: "mtime", - length: 12, - }, - { - field: "checksum", - length: 8, - }, - { - field: "type", - length: 1, - }, - { - field: "linkName", - length: 100, - }, - { - field: "ustar", - length: 8, - }, - { - field: "owner", - length: 32, - }, - { - field: "group", - length: 32, - }, - { - field: "majorNumber", - length: 8, - }, - { - field: "minorNumber", - length: 8, - }, - { - field: "fileNamePrefix", - length: 155, - }, - { - field: "padding", - length: 12, - }, -]; - -/** - * Create header for a file in a tar archive - */ -function formatHeader(data: TarData): Uint8Array { - const encoder = new TextEncoder(), - buffer = clean(512); - let offset = 0; - ustarStructure.forEach(function (value): void { - const entry = encoder.encode(data[value.field as keyof TarData] || ""); - buffer.set(entry, offset); - offset += value.length; // space it out with nulls - }); - return buffer; -} - -/** - * Parse file header in a tar archive - * @param length - */ -function parseHeader(buffer: Uint8Array): { [key: string]: Uint8Array } { - const data: { [key: string]: Uint8Array } = {}; - let offset = 0; - ustarStructure.forEach(function (value): void { - const arr = buffer.subarray(offset, offset + value.length); - data[value.field] = arr; - offset += value.length; - }); - return data; -} - -interface TarHeader { - [key: string]: Uint8Array; -} - -export interface TarData { - fileName?: string; - fileNamePrefix?: string; - fileMode?: string; - uid?: string; - gid?: string; - fileSize?: string; - mtime?: string; - checksum?: string; - type?: string; - ustar?: string; - owner?: string; - group?: string; -} - -export interface TarDataWithSource extends TarData { - /** - * file to read - */ - filePath?: string; - /** - * buffer to read - */ - reader?: Reader; -} - -export interface TarInfo { - fileMode?: number; - mtime?: number; - uid?: number; - gid?: number; - owner?: string; - group?: string; - type?: string; -} - -export interface TarOptions extends TarInfo { - /** - * append file - */ - filePath?: string; - - /** - * append any arbitrary content - */ - reader?: Reader; - - /** - * size of the content to be appended - */ - contentSize?: number; -} - -export interface TarMeta extends TarInfo { - fileName: string; - fileSize?: number; -} - -// deno-lint-ignore no-empty-interface -interface TarEntry extends TarMeta {} - -/** - * A class to create a tar archive - */ -export class Tar { - data: TarDataWithSource[]; - - constructor() { - this.data = []; - } - - /** - * Append a file to this tar archive - * @param fn file name - * e.g., test.txt; use slash for directory separators - * @param opts options - */ - async append(fn: string, opts: TarOptions): Promise { - if (typeof fn !== "string") { - throw new Error("file name not specified"); - } - let fileName = fn; - // separate file name into two parts if needed - let fileNamePrefix: string | undefined; - if (fileName.length > 100) { - let i = fileName.length; - while (i >= 0) { - i = fileName.lastIndexOf("/", i); - if (i <= 155) { - fileNamePrefix = fileName.substr(0, i); - fileName = fileName.substr(i + 1); - break; - } - i--; - } - const errMsg = - "ustar format does not allow a long file name (length of [file name" + - "prefix] + / + [file name] must be shorter than 256 bytes)"; - if (i < 0 || fileName.length > 100) { - throw new Error(errMsg); - } else { - assert(fileNamePrefix != null); - if (fileNamePrefix.length > 155) { - throw new Error(errMsg); - } - } - } - - opts = opts || {}; - - // set meta data - let info: Deno.FileInfo | undefined; - if (opts.filePath) { - info = await Deno.stat(opts.filePath); - if (info.isDirectory) { - info.size = 0; - opts.reader = new Deno.Buffer(); - } - } - - const mode = opts.fileMode || (info && info.mode) || - parseInt("777", 8) & 0xfff, - mtime = Math.floor( - opts.mtime ?? (info?.mtime ?? new Date()).valueOf() / 1000, - ), - uid = opts.uid || 0, - gid = opts.gid || 0; - if (typeof opts.owner === "string" && opts.owner.length >= 32) { - throw new Error( - "ustar format does not allow owner name length >= 32 bytes", - ); - } - if (typeof opts.group === "string" && opts.group.length >= 32) { - throw new Error( - "ustar format does not allow group name length >= 32 bytes", - ); - } - - const fileSize = info?.size ?? opts.contentSize; - assert(fileSize != null, "fileSize must be set"); - - const type = opts.type - ? FileTypes[opts.type as keyof typeof FileTypes] - : (info?.isDirectory ? FileTypes.directory : FileTypes.file); - const tarData: TarDataWithSource = { - fileName, - fileNamePrefix, - fileMode: pad(mode, 7), - uid: pad(uid, 7), - gid: pad(gid, 7), - fileSize: pad(fileSize, 11), - mtime: pad(mtime, 11), - checksum: " ", - type: type.toString(), - ustar, - owner: opts.owner || "", - group: opts.group || "", - filePath: opts.filePath, - reader: opts.reader, - }; - - // calculate the checksum - let checksum = 0; - const encoder = new TextEncoder(); - Object.keys(tarData) - .filter((key): boolean => ["filePath", "reader"].indexOf(key) < 0) - .forEach(function (key): void { - checksum += encoder - .encode(tarData[key as keyof TarData]) - .reduce((p, c): number => p + c, 0); - }); - - tarData.checksum = pad(checksum, 6) + "\u0000 "; - this.data.push(tarData); - } - - /** - * Get a Reader instance for this tar data - */ - getReader(): Reader { - const readers: Reader[] = []; - this.data.forEach((tarData): void => { - let { reader } = tarData; - const { filePath } = tarData; - const headerArr = formatHeader(tarData); - readers.push(new Deno.Buffer(headerArr)); - if (!reader) { - assert(filePath != null); - reader = new FileReader(filePath); - } - readers.push(reader); - - // to the nearest multiple of recordSize - assert(tarData.fileSize != null, "fileSize must be set"); - readers.push( - new Deno.Buffer( - clean( - recordSize - - (parseInt(tarData.fileSize, 8) % recordSize || recordSize), - ), - ), - ); - }); - - // append 2 empty records - readers.push(new Deno.Buffer(clean(recordSize * 2))); - return new MultiReader(...readers); - } -} - -class TarEntry implements Reader { - #header: TarHeader; - #reader: Reader | (Reader & Deno.Seeker); - #size: number; - #read = 0; - #consumed = false; - #entrySize: number; - constructor( - meta: TarMeta, - header: TarHeader, - reader: Reader | (Reader & Deno.Seeker), - ) { - Object.assign(this, meta); - this.#header = header; - this.#reader = reader; - - // File Size - this.#size = this.fileSize || 0; - // Entry Size - const blocks = Math.ceil(this.#size / recordSize); - this.#entrySize = blocks * recordSize; - } - - get consumed(): boolean { - return this.#consumed; - } - - async read(p: Uint8Array): Promise { - // Bytes left for entry - const entryBytesLeft = this.#entrySize - this.#read; - const bufSize = Math.min( - // bufSize can't be greater than p.length nor bytes left in the entry - p.length, - entryBytesLeft, - ); - - if (entryBytesLeft <= 0) { - this.#consumed = true; - return null; - } - - const block = new Uint8Array(bufSize); - const n = await readBlock(this.#reader, block); - const bytesLeft = this.#size - this.#read; - - this.#read += n || 0; - if (n === null || bytesLeft <= 0) { - if (n === null) this.#consumed = true; - return null; - } - - // Remove zero filled - const offset = bytesLeft < n ? bytesLeft : n; - p.set(block.subarray(0, offset), 0); - - return offset < 0 ? n - Math.abs(offset) : offset; - } - - async discard(): Promise { - // Discard current entry - if (this.#consumed) return; - this.#consumed = true; - - if (typeof (this.#reader as Seeker).seek === "function") { - await (this.#reader as Seeker).seek( - this.#entrySize - this.#read, - Deno.SeekMode.Current, - ); - this.#read = this.#entrySize; - } else { - await Deno.readAll(this); - } - } -} - -/** - * A class to extract a tar archive - */ -export class Untar { - reader: Reader; - block: Uint8Array; - #entry: TarEntry | undefined; - - constructor(reader: Reader) { - this.reader = reader; - this.block = new Uint8Array(recordSize); - } - - #checksum = (header: Uint8Array): number => { - let sum = initialChecksum; - for (let i = 0; i < 512; i++) { - if (i >= 148 && i < 156) { - // Ignore checksum header - continue; - } - sum += header[i]; - } - return sum; - }; - - #getHeader = async (): Promise => { - await readBlock(this.reader, this.block); - const header = parseHeader(this.block); - - // calculate the checksum - const decoder = new TextDecoder(); - const checksum = this.#checksum(this.block); - - if (parseInt(decoder.decode(header.checksum), 8) !== checksum) { - if (checksum === initialChecksum) { - // EOF - return null; - } - throw new Error("checksum error"); - } - - const magic = decoder.decode(header.ustar); - - if (magic.indexOf("ustar")) { - throw new Error(`unsupported archive format: ${magic}`); - } - - return header; - }; - - #getMetadata = (header: TarHeader): TarMeta => { - const decoder = new TextDecoder(); - // get meta data - const meta: TarMeta = { - fileName: decoder.decode(trim(header.fileName)), - }; - const fileNamePrefix = trim(header.fileNamePrefix); - if (fileNamePrefix.byteLength > 0) { - meta.fileName = decoder.decode(fileNamePrefix) + "/" + meta.fileName; - } - (["fileMode", "mtime", "uid", "gid"] as [ - "fileMode", - "mtime", - "uid", - "gid", - ]).forEach((key): void => { - const arr = trim(header[key]); - if (arr.byteLength > 0) { - meta[key] = parseInt(decoder.decode(arr), 8); - } - }); - (["owner", "group", "type"] as ["owner", "group", "type"]).forEach( - (key): void => { - const arr = trim(header[key]); - if (arr.byteLength > 0) { - meta[key] = decoder.decode(arr); - } - }, - ); - - meta.fileSize = parseInt(decoder.decode(header.fileSize), 8); - meta.type = FileTypes[parseInt(meta.type!)] ?? meta.type; - - return meta; - }; - - async extract(): Promise { - if (this.#entry && !this.#entry.consumed) { - // If entry body was not read, discard the body - // so we can read the next entry. - await this.#entry.discard(); - } - - const header = await this.#getHeader(); - if (header === null) return null; - - const meta = this.#getMetadata(header); - - this.#entry = new TarEntry(meta, header, this.reader); - - return this.#entry; - } - - async *[Symbol.asyncIterator](): AsyncIterableIterator { - while (true) { - const entry = await this.extract(); - - if (entry === null) return; - - yield entry; - } - } -} diff --git a/std/archive/tar_test.ts b/std/archive/tar_test.ts deleted file mode 100644 index cf65e94c2a..0000000000 --- a/std/archive/tar_test.ts +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -/** - * Tar test - * - * **test summary** - * - create a tar archive in memory containing output.txt and dir/tar.ts. - * - read and deflate a tar archive containing output.txt - * - * **to run this test** - * deno run --allow-read archive/tar_test.ts - */ -import { assert, assertEquals } from "../testing/asserts.ts"; - -import { dirname, fromFileUrl, resolve } from "../path/mod.ts"; -import { Tar, Untar } from "./tar.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); -const testdataDir = resolve(moduleDir, "testdata"); -const filePath = resolve(testdataDir, "example.txt"); - -interface TestEntry { - name: string; - content?: Uint8Array; - filePath?: string; -} - -async function createTar(entries: TestEntry[]): Promise { - const tar = new Tar(); - // put data on memory - for (const file of entries) { - let options; - - if (file.content) { - options = { - reader: new Deno.Buffer(file.content), - contentSize: file.content.byteLength, - }; - } else { - options = { filePath: file.filePath }; - } - - await tar.append(file.name, options); - } - - return tar; -} - -Deno.test("createTarArchive", async function (): Promise { - // initialize - const tar = new Tar(); - - // put data on memory - const content = new TextEncoder().encode("hello tar world!"); - await tar.append("output.txt", { - reader: new Deno.Buffer(content), - contentSize: content.byteLength, - }); - - // put a file - await tar.append("dir/tar.ts", { filePath }); - - // write tar data to a buffer - const writer = new Deno.Buffer(); - const wrote = await Deno.copy(tar.getReader(), writer); - - /** - * 3072 = 512 (header) + 512 (content) + 512 (header) + 512 (content) - * + 1024 (footer) - */ - assertEquals(wrote, 3072); -}); - -Deno.test("deflateTarArchive", async function (): Promise { - const fileName = "output.txt"; - const text = "hello tar world!"; - - // create a tar archive - const tar = new Tar(); - const content = new TextEncoder().encode(text); - await tar.append(fileName, { - reader: new Deno.Buffer(content), - contentSize: content.byteLength, - }); - - // read data from a tar archive - const untar = new Untar(tar.getReader()); - const result = await untar.extract(); - assert(result !== null); - const untarText = new TextDecoder("utf-8").decode(await Deno.readAll(result)); - - assertEquals(await untar.extract(), null); // EOF - // tests - assertEquals(result.fileName, fileName); - assertEquals(untarText, text); -}); - -Deno.test("appendFileWithLongNameToTarArchive", async function (): Promise< - void -> { - // 9 * 15 + 13 = 148 bytes - const fileName = new Array(10).join("long-file-name/") + "file-name.txt"; - const text = "hello tar world!"; - - // create a tar archive - const tar = new Tar(); - const content = new TextEncoder().encode(text); - await tar.append(fileName, { - reader: new Deno.Buffer(content), - contentSize: content.byteLength, - }); - - // read data from a tar archive - const untar = new Untar(tar.getReader()); - const result = await untar.extract(); - assert(result !== null); - assert(!result.consumed); - const untarText = new TextDecoder("utf-8").decode(await Deno.readAll(result)); - assert(result.consumed); - - // tests - assertEquals(result.fileName, fileName); - assertEquals(untarText, text); -}); - -Deno.test("untarAsyncIterator", async function (): Promise { - const entries: TestEntry[] = [ - { - name: "output.txt", - content: new TextEncoder().encode("hello tar world!"), - }, - { - name: "dir/tar.ts", - filePath, - }, - ]; - - const tar = await createTar(entries); - - // read data from a tar archive - const untar = new Untar(tar.getReader()); - - let lastEntry; - for await (const entry of untar) { - const expected = entries.shift(); - assert(expected); - - let content = expected.content; - if (expected.filePath) { - content = await Deno.readFile(expected.filePath); - } - assertEquals(content, await Deno.readAll(entry)); - assertEquals(expected.name, entry.fileName); - - if (lastEntry) assert(lastEntry.consumed); - lastEntry = entry; - } - assert(lastEntry); - assert(lastEntry.consumed); - assertEquals(entries.length, 0); -}); - -Deno.test("untarAsyncIteratorWithoutReadingBody", async function (): Promise< - void -> { - const entries: TestEntry[] = [ - { - name: "output.txt", - content: new TextEncoder().encode("hello tar world!"), - }, - { - name: "dir/tar.ts", - filePath, - }, - ]; - - const tar = await createTar(entries); - - // read data from a tar archive - const untar = new Untar(tar.getReader()); - - for await (const entry of untar) { - const expected = entries.shift(); - assert(expected); - assertEquals(expected.name, entry.fileName); - } - - assertEquals(entries.length, 0); -}); - -Deno.test( - "untarAsyncIteratorWithoutReadingBodyFromFileReader", - async function (): Promise { - const entries: TestEntry[] = [ - { - name: "output.txt", - content: new TextEncoder().encode("hello tar world!"), - }, - { - name: "dir/tar.ts", - filePath, - }, - ]; - - const outputFile = resolve(testdataDir, "test.tar"); - - const tar = await createTar(entries); - const file = await Deno.open(outputFile, { create: true, write: true }); - await Deno.copy(tar.getReader(), file); - file.close(); - - const reader = await Deno.open(outputFile, { read: true }); - // read data from a tar archive - const untar = new Untar(reader); - - for await (const entry of untar) { - const expected = entries.shift(); - assert(expected); - assertEquals(expected.name, entry.fileName); - } - - reader.close(); - await Deno.remove(outputFile); - assertEquals(entries.length, 0); - }, -); - -Deno.test("untarAsyncIteratorFromFileReader", async function (): Promise { - const entries: TestEntry[] = [ - { - name: "output.txt", - content: new TextEncoder().encode("hello tar world!"), - }, - { - name: "dir/tar.ts", - filePath, - }, - ]; - - const outputFile = resolve(testdataDir, "test.tar"); - - const tar = await createTar(entries); - const file = await Deno.open(outputFile, { create: true, write: true }); - await Deno.copy(tar.getReader(), file); - file.close(); - - const reader = await Deno.open(outputFile, { read: true }); - // read data from a tar archive - const untar = new Untar(reader); - - for await (const entry of untar) { - const expected = entries.shift(); - assert(expected); - - let content = expected.content; - if (expected.filePath) { - content = await Deno.readFile(expected.filePath); - } - - assertEquals(content, await Deno.readAll(entry)); - assertEquals(expected.name, entry.fileName); - } - - reader.close(); - await Deno.remove(outputFile); - assertEquals(entries.length, 0); -}); - -Deno.test( - "untarAsyncIteratorReadingLessThanRecordSize", - async function (): Promise { - // record size is 512 - const bufSizes = [1, 53, 256, 511]; - - for (const bufSize of bufSizes) { - const entries: TestEntry[] = [ - { - name: "output.txt", - content: new TextEncoder().encode("hello tar world!".repeat(100)), - }, - // Need to test at least two files, to make sure the first entry doesn't over-read - // Causing the next to fail with: chesum error - { - name: "deni.txt", - content: new TextEncoder().encode("deno!".repeat(250)), - }, - ]; - - const tar = await createTar(entries); - - // read data from a tar archive - const untar = new Untar(tar.getReader()); - - for await (const entry of untar) { - const expected = entries.shift(); - assert(expected); - assertEquals(expected.name, entry.fileName); - - const writer = new Deno.Buffer(); - while (true) { - const buf = new Uint8Array(bufSize); - const n = await entry.read(buf); - if (n === null) break; - - await writer.write(buf.subarray(0, n)); - } - assertEquals(writer.bytes(), expected!.content); - } - - assertEquals(entries.length, 0); - } - }, -); - -Deno.test("untarLinuxGeneratedTar", async function (): Promise { - const filePath = resolve(testdataDir, "deno.tar"); - const file = await Deno.open(filePath, { read: true }); - - const expectedEntries = [ - { - fileName: "archive/", - fileSize: 0, - fileMode: 509, - mtime: 1591800767, - uid: 1001, - gid: 1001, - owner: "deno", - group: "deno", - type: "directory", - }, - { - fileName: "archive/deno/", - fileSize: 0, - fileMode: 509, - mtime: 1591799635, - uid: 1001, - gid: 1001, - owner: "deno", - group: "deno", - type: "directory", - }, - { - fileName: "archive/deno/land/", - fileSize: 0, - fileMode: 509, - mtime: 1591799660, - uid: 1001, - gid: 1001, - owner: "deno", - group: "deno", - type: "directory", - }, - { - fileName: "archive/deno/land/land.txt", - fileMode: 436, - fileSize: 5, - mtime: 1591799660, - uid: 1001, - gid: 1001, - owner: "deno", - group: "deno", - type: "file", - content: new TextEncoder().encode("land\n"), - }, - { - fileName: "archive/file.txt", - fileMode: 436, - fileSize: 5, - mtime: 1591799626, - uid: 1001, - gid: 1001, - owner: "deno", - group: "deno", - type: "file", - content: new TextEncoder().encode("file\n"), - }, - { - fileName: "archive/deno.txt", - fileMode: 436, - fileSize: 5, - mtime: 1591799642, - uid: 1001, - gid: 1001, - owner: "deno", - group: "deno", - type: "file", - content: new TextEncoder().encode("deno\n"), - }, - ]; - - const untar = new Untar(file); - - for await (const entry of untar) { - const expected = expectedEntries.shift(); - assert(expected); - const content = expected.content; - delete expected.content; - - assertEquals(entry, expected); - - if (content) { - assertEquals(content, await Deno.readAll(entry)); - } - } - - file.close(); -}); - -Deno.test("directoryEntryType", async function (): Promise { - const tar = new Tar(); - - tar.append("directory/", { - reader: new Deno.Buffer(), - contentSize: 0, - type: "directory", - }); - - const filePath = resolve(testdataDir); - tar.append("archive/testdata/", { - filePath, - }); - - const outputFile = resolve(testdataDir, "directory_type_test.tar"); - const file = await Deno.open(outputFile, { create: true, write: true }); - await Deno.copy(tar.getReader(), file); - await file.close(); - - const reader = await Deno.open(outputFile, { read: true }); - const untar = new Untar(reader); - for await (const entry of untar) { - assertEquals(entry.type, "directory"); - } - - await reader.close(); - await Deno.remove(outputFile); -}); diff --git a/std/archive/testdata/deno.tar b/std/archive/testdata/deno.tar deleted file mode 100644 index 300ce003b5..0000000000 Binary files a/std/archive/testdata/deno.tar and /dev/null differ diff --git a/std/archive/testdata/example.txt b/std/archive/testdata/example.txt deleted file mode 100644 index a042389697..0000000000 --- a/std/archive/testdata/example.txt +++ /dev/null @@ -1 +0,0 @@ -hello world! diff --git a/std/async/README.md b/std/async/README.md deleted file mode 100644 index 2c2ca10183..0000000000 --- a/std/async/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# async - -async is a module to provide help with asynchronous tasks. - -# Usage - -The following functions and class are exposed in `mod.ts`: - -## deferred - -Create a Promise with the `reject` and `resolve` functions. - -```typescript -import { deferred } from "https://deno.land/std/async/mod.ts"; - -const p = deferred(); -// ... -p.resolve(42); -``` - -## delay - -Resolve a Promise after a given amount of milliseconds. - -```typescript -import { delay } from "https://deno.land/std/async/mod.ts"; - -// ... -const delayedPromise = delay(100); -const result = await delayedPromise; -// ... -``` - -## MuxAsyncIterator - -The MuxAsyncIterator class multiplexes multiple async iterators into a single -stream. - -The class makes an assumption that the final result (the value returned and not -yielded from the iterator) does not matter. If there is any result, it is -discarded. - -```typescript -import { MuxAsyncIterator } from "https://deno.land/std/async/mod.ts"; - -async function* gen123(): AsyncIterableIterator { - yield 1; - yield 2; - yield 3; -} - -async function* gen456(): AsyncIterableIterator { - yield 4; - yield 5; - yield 6; -} - -const mux = new MuxAsyncIterator(); -mux.add(gen123()); -mux.add(gen456()); -for await (const value of mux) { - // ... -} -// .. -``` - -## pooledMap - -Transform values from an (async) iterable into another async iterable. The -transforms are done concurrently, with a max concurrency defined by the -poolLimit. - -```typescript -import { pooledMap } from "https://deno.land/std/async/mod.ts"; - -const results = pooledMap( - 2, - [1, 2, 3], - (i) => new Promise((r) => setTimeout(() => r(i), 1000)), -); - -for await (const value of results) { - // ... -} -``` diff --git a/std/async/deferred.ts b/std/async/deferred.ts deleted file mode 100644 index ca05a29bb1..0000000000 --- a/std/async/deferred.ts +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// TODO(ry) It'd be better to make Deferred a class that inherits from -// Promise, rather than an interface. This is possible in ES2016, however -// typescript produces broken code when targeting ES5 code. -// See https://github.com/Microsoft/TypeScript/issues/15202 -// At the time of writing, the github issue is closed but the problem remains. -export interface Deferred extends Promise { - resolve: (value?: T | PromiseLike) => void; - // deno-lint-ignore no-explicit-any - reject: (reason?: any) => void; -} - -/** Creates a Promise with the `reject` and `resolve` functions - * placed as methods on the promise object itself. It allows you to do: - * - * const p = deferred(); - * // ... - * p.resolve(42); - */ -export function deferred(): Deferred { - let methods; - const promise = new Promise((resolve, reject): void => { - methods = { resolve, reject }; - }); - return Object.assign(promise, methods) as Deferred; -} diff --git a/std/async/deferred_test.ts b/std/async/deferred_test.ts deleted file mode 100644 index ba287f4427..0000000000 --- a/std/async/deferred_test.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals, assertThrowsAsync } from "../testing/asserts.ts"; -import { deferred } from "./deferred.ts"; - -Deno.test("[async] deferred: resolve", async function (): Promise { - const d = deferred(); - d.resolve("🦕"); - assertEquals(await d, "🦕"); -}); - -Deno.test("[async] deferred: reject", async function (): Promise { - const d = deferred(); - d.reject(new Error("A deno error 🦕")); - await assertThrowsAsync(async () => { - await d; - }); -}); diff --git a/std/async/delay.ts b/std/async/delay.ts deleted file mode 100644 index 0a9e1f529d..0000000000 --- a/std/async/delay.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -/* Resolves after the given number of milliseconds. */ -export function delay(ms: number): Promise { - return new Promise((res): number => - setTimeout((): void => { - res(); - }, ms) - ); -} diff --git a/std/async/delay_test.ts b/std/async/delay_test.ts deleted file mode 100644 index e5f08f110f..0000000000 --- a/std/async/delay_test.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { delay } from "./delay.ts"; -import { assert } from "../testing/asserts.ts"; - -Deno.test("[async] delay", async function (): Promise { - const start = new Date(); - const delayedPromise = delay(100); - const result = await delayedPromise; - const diff = new Date().getTime() - start.getTime(); - assert(result === undefined); - assert(diff >= 100); -}); diff --git a/std/async/mod.ts b/std/async/mod.ts deleted file mode 100644 index 0345d8cafc..0000000000 --- a/std/async/mod.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -export * from "./deferred.ts"; -export * from "./delay.ts"; -export * from "./mux_async_iterator.ts"; -export * from "./pool.ts"; diff --git a/std/async/mux_async_iterator.ts b/std/async/mux_async_iterator.ts deleted file mode 100644 index 0bda4f5790..0000000000 --- a/std/async/mux_async_iterator.ts +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { Deferred, deferred } from "./deferred.ts"; - -interface TaggedYieldedValue { - iterator: AsyncIterableIterator; - value: T; -} - -/** The MuxAsyncIterator class multiplexes multiple async iterators into a - * single stream. It currently makes an assumption: - * - The final result (the value returned and not yielded from the iterator) - * does not matter; if there is any, it is discarded. - */ -export class MuxAsyncIterator implements AsyncIterable { - private iteratorCount = 0; - private yields: Array> = []; - // deno-lint-ignore no-explicit-any - private throws: any[] = []; - private signal: Deferred = deferred(); - - add(iterator: AsyncIterableIterator): void { - ++this.iteratorCount; - this.callIteratorNext(iterator); - } - - private async callIteratorNext( - iterator: AsyncIterableIterator, - ): Promise { - try { - const { value, done } = await iterator.next(); - if (done) { - --this.iteratorCount; - } else { - this.yields.push({ iterator, value }); - } - } catch (e) { - this.throws.push(e); - } - this.signal.resolve(); - } - - async *iterate(): AsyncIterableIterator { - while (this.iteratorCount > 0) { - // Sleep until any of the wrapped iterators yields. - await this.signal; - - // Note that while we're looping over `yields`, new items may be added. - for (let i = 0; i < this.yields.length; i++) { - const { iterator, value } = this.yields[i]; - yield value; - this.callIteratorNext(iterator); - } - - if (this.throws.length) { - for (const e of this.throws) { - throw e; - } - this.throws.length = 0; - } - // Clear the `yields` list and reset the `signal` promise. - this.yields.length = 0; - this.signal = deferred(); - } - } - - [Symbol.asyncIterator](): AsyncIterableIterator { - return this.iterate(); - } -} diff --git a/std/async/mux_async_iterator_test.ts b/std/async/mux_async_iterator_test.ts deleted file mode 100644 index e1bdb47b42..0000000000 --- a/std/async/mux_async_iterator_test.ts +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals, assertThrowsAsync } from "../testing/asserts.ts"; -import { MuxAsyncIterator } from "./mux_async_iterator.ts"; - -async function* gen123(): AsyncIterableIterator { - yield 1; - yield 2; - yield 3; -} - -async function* gen456(): AsyncIterableIterator { - yield 4; - yield 5; - yield 6; -} - -async function* genThrows(): AsyncIterableIterator { - yield 7; - throw new Error("something went wrong"); -} - -Deno.test("[async] MuxAsyncIterator", async function (): Promise { - const mux = new MuxAsyncIterator(); - mux.add(gen123()); - mux.add(gen456()); - const results = new Set(); - for await (const value of mux) { - results.add(value); - } - assertEquals(results.size, 6); -}); - -Deno.test({ - name: "[async] MuxAsyncIterator throws", - async fn() { - const mux = new MuxAsyncIterator(); - mux.add(gen123()); - mux.add(genThrows()); - const results = new Set(); - await assertThrowsAsync( - async () => { - for await (const value of mux) { - results.add(value); - } - }, - Error, - "something went wrong", - ); - }, -}); diff --git a/std/async/pool.ts b/std/async/pool.ts deleted file mode 100644 index 0b87f24ac8..0000000000 --- a/std/async/pool.ts +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -/** - * pooledMap transforms values from an (async) iterable into another async - * iterable. The transforms are done concurrently, with a max concurrency - * defined by the poolLimit. - * - * If an error is thrown from `iterableFn`, no new transformations will begin. - * All currently executing transformations are allowed to finish and still - * yielded on success. After that, the rejections among them are gathered and - * thrown by the iterator in an `AggregateError`. - * - * @param poolLimit The maximum count of items being processed concurrently. - * @param array The input array for mapping. - * @param iteratorFn The function to call for every item of the array. - */ -export function pooledMap( - poolLimit: number, - array: Iterable | AsyncIterable, - iteratorFn: (data: T) => Promise, -): AsyncIterableIterator { - // Create the async iterable that is returned from this function. - const res = new TransformStream, R>({ - async transform( - p: Promise, - controller: TransformStreamDefaultController, - ): Promise { - controller.enqueue(await p); - }, - }); - // Start processing items from the iterator - (async (): Promise => { - const writer = res.writable.getWriter(); - const executing: Array> = []; - try { - for await (const item of array) { - const p = Promise.resolve().then(() => iteratorFn(item)); - // Only write on success. If we `writer.write()` a rejected promise, - // that will end the iteration. We don't want that yet. Instead let it - // fail the race, taking us to the catch block where all currently - // executing jobs are allowed to finish and all rejections among them - // can be reported together. - p.then((v) => writer.write(Promise.resolve(v))).catch(() => {}); - const e: Promise = p.then(() => - executing.splice(executing.indexOf(e), 1) - ); - executing.push(e); - if (executing.length >= poolLimit) { - await Promise.race(executing); - } - } - // Wait until all ongoing events have processed, then close the writer. - await Promise.all(executing); - writer.close(); - } catch { - const errors = []; - for (const result of await Promise.allSettled(executing)) { - if (result.status == "rejected") { - errors.push(result.reason); - } - } - writer.write(Promise.reject( - new AggregateError(errors, "Threw while mapping."), - )).catch(() => {}); - } - })(); - return res.readable[Symbol.asyncIterator](); -} diff --git a/std/async/pool_test.ts b/std/async/pool_test.ts deleted file mode 100644 index 81be903ed6..0000000000 --- a/std/async/pool_test.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { delay } from "./delay.ts"; -import { pooledMap } from "./pool.ts"; -import { - assert, - assertEquals, - assertStringIncludes, - assertThrowsAsync, -} from "../testing/asserts.ts"; - -Deno.test("[async] pooledMap", async function (): Promise { - const start = new Date(); - const results = pooledMap( - 2, - [1, 2, 3], - (i) => new Promise((r) => setTimeout(() => r(i), 1000)), - ); - for await (const value of results) { - console.log(value); - } - const diff = new Date().getTime() - start.getTime(); - assert(diff >= 2000); - assert(diff < 3000); -}); - -Deno.test("[async] pooledMap errors", async function (): Promise { - async function mapNumber(n: number): Promise { - if (n <= 2) { - throw new Error(`Bad number: ${n}`); - } - await delay(100); - return n; - } - const mappedNumbers: number[] = []; - const error = await assertThrowsAsync(async () => { - for await (const m of pooledMap(3, [1, 2, 3, 4], mapNumber)) { - mappedNumbers.push(m); - } - }, AggregateError) as AggregateError; - assertEquals(mappedNumbers, [3]); - assertEquals(error.errors.length, 2); - assertStringIncludes(error.errors[0].stack, "Error: Bad number: 1"); - assertStringIncludes(error.errors[1].stack, "Error: Bad number: 2"); -}); diff --git a/std/async/test.ts b/std/async/test.ts deleted file mode 100644 index 590417055f..0000000000 --- a/std/async/test.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import "./mod.ts"; diff --git a/std/bytes/README.md b/std/bytes/README.md deleted file mode 100644 index ae0c988ec5..0000000000 --- a/std/bytes/README.md +++ /dev/null @@ -1,137 +0,0 @@ -# bytes - -bytes module is made to provide helpers to manipulation of bytes slice. - -# usage - -All the following functions are exposed in `mod.ts`. - -## indexOf - -Find first index of binary pattern from given binary array, or -1 if it is not -present. - -```typescript -import { indexOf } from "https://deno.land/std@$STD_VERSION/bytes/mod.ts"; - -indexOf( - new Uint8Array([1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 3]), - new Uint8Array([0, 1, 2]), -); // => returns 2 - -indexOf( - new Uint8Array([1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 3]), - new Uint8Array([0, 1, 2]), - 3, -); // => returns 5 -``` - -## lastIndexOf - -Find last index of binary pattern from given binary array, or -1 if it is not -present. - -```typescript -import { lastIndexOf } from "https://deno.land/std@$STD_VERSION/bytes/mod.ts"; - -lastIndexOf( - new Uint8Array([0, 1, 2, 3, 3, 0, 1, 2]), - new Uint8Array([0, 1, 2]), -); // => returns 5 - -lastIndexOf( - new Uint8Array([0, 1, 2, 3, 3, 0, 1, 2]), - new Uint8Array([0, 1, 2]), - 3, -); // => returns 0 -``` - -## equals - -Check whether given binary arrays are equal to each other. - -```typescript -import { equals } from "https://deno.land/std@$STD_VERSION/bytes/mod.ts"; - -equals(new Uint8Array([0, 1, 2, 3]), new Uint8Array([0, 1, 2, 3])); // returns true -equals(new Uint8Array([0, 1, 2, 3]), new Uint8Array([0, 1, 2, 4])); // returns false -``` - -## startsWith - -Check whether binary array starts with prefix. - -```typescript -import { startsWith } from "https://deno.land/std@$STD_VERSION/bytes/mod.ts"; - -startsWith(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1])); // returns true -startsWith(new Uint8Array([0, 1, 2]), new Uint8Array([1, 2])); // returns false -``` - -## endsWith - -Check whether binary array ends with suffix. - -```typescript -import { endsWith } from "https://deno.land/std@$STD_VERSION/bytes/mod.ts"; - -endsWith(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1])); // returns false -endsWith(new Uint8Array([0, 1, 2]), new Uint8Array([1, 2])); // returns true -``` - -## repeat - -Repeat bytes of given binary array and return new one. - -```typescript -import { repeat } from "https://deno.land/std@$STD_VERSION/bytes/mod.ts"; - -repeat(new Uint8Array([1]), 3); // returns Uint8Array(3) [ 1, 1, 1 ] -``` - -## concat - -Concatenate multiple binary arrays and return new one. - -```typescript -import { concat } from "https://deno.land/std@$STD_VERSION/bytes/mod.ts"; - -concat(new Uint8Array([1, 2]), new Uint8Array([3, 4])); // returns Uint8Array(4) [ 1, 2, 3, 4 ] - -concat( - new Uint8Array([1, 2]), - new Uint8Array([3, 4]), - new Uint8Array([5, 6]), - new Uint8Array([7, 8]), -); // => returns Uint8Array(8) [ 1, 2, 3, 4, 5, 6, 7, 8 ] -``` - -## contains - -Check source array contains pattern array. - -```typescript -import { contains } from "https://deno.land/std@$STD_VERSION/bytes/mod.ts"; - -contains( - new Uint8Array([1, 2, 0, 1, 2, 0, 2, 1, 3]), - new Uint8Array([0, 1, 2]), -); // => returns true - -contains( - new Uint8Array([1, 2, 0, 1, 2, 0, 2, 1, 3]), - new Uint8Array([2, 2]), -); // => returns false -``` - -## copy - -Copy bytes from one binary array to another. - -```typescript -import { copy } from "https://deno.land/std@$STD_VERSION/bytes/mod.ts"; - -const dest = new Uint8Array(4); -const src = Uint8Array.of(1, 2, 3, 4); -const len = copy(src, dest); // returns len = 4 -``` diff --git a/std/bytes/mod.ts b/std/bytes/mod.ts deleted file mode 100644 index bb2b7d46eb..0000000000 --- a/std/bytes/mod.ts +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -/** Find first index of binary pattern from source. If not found, then return -1 - * @param source source array - * @param pat pattern to find in source array - * @param start the index to start looking in the source - */ -export function indexOf( - source: Uint8Array, - pat: Uint8Array, - start = 0, -): number { - if (start >= source.length) { - return -1; - } - if (start < 0) { - start = 0; - } - const s = pat[0]; - for (let i = start; i < source.length; i++) { - if (source[i] !== s) continue; - const pin = i; - let matched = 1; - let j = i; - while (matched < pat.length) { - j++; - if (source[j] !== pat[j - pin]) { - break; - } - matched++; - } - if (matched === pat.length) { - return pin; - } - } - return -1; -} - -/** Find last index of binary pattern from source. If not found, then return -1. - * @param source source array - * @param pat pattern to find in source array - * @param start the index to start looking in the source - */ -export function lastIndexOf( - source: Uint8Array, - pat: Uint8Array, - start = source.length - 1, -): number { - if (start < 0) { - return -1; - } - if (start >= source.length) { - start = source.length - 1; - } - const e = pat[pat.length - 1]; - for (let i = start; i >= 0; i--) { - if (source[i] !== e) continue; - const pin = i; - let matched = 1; - let j = i; - while (matched < pat.length) { - j--; - if (source[j] !== pat[pat.length - 1 - (pin - j)]) { - break; - } - matched++; - } - if (matched === pat.length) { - return pin - pat.length + 1; - } - } - return -1; -} - -/** Check whether binary arrays are equal to each other. - * @param a first array to check equality - * @param b second array to check equality - */ -export function equals(a: Uint8Array, b: Uint8Array): boolean { - if (a.length !== b.length) return false; - for (let i = 0; i < b.length; i++) { - if (a[i] !== b[i]) return false; - } - return true; -} - -/** Check whether binary array starts with prefix. - * @param source source array - * @param prefix prefix array to check in source - */ -export function startsWith(source: Uint8Array, prefix: Uint8Array): boolean { - for (let i = 0, max = prefix.length; i < max; i++) { - if (source[i] !== prefix[i]) return false; - } - return true; -} - -/** Check whether binary array ends with suffix. - * @param source source array - * @param suffix suffix array to check in source - */ -export function endsWith(source: Uint8Array, suffix: Uint8Array): boolean { - for ( - let srci = source.length - 1, sfxi = suffix.length - 1; - sfxi >= 0; - srci--, sfxi-- - ) { - if (source[srci] !== suffix[sfxi]) return false; - } - return true; -} - -/** Repeat bytes. returns a new byte slice consisting of `count` copies of `b`. - * @param origin The origin bytes - * @param count The count you want to repeat. - * @throws `RangeError` When count is negative - */ -export function repeat(origin: Uint8Array, count: number): Uint8Array { - if (count === 0) { - return new Uint8Array(); - } - - if (count < 0) { - throw new RangeError("bytes: negative repeat count"); - } else if ((origin.length * count) / count !== origin.length) { - throw new Error("bytes: repeat count causes overflow"); - } - - const int = Math.floor(count); - - if (int !== count) { - throw new Error("bytes: repeat count must be an integer"); - } - - const nb = new Uint8Array(origin.length * count); - - let bp = copy(origin, nb); - - for (; bp < nb.length; bp *= 2) { - copy(nb.slice(0, bp), nb, bp); - } - - return nb; -} - -/** Concatenate multiple binary arrays and return new one. - * @param buf binary arrays to concatenate - */ -export function concat(...buf: Uint8Array[]): Uint8Array { - let length = 0; - for (const b of buf) { - length += b.length; - } - - const output = new Uint8Array(length); - let index = 0; - for (const b of buf) { - output.set(b, index); - index += b.length; - } - - return output; -} - -/** Check source array contains pattern array. - * @param source source array - * @param pat patter array - */ -export function contains(source: Uint8Array, pat: Uint8Array): boolean { - return indexOf(source, pat) != -1; -} - -/** - * Copy bytes from one Uint8Array to another. Bytes from `src` which don't fit - * into `dst` will not be copied. - * - * @param src Source byte array - * @param dst Destination byte array - * @param off Offset into `dst` at which to begin writing values from `src`. - * @return number of bytes copied - */ -export function copy(src: Uint8Array, dst: Uint8Array, off = 0): number { - off = Math.max(0, Math.min(off, dst.byteLength)); - const dstBytesAvailable = dst.byteLength - off; - if (src.byteLength > dstBytesAvailable) { - src = src.subarray(0, dstBytesAvailable); - } - dst.set(src, off); - return src.byteLength; -} diff --git a/std/bytes/test.ts b/std/bytes/test.ts deleted file mode 100644 index 17db0672d1..0000000000 --- a/std/bytes/test.ts +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { - concat, - contains, - copy, - endsWith, - equals, - indexOf, - lastIndexOf, - repeat, - startsWith, -} from "./mod.ts"; -import { assert, assertEquals, assertThrows } from "../testing/asserts.ts"; -import { decode, encode } from "../encoding/utf8.ts"; - -Deno.test("[bytes] indexOf1", () => { - const i = indexOf( - new Uint8Array([1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 3]), - new Uint8Array([0, 1, 2]), - ); - assertEquals(i, 2); -}); - -Deno.test("[bytes] indexOf2", () => { - const i = indexOf(new Uint8Array([0, 0, 1]), new Uint8Array([0, 1])); - assertEquals(i, 1); -}); - -Deno.test("[bytes] indexOf3", () => { - const i = indexOf(encode("Deno"), encode("D")); - assertEquals(i, 0); -}); - -Deno.test("[bytes] indexOf4", () => { - const i = indexOf(new Uint8Array(), new Uint8Array([0, 1])); - assertEquals(i, -1); -}); - -Deno.test("[bytes] indexOf with start index", () => { - const i = indexOf( - new Uint8Array([0, 1, 2, 0, 1, 2]), - new Uint8Array([0, 1]), - 1, - ); - assertEquals(i, 3); -}); - -Deno.test("[bytes] indexOf with start index 2", () => { - const i = indexOf( - new Uint8Array([0, 1, 2, 0, 1, 2]), - new Uint8Array([0, 1]), - 7, - ); - assertEquals(i, -1); -}); - -Deno.test("[bytes] lastIndexOf1", () => { - const i = lastIndexOf( - new Uint8Array([0, 1, 2, 0, 1, 2, 0, 1, 3]), - new Uint8Array([0, 1, 2]), - ); - assertEquals(i, 3); -}); - -Deno.test("[bytes] lastIndexOf2", () => { - const i = lastIndexOf(new Uint8Array([0, 1, 1]), new Uint8Array([0, 1])); - assertEquals(i, 0); -}); - -Deno.test("[bytes] lastIndexOf3", () => { - const i = lastIndexOf(new Uint8Array(), new Uint8Array([0, 1])); - assertEquals(i, -1); -}); - -Deno.test("[bytes] lastIndexOf with start index", () => { - const i = lastIndexOf( - new Uint8Array([0, 1, 2, 0, 1, 2]), - new Uint8Array([0, 1]), - 2, - ); - assertEquals(i, 0); -}); - -Deno.test("[bytes] lastIndexOf with start index 2", () => { - const i = lastIndexOf( - new Uint8Array([0, 1, 2, 0, 1, 2]), - new Uint8Array([0, 1]), - -1, - ); - assertEquals(i, -1); -}); - -Deno.test("[bytes] equals", () => { - const v = equals(new Uint8Array([0, 1, 2, 3]), new Uint8Array([0, 1, 2, 3])); - assertEquals(v, true); -}); - -Deno.test("[bytes] startsWith", () => { - const v = startsWith(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1])); - assertEquals(v, true); -}); - -Deno.test("[bytes] endsWith", () => { - const v = endsWith(new Uint8Array([0, 1, 2]), new Uint8Array([1, 2])); - assertEquals(v, true); -}); - -Deno.test("[bytes] repeat", () => { - // input / output / count / error message - const repeatTestCase = [ - ["", "", 0], - ["", "", 1], - ["", "", 1.1, "bytes: repeat count must be an integer"], - ["", "", 2], - ["", "", 0], - ["-", "", 0], - ["-", "-", -1, "bytes: negative repeat count"], - ["-", "----------", 10], - ["abc ", "abc abc abc ", 3], - ]; - for (const [input, output, count, errMsg] of repeatTestCase) { - if (errMsg) { - assertThrows( - (): void => { - repeat(new TextEncoder().encode(input as string), count as number); - }, - Error, - errMsg as string, - ); - } else { - const newBytes = repeat( - new TextEncoder().encode(input as string), - count as number, - ); - - assertEquals(new TextDecoder().decode(newBytes), output); - } - } -}); - -Deno.test("[bytes] concat", () => { - const u1 = encode("Hello "); - const u2 = encode("World"); - const joined = concat(u1, u2); - assertEquals(decode(joined), "Hello World"); - assert(u1 !== joined); - assert(u2 !== joined); -}); - -Deno.test("[bytes] concat empty arrays", () => { - const u1 = new Uint8Array(); - const u2 = new Uint8Array(); - const joined = concat(u1, u2); - assertEquals(joined.byteLength, 0); - assert(u1 !== joined); - assert(u2 !== joined); -}); - -Deno.test("[bytes] concat multiple arrays", () => { - const u1 = encode("Hello "); - const u2 = encode("W"); - const u3 = encode("o"); - const u4 = encode("r"); - const u5 = encode("l"); - const u6 = encode("d"); - const joined = concat(u1, u2, u3, u4, u5, u6); - assertEquals(decode(joined), "Hello World"); - assert(u1 !== joined); - assert(u2 !== joined); -}); - -Deno.test("[bytes] contains", () => { - const source = encode("deno.land"); - const pattern = encode("deno"); - assert(contains(source, pattern)); - - assert(contains(new Uint8Array([0, 1, 2, 3]), new Uint8Array([2, 3]))); -}); - -Deno.test("[bytes] copy", function (): void { - const dst = new Uint8Array(4); - - dst.fill(0); - let src = Uint8Array.of(1, 2); - let len = copy(src, dst, 0); - assert(len === 2); - assertEquals(dst, Uint8Array.of(1, 2, 0, 0)); - - dst.fill(0); - src = Uint8Array.of(1, 2); - len = copy(src, dst, 1); - assert(len === 2); - assertEquals(dst, Uint8Array.of(0, 1, 2, 0)); - - dst.fill(0); - src = Uint8Array.of(1, 2, 3, 4, 5); - len = copy(src, dst); - assert(len === 4); - assertEquals(dst, Uint8Array.of(1, 2, 3, 4)); - - dst.fill(0); - src = Uint8Array.of(1, 2); - len = copy(src, dst, 100); - assert(len === 0); - assertEquals(dst, Uint8Array.of(0, 0, 0, 0)); - - dst.fill(0); - src = Uint8Array.of(3, 4); - len = copy(src, dst, -2); - assert(len === 2); - assertEquals(dst, Uint8Array.of(3, 4, 0, 0)); -}); diff --git a/std/datetime/README.md b/std/datetime/README.md deleted file mode 100644 index b168cd08bd..0000000000 --- a/std/datetime/README.md +++ /dev/null @@ -1,188 +0,0 @@ -# datetime - -Simple helper to help parse date strings into `Date`, with additional functions. - -## Usage - -The following symbols from -[unicode LDML](http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table) -are supported: - -- `yyyy` - numeric year. -- `yy` - 2-digit year. -- `M` - numeric month. -- `MM` - 2-digit month. -- `d` - numeric day. -- `dd` - 2-digit day. - -- `H` - numeric hour (0-23 hours). -- `HH` - 2-digit hour (00-23 hours). -- `h` - numeric hour (1-12 hours). -- `hh` - 2-digit hour (01-12 hours). -- `m` - numeric minute. -- `mm` - 2-digit minute. -- `s` - numeric second. -- `ss` - 2-digit second. -- `S` - 1-digit fractionalSecond. -- `SS` - 2-digit fractionalSecond. -- `SSS` - 3-digit fractionalSecond. - -- `a` - dayPeriod, either `AM` or `PM`. - -- `'foo'` - quoted literal. -- `./-` - unquoted literal. - -## Methods - -### parse - -Takes an input `string` and a `formatString` to parse to a `date`. - -```ts -import { parse } from 'https://deno.land/std@$STD_VERSION/datetime/mod.ts' - -parse("20-01-2019", "dd-MM-yyyy") // output : new Date(2019, 0, 20) -parse("2019-01-20", "yyyy-MM-dd") // output : new Date(2019, 0, 20) -parse("20.01.2019", "dd.MM.yyyy") // output : new Date(2019, 0, 20) -parse("01-20-2019 16:34", "MM-dd-yyyy HH:mm") // output : new Date(2019, 0, 20, 16, 34) -parse("01-20-2019 04:34 PM", "MM-dd-yyyy hh:mm a") // output : new Date(2019, 0, 20, 16, 34) -parse("16:34 01-20-2019", "HH:mm MM-dd-yyyy") // output : new Date(2019, 0, 20, 16, 34) -parse("01-20-2019 16:34:23.123", "MM-dd-yyyy HH:mm:ss.SSS") // output : new Date(2019, 0, 20, 16, 34, 23, 123) -... -``` - -### format - -Takes an input `date` and a `formatString` to format to a `string`. - -```ts -import { format } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; - -format(new Date(2019, 0, 20), "dd-MM-yyyy"); // output : "20-01-2019" -format(new Date(2019, 0, 20), "yyyy-MM-dd"); // output : "2019-01-20" -format(new Date(2019, 0, 20), "dd.MM.yyyy"); // output : "2019-01-20" -format(new Date(2019, 0, 20, 16, 34), "MM-dd-yyyy HH:mm"); // output : "01-20-2019 16:34" -format(new Date(2019, 0, 20, 16, 34), "MM-dd-yyyy hh:mm a"); // output : "01-20-2019 04:34 PM" -format(new Date(2019, 0, 20, 16, 34), "HH:mm MM-dd-yyyy"); // output : "16:34 01-20-2019" -format(new Date(2019, 0, 20, 16, 34, 23, 123), "MM-dd-yyyy HH:mm:ss.SSS"); // output : "01-20-2019 16:34:23.123" -format(new Date(2019, 0, 20), "'today:' yyyy-MM-dd"); // output : "today: 2019-01-20" -``` - -### dayOfYear - -Returns the number of the day in the year. - -```ts -import { dayOfYear } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; - -dayOfYear(new Date("2019-03-11T03:24:00")); // output: 70 -``` - -### weekOfYear - -Returns the ISO week number of the provided date (1-53). - -```ts -import { weekOfYear } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; - -weekOfYear(new Date("2020-12-28T03:24:00")); // Returns 53 -``` - -### toIMF - -Formats the given date to IMF date time format. (Reference: -https://tools.ietf.org/html/rfc7231#section-7.1.1.1 ) - -```js -import { toIMF } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; - -toIMF(new Date(0)); // => returns "Thu, 01 Jan 1970 00:00:00 GMT" -``` - -### isLeap - -Returns true if the given date or year (in number) is a leap year. Returns false -otherwise. - -```js -import { isLeap } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; - -isLeap(new Date("1970-01-01")); // => returns false -isLeap(new Date("1972-01-01")); // => returns true -isLeap(new Date("2000-01-01")); // => returns true -isLeap(new Date("2100-01-01")); // => returns false -isLeap(1972); // => returns true -``` - -### difference - -Returns the difference of the 2 given dates in the given units. If the units are -omitted, it returns the difference in the all available units. - -Available units: "milliseconds", "seconds", "minutes", "hours", "days", "weeks", -"months", "quarters", "years" - -```js -import { difference } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; - -const date0 = new Date("2018-05-14"); -const date1 = new Date("2020-05-13"); - -difference(date0, date1, { units: ["days", "months", "years"] }); -// => returns { days: 730, months: 23, years: 1 } - -difference(date0, date1); -// => returns { -// milliseconds: 63072000000, -// seconds: 63072000, -// minutes: 1051200, -// hours: 17520, -// days: 730, -// weeks: 104, -// months: 23, -// quarters: 5, -// years: 1 -// } -``` - -## Constants - -### SECOND - -``` -import { SECOND } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; - -console.log(SECOND); // => 1000 -``` - -### MINUTE - -``` -import { MINUTE } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; - -console.log(MINUTE); // => 60000 (60 * 1000) -``` - -### HOUR - -``` -import { HOUR } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; - -console.log(HOUR); // => 3600000 (60 * 60 * 1000) -``` - -### DAY - -``` -import { DAY } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; - -console.log(DAY); // => 86400000 (24 * 60 * 60 * 1000) -``` - -### WEEK - -``` -import { WEEK } from "https://deno.land/std@$STD_VERSION/datetime/mod.ts"; - -console.log(WEEK); // => 604800000 (7 * 24 * 60 * 60 * 1000) -``` diff --git a/std/datetime/formatter.ts b/std/datetime/formatter.ts deleted file mode 100644 index 788de6d00c..0000000000 --- a/std/datetime/formatter.ts +++ /dev/null @@ -1,594 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - CallbackResult, - ReceiverResult, - Rule, - TestFunction, - TestResult, - Tokenizer, -} from "./tokenizer.ts"; - -function digits(value: string | number, count = 2): string { - return String(value).padStart(count, "0"); -} - -// as declared as in namespace Intl -type DateTimeFormatPartTypes = - | "day" - | "dayPeriod" - // | "era" - | "hour" - | "literal" - | "minute" - | "month" - | "second" - | "timeZoneName" - // | "weekday" - | "year" - | "fractionalSecond"; - -interface DateTimeFormatPart { - type: DateTimeFormatPartTypes; - value: string; -} - -type TimeZone = "UTC"; - -interface Options { - timeZone?: TimeZone; -} - -function createLiteralTestFunction(value: string): TestFunction { - return (string: string): TestResult => { - return string.startsWith(value) - ? { value, length: value.length } - : undefined; - }; -} - -function createMatchTestFunction(match: RegExp): TestFunction { - return (string: string): TestResult => { - const result = match.exec(string); - if (result) return { value: result, length: result[0].length }; - }; -} - -// according to unicode symbols (http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table) -const defaultRules = [ - { - test: createLiteralTestFunction("yyyy"), - fn: (): CallbackResult => ({ type: "year", value: "numeric" }), - }, - { - test: createLiteralTestFunction("yy"), - fn: (): CallbackResult => ({ type: "year", value: "2-digit" }), - }, - - { - test: createLiteralTestFunction("MM"), - fn: (): CallbackResult => ({ type: "month", value: "2-digit" }), - }, - { - test: createLiteralTestFunction("M"), - fn: (): CallbackResult => ({ type: "month", value: "numeric" }), - }, - { - test: createLiteralTestFunction("dd"), - fn: (): CallbackResult => ({ type: "day", value: "2-digit" }), - }, - { - test: createLiteralTestFunction("d"), - fn: (): CallbackResult => ({ type: "day", value: "numeric" }), - }, - - { - test: createLiteralTestFunction("HH"), - fn: (): CallbackResult => ({ type: "hour", value: "2-digit" }), - }, - { - test: createLiteralTestFunction("H"), - fn: (): CallbackResult => ({ type: "hour", value: "numeric" }), - }, - { - test: createLiteralTestFunction("hh"), - fn: (): CallbackResult => ({ - type: "hour", - value: "2-digit", - hour12: true, - }), - }, - { - test: createLiteralTestFunction("h"), - fn: (): CallbackResult => ({ - type: "hour", - value: "numeric", - hour12: true, - }), - }, - { - test: createLiteralTestFunction("mm"), - fn: (): CallbackResult => ({ type: "minute", value: "2-digit" }), - }, - { - test: createLiteralTestFunction("m"), - fn: (): CallbackResult => ({ type: "minute", value: "numeric" }), - }, - { - test: createLiteralTestFunction("ss"), - fn: (): CallbackResult => ({ type: "second", value: "2-digit" }), - }, - { - test: createLiteralTestFunction("s"), - fn: (): CallbackResult => ({ type: "second", value: "numeric" }), - }, - { - test: createLiteralTestFunction("SSS"), - fn: (): CallbackResult => ({ type: "fractionalSecond", value: 3 }), - }, - { - test: createLiteralTestFunction("SS"), - fn: (): CallbackResult => ({ type: "fractionalSecond", value: 2 }), - }, - { - test: createLiteralTestFunction("S"), - fn: (): CallbackResult => ({ type: "fractionalSecond", value: 1 }), - }, - - { - test: createLiteralTestFunction("a"), - fn: (value: unknown): CallbackResult => ({ - type: "dayPeriod", - value: value as string, - }), - }, - - // quoted literal - { - test: createMatchTestFunction(/^(')(?\\.|[^\']*)\1/), - fn: (match: unknown): CallbackResult => ({ - type: "literal", - value: (match as RegExpExecArray).groups!.value as string, - }), - }, - // literal - { - test: createMatchTestFunction(/^.+?\s*/), - fn: (match: unknown): CallbackResult => ({ - type: "literal", - value: (match as RegExpExecArray)[0], - }), - }, -]; - -type FormatPart = { - type: DateTimeFormatPartTypes; - value: string | number; - hour12?: boolean; -}; -type Format = FormatPart[]; - -export class DateTimeFormatter { - #format: Format; - - constructor(formatString: string, rules: Rule[] = defaultRules) { - const tokenizer = new Tokenizer(rules); - this.#format = tokenizer.tokenize( - formatString, - ({ type, value, hour12 }) => { - const result = { - type, - value, - } as unknown as ReceiverResult; - if (hour12) result.hour12 = hour12 as boolean; - return result; - }, - ) as Format; - } - - format(date: Date, options: Options = {}): string { - let string = ""; - - const utc = options.timeZone === "UTC"; - - for (const token of this.#format) { - const type = token.type; - - switch (type) { - case "year": { - const value = utc ? date.getUTCFullYear() : date.getFullYear(); - switch (token.value) { - case "numeric": { - string += value; - break; - } - case "2-digit": { - string += digits(value, 2).slice(-2); - break; - } - default: - throw Error( - `FormatterError: value "${token.value}" is not supported`, - ); - } - break; - } - case "month": { - const value = (utc ? date.getUTCMonth() : date.getMonth()) + 1; - switch (token.value) { - case "numeric": { - string += value; - break; - } - case "2-digit": { - string += digits(value, 2); - break; - } - default: - throw Error( - `FormatterError: value "${token.value}" is not supported`, - ); - } - break; - } - case "day": { - const value = utc ? date.getUTCDate() : date.getDate(); - switch (token.value) { - case "numeric": { - string += value; - break; - } - case "2-digit": { - string += digits(value, 2); - break; - } - default: - throw Error( - `FormatterError: value "${token.value}" is not supported`, - ); - } - break; - } - case "hour": { - let value = utc ? date.getUTCHours() : date.getHours(); - value -= token.hour12 && date.getHours() > 12 ? 12 : 0; - switch (token.value) { - case "numeric": { - string += value; - break; - } - case "2-digit": { - string += digits(value, 2); - break; - } - default: - throw Error( - `FormatterError: value "${token.value}" is not supported`, - ); - } - break; - } - case "minute": { - const value = utc ? date.getUTCMinutes() : date.getMinutes(); - switch (token.value) { - case "numeric": { - string += value; - break; - } - case "2-digit": { - string += digits(value, 2); - break; - } - default: - throw Error( - `FormatterError: value "${token.value}" is not supported`, - ); - } - break; - } - case "second": { - const value = utc ? date.getUTCSeconds() : date.getSeconds(); - switch (token.value) { - case "numeric": { - string += value; - break; - } - case "2-digit": { - string += digits(value, 2); - break; - } - default: - throw Error( - `FormatterError: value "${token.value}" is not supported`, - ); - } - break; - } - case "fractionalSecond": { - const value = utc - ? date.getUTCMilliseconds() - : date.getMilliseconds(); - string += digits(value, Number(token.value)); - break; - } - // FIXME(bartlomieju) - case "timeZoneName": { - // string += utc ? "Z" : token.value - break; - } - case "dayPeriod": { - string += token.value ? (date.getHours() >= 12 ? "PM" : "AM") : ""; - break; - } - case "literal": { - string += token.value; - break; - } - - default: - throw Error(`FormatterError: { ${token.type} ${token.value} }`); - } - } - - return string; - } - - parseToParts(string: string): DateTimeFormatPart[] { - const parts: DateTimeFormatPart[] = []; - - for (const token of this.#format) { - const type = token.type; - - let value = ""; - switch (token.type) { - case "year": { - switch (token.value) { - case "numeric": { - value = /^\d{1,4}/.exec(string)?.[0] as string; - break; - } - case "2-digit": { - value = /^\d{1,2}/.exec(string)?.[0] as string; - break; - } - } - break; - } - case "month": { - switch (token.value) { - case "numeric": { - value = /^\d{1,2}/.exec(string)?.[0] as string; - break; - } - case "2-digit": { - value = /^\d{2}/.exec(string)?.[0] as string; - break; - } - case "narrow": { - value = /^[a-zA-Z]+/.exec(string)?.[0] as string; - break; - } - case "short": { - value = /^[a-zA-Z]+/.exec(string)?.[0] as string; - break; - } - case "long": { - value = /^[a-zA-Z]+/.exec(string)?.[0] as string; - break; - } - default: - throw Error( - `ParserError: value "${token.value}" is not supported`, - ); - } - break; - } - case "day": { - switch (token.value) { - case "numeric": { - value = /^\d{1,2}/.exec(string)?.[0] as string; - break; - } - case "2-digit": { - value = /^\d{2}/.exec(string)?.[0] as string; - break; - } - default: - throw Error( - `ParserError: value "${token.value}" is not supported`, - ); - } - break; - } - case "hour": { - switch (token.value) { - case "numeric": { - value = /^\d{1,2}/.exec(string)?.[0] as string; - if (token.hour12 && parseInt(value) > 12) { - console.error( - `Trying to parse hour greater than 12. Use 'H' instead of 'h'.`, - ); - } - break; - } - case "2-digit": { - value = /^\d{2}/.exec(string)?.[0] as string; - if (token.hour12 && parseInt(value) > 12) { - console.error( - `Trying to parse hour greater than 12. Use 'HH' instead of 'hh'.`, - ); - } - break; - } - default: - throw Error( - `ParserError: value "${token.value}" is not supported`, - ); - } - break; - } - case "minute": { - switch (token.value) { - case "numeric": { - value = /^\d{1,2}/.exec(string)?.[0] as string; - break; - } - case "2-digit": { - value = /^\d{2}/.exec(string)?.[0] as string; - break; - } - default: - throw Error( - `ParserError: value "${token.value}" is not supported`, - ); - } - break; - } - case "second": { - switch (token.value) { - case "numeric": { - value = /^\d{1,2}/.exec(string)?.[0] as string; - break; - } - case "2-digit": { - value = /^\d{2}/.exec(string)?.[0] as string; - break; - } - default: - throw Error( - `ParserError: value "${token.value}" is not supported`, - ); - } - break; - } - case "fractionalSecond": { - value = new RegExp(`^\\d{${token.value}}`).exec(string) - ?.[0] as string; - break; - } - case "timeZoneName": { - value = token.value as string; - break; - } - case "dayPeriod": { - value = /^(A|P)M/.exec(string)?.[0] as string; - break; - } - case "literal": { - if (!string.startsWith(token.value as string)) { - throw Error( - `Literal "${token.value}" not found "${string.slice(0, 25)}"`, - ); - } - value = token.value as string; - break; - } - - default: - throw Error(`${token.type} ${token.value}`); - } - - if (!value) { - throw Error( - `value not valid for token { ${type} ${value} } ${ - string.slice( - 0, - 25, - ) - }`, - ); - } - parts.push({ type, value }); - - string = string.slice(value.length); - } - - if (string.length) { - throw Error( - `datetime string was not fully parsed! ${string.slice(0, 25)}`, - ); - } - - return parts; - } - - /** sort & filter dateTimeFormatPart */ - sortDateTimeFormatPart(parts: DateTimeFormatPart[]): DateTimeFormatPart[] { - let result: DateTimeFormatPart[] = []; - const typeArray = [ - "year", - "month", - "day", - "hour", - "minute", - "second", - "fractionalSecond", - ]; - for (const type of typeArray) { - const current = parts.findIndex((el) => el.type === type); - if (current !== -1) { - result = result.concat(parts.splice(current, 1)); - } - } - result = result.concat(parts); - return result; - } - - partsToDate(parts: DateTimeFormatPart[]): Date { - const date = new Date(); - const utc = parts.find( - (part) => part.type === "timeZoneName" && part.value === "UTC", - ); - - utc ? date.setUTCHours(0, 0, 0, 0) : date.setHours(0, 0, 0, 0); - for (const part of parts) { - switch (part.type) { - case "year": { - const value = Number(part.value.padStart(4, "20")); - utc ? date.setUTCFullYear(value) : date.setFullYear(value); - break; - } - case "month": { - const value = Number(part.value) - 1; - utc ? date.setUTCMonth(value) : date.setMonth(value); - break; - } - case "day": { - const value = Number(part.value); - utc ? date.setUTCDate(value) : date.setDate(value); - break; - } - case "hour": { - let value = Number(part.value); - const dayPeriod = parts.find( - (part: DateTimeFormatPart) => part.type === "dayPeriod", - ); - if (dayPeriod?.value === "PM") value += 12; - utc ? date.setUTCHours(value) : date.setHours(value); - break; - } - case "minute": { - const value = Number(part.value); - utc ? date.setUTCMinutes(value) : date.setMinutes(value); - break; - } - case "second": { - const value = Number(part.value); - utc ? date.setUTCSeconds(value) : date.setSeconds(value); - break; - } - case "fractionalSecond": { - const value = Number(part.value); - utc ? date.setUTCMilliseconds(value) : date.setMilliseconds(value); - break; - } - } - } - return date; - } - - parse(string: string): Date { - const parts = this.parseToParts(string); - const sortParts = this.sortDateTimeFormatPart(parts); - return this.partsToDate(sortParts); - } -} diff --git a/std/datetime/mod.ts b/std/datetime/mod.ts deleted file mode 100644 index 8a3ec7c4ff..0000000000 --- a/std/datetime/mod.ts +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { DateTimeFormatter } from "./formatter.ts"; - -export const SECOND = 1e3; -export const MINUTE = SECOND * 60; -export const HOUR = MINUTE * 60; -export const DAY = HOUR * 24; -export const WEEK = DAY * 7; -const DAYS_PER_WEEK = 7; - -enum Day { - Sun, - Mon, - Tue, - Wed, - Thu, - Fri, - Sat, -} - -/** - * Parse date from string using format string - * @param dateString Date string - * @param format Format string - * @return Parsed date - */ -export function parse(dateString: string, formatString: string): Date { - const formatter = new DateTimeFormatter(formatString); - const parts = formatter.parseToParts(dateString); - const sortParts = formatter.sortDateTimeFormatPart(parts); - return formatter.partsToDate(sortParts); -} - -/** - * Format date using format string - * @param date Date - * @param format Format string - * @return formatted date string - */ -export function format(date: Date, formatString: string): string { - const formatter = new DateTimeFormatter(formatString); - return formatter.format(date); -} - -/** - * Get number of the day in the year - * @return Number of the day in year - */ -export function dayOfYear(date: Date): number { - // Values from 0 to 99 map to the years 1900 to 1999. All other values are the actual year. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date) - // Using setFullYear as a workaround - - const yearStart = new Date(date); - - yearStart.setUTCFullYear(date.getUTCFullYear(), 0, 0); - const diff = date.getTime() - - yearStart.getTime() + - (yearStart.getTimezoneOffset() - date.getTimezoneOffset()) * 60 * 1000; - - return Math.floor(diff / DAY); -} -/** - * Get number of the week in the year (ISO-8601) - * @return Number of the week in year - */ -export function weekOfYear(date: Date): number { - const workingDate = new Date( - Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()), - ); - - const day = workingDate.getUTCDay(); - - const nearestThursday = workingDate.getUTCDate() + - Day.Thu - - (day === Day.Sun ? DAYS_PER_WEEK : day); - - workingDate.setUTCDate(nearestThursday); - - // Get first day of year - const yearStart = new Date(Date.UTC(workingDate.getUTCFullYear(), 0, 1)); - - // return the calculated full weeks to nearest Thursday - return Math.ceil((workingDate.getTime() - yearStart.getTime() + DAY) / WEEK); -} - -/** - * Parse a date to return a IMF formatted string date - * RFC: https://tools.ietf.org/html/rfc7231#section-7.1.1.1 - * IMF is the time format to use when generating times in HTTP - * headers. The time being formatted must be in UTC for Format to - * generate the correct format. - * @param date Date to parse - * @return IMF date formatted string - */ -export function toIMF(date: Date): string { - function dtPad(v: string, lPad = 2): string { - return v.padStart(lPad, "0"); - } - const d = dtPad(date.getUTCDate().toString()); - const h = dtPad(date.getUTCHours().toString()); - const min = dtPad(date.getUTCMinutes().toString()); - const s = dtPad(date.getUTCSeconds().toString()); - const y = date.getUTCFullYear(); - const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; - const months = [ - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ]; - return `${days[date.getUTCDay()]}, ${d} ${ - months[date.getUTCMonth()] - } ${y} ${h}:${min}:${s} GMT`; -} - -/** - * Check given year is a leap year or not. - * based on : https://docs.microsoft.com/en-us/office/troubleshoot/excel/determine-a-leap-year - * @param year year in number or Date format - */ -export function isLeap(year: Date | number): boolean { - const yearNumber = year instanceof Date ? year.getFullYear() : year; - return ( - (yearNumber % 4 === 0 && yearNumber % 100 !== 0) || yearNumber % 400 === 0 - ); -} - -export type Unit = - | "milliseconds" - | "seconds" - | "minutes" - | "hours" - | "days" - | "weeks" - | "months" - | "quarters" - | "years"; - -export type DifferenceFormat = Partial>; - -export type DifferenceOptions = { - units?: Unit[]; -}; - -/** - * Calculate difference between two dates. - * @param from Year to calculate difference - * @param to Year to calculate difference with - * @param options Options for determining how to respond - * - * example : - * - * ```typescript - * datetime.difference(new Date("2020/1/1"),new Date("2020/2/2"),{ units : ["days","months"] }) - * ``` - */ -export function difference( - from: Date, - to: Date, - options?: DifferenceOptions, -): DifferenceFormat { - const uniqueUnits = options?.units ? [...new Set(options?.units)] : [ - "milliseconds", - "seconds", - "minutes", - "hours", - "days", - "weeks", - "months", - "quarters", - "years", - ]; - - const bigger = Math.max(from.getTime(), to.getTime()); - const smaller = Math.min(from.getTime(), to.getTime()); - const differenceInMs = bigger - smaller; - - const differences: DifferenceFormat = {}; - - for (const uniqueUnit of uniqueUnits) { - switch (uniqueUnit) { - case "milliseconds": - differences.milliseconds = differenceInMs; - break; - case "seconds": - differences.seconds = Math.floor(differenceInMs / SECOND); - break; - case "minutes": - differences.minutes = Math.floor(differenceInMs / MINUTE); - break; - case "hours": - differences.hours = Math.floor(differenceInMs / HOUR); - break; - case "days": - differences.days = Math.floor(differenceInMs / DAY); - break; - case "weeks": - differences.weeks = Math.floor(differenceInMs / WEEK); - break; - case "months": - differences.months = calculateMonthsDifference(bigger, smaller); - break; - case "quarters": - differences.quarters = Math.floor( - (typeof differences.months !== "undefined" && - differences.months / 4) || - calculateMonthsDifference(bigger, smaller) / 4, - ); - break; - case "years": - differences.years = Math.floor( - (typeof differences.months !== "undefined" && - differences.months / 12) || - calculateMonthsDifference(bigger, smaller) / 12, - ); - break; - } - } - - return differences; -} - -function calculateMonthsDifference(bigger: number, smaller: number): number { - const biggerDate = new Date(bigger); - const smallerDate = new Date(smaller); - const yearsDiff = biggerDate.getFullYear() - smallerDate.getFullYear(); - const monthsDiff = biggerDate.getMonth() - smallerDate.getMonth(); - const calendarDifferences = Math.abs(yearsDiff * 12 + monthsDiff); - const compareResult = biggerDate > smallerDate ? 1 : -1; - biggerDate.setMonth( - biggerDate.getMonth() - compareResult * calendarDifferences, - ); - const isLastMonthNotFull = biggerDate > smallerDate - ? 1 - : -1 === -compareResult - ? 1 - : 0; - const months = compareResult * (calendarDifferences - isLastMonthNotFull); - return months === 0 ? 0 : months; -} diff --git a/std/datetime/test.ts b/std/datetime/test.ts deleted file mode 100644 index b2614bc00e..0000000000 --- a/std/datetime/test.ts +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals, assertThrows } from "../testing/asserts.ts"; -import * as datetime from "./mod.ts"; - -Deno.test({ - name: "[std/datetime] parse", - fn: () => { - assertEquals( - datetime.parse("01-03-2019 16:30", "MM-dd-yyyy HH:mm"), - new Date(2019, 0, 3, 16, 30), - ); - assertEquals( - datetime.parse("01.03.2019 16:30", "MM.dd.yyyy HH:mm"), - new Date(2019, 0, 3, 16, 30), - ); - assertEquals( - datetime.parse("01.03.2019 16:30", "MM.dd.yyyy HH:mm"), - new Date(2019, 0, 3, 16, 30), - ); - assertEquals( - datetime.parse("03-01-2019 16:31", "dd-MM-yyyy HH:mm"), - new Date(2019, 0, 3, 16, 31), - ); - assertEquals( - datetime.parse("2019-01-03 16:32", "yyyy-MM-dd HH:mm"), - new Date(2019, 0, 3, 16, 32), - ); - assertEquals( - datetime.parse("16:33 01-03-2019", "HH:mm MM-dd-yyyy"), - new Date(2019, 0, 3, 16, 33), - ); - assertEquals( - datetime.parse("01-03-2019 16:33:23.123", "MM-dd-yyyy HH:mm:ss.SSS"), - new Date(2019, 0, 3, 16, 33, 23, 123), - ); - assertEquals( - datetime.parse("01-03-2019 09:33 PM", "MM-dd-yyyy HH:mm a"), - new Date(2019, 0, 3, 21, 33), - ); - assertEquals( - datetime.parse("16:34 03-01-2019", "HH:mm dd-MM-yyyy"), - new Date(2019, 0, 3, 16, 34), - ); - assertEquals( - datetime.parse("16:35 2019-01-03", "HH:mm yyyy-MM-dd"), - new Date(2019, 0, 3, 16, 35), - ); - assertEquals( - datetime.parse("01-03-2019", "MM-dd-yyyy"), - new Date(2019, 0, 3), - ); - assertEquals( - datetime.parse("03-01-2019", "dd-MM-yyyy"), - new Date(2019, 0, 3), - ); - assertEquals( - datetime.parse("31-10-2019", "dd-MM-yyyy"), - new Date(2019, 9, 31), - ); - assertEquals( - datetime.parse("2019-01-03", "yyyy-MM-dd"), - new Date(2019, 0, 3), - ); - }, -}); - -Deno.test({ - name: "[std/datetime] invalidParseDateTimeFormatThrows", - fn: () => { - assertThrows((): void => { - // deno-lint-ignore no-explicit-any - (datetime as any).parse("2019-01-01 00:00", "x-y-z"); - }, Error); - assertThrows((): void => { - // deno-lint-ignore no-explicit-any - (datetime as any).parse("2019-01-01", "x-y-z"); - }, Error); - }, -}); - -Deno.test({ - name: "[std/datetime] format", - fn: () => { - // 00 hours - assertEquals( - "01:00:00", - datetime.format(new Date("2019-01-01T01:00:00"), "HH:mm:ss"), - ); - assertEquals( - "13:00:00", - datetime.format(new Date("2019-01-01T13:00:00"), "HH:mm:ss"), - ); - - // 12 hours - assertEquals( - "01:00:00", - datetime.format(new Date("2019-01-01T01:00:00"), "hh:mm:ss"), - ); - assertEquals( - "01:00:00", - datetime.format(new Date("2019-01-01T13:00:00"), "hh:mm:ss"), - ); - - // milliseconds - assertEquals( - "13:00:00.000", - datetime.format(new Date("2019-01-01T13:00:00"), "HH:mm:ss.SSS"), - ); - assertEquals( - "13:00:00.000", - datetime.format(new Date("2019-01-01T13:00:00.000"), "HH:mm:ss.SSS"), - ); - assertEquals( - "13:00:00.123", - datetime.format(new Date("2019-01-01T13:00:00.123"), "HH:mm:ss.SSS"), - ); - - // day period - assertEquals( - "01:00:00 AM", - datetime.format(new Date("2019-01-01T01:00:00"), "HH:mm:ss a"), - ); - assertEquals( - "01:00:00 AM", - datetime.format(new Date("2019-01-01T01:00:00"), "hh:mm:ss a"), - ); - assertEquals( - "01:00:00 PM", - datetime.format(new Date("2019-01-01T13:00:00"), "hh:mm:ss a"), - ); - assertEquals( - "21:00:00 PM", - datetime.format(new Date("2019-01-01T21:00:00"), "HH:mm:ss a"), - ); - assertEquals( - "09:00:00 PM", - datetime.format(new Date("2019-01-01T21:00:00"), "hh:mm:ss a"), - ); - - // quoted literal - assertEquals( - datetime.format(new Date(2019, 0, 20), "'today:' yyyy-MM-dd"), - "today: 2019-01-20", - ); - }, -}); - -Deno.test({ - name: "[std/datetime] dayOfYear", - fn: () => { - // from https://golang.org/src/time/time_test.go - // Test YearDay in several different scenarios - // and corner cases - // Non-leap-year tests - assertEquals(datetime.dayOfYear(new Date("2007-01-01T00:00:00.000Z")), 1); - assertEquals(datetime.dayOfYear(new Date("2007-01-15T00:00:00.000Z")), 15); - assertEquals(datetime.dayOfYear(new Date("2007-02-01T00:00:00.000Z")), 32); - assertEquals(datetime.dayOfYear(new Date("2007-02-15T00:00:00.000Z")), 46); - assertEquals(datetime.dayOfYear(new Date("2007-03-01T00:00:00.000Z")), 60); - assertEquals(datetime.dayOfYear(new Date("2007-03-15T00:00:00.000Z")), 74); - assertEquals(datetime.dayOfYear(new Date("2007-04-01T00:00:00.000Z")), 91); - assertEquals(datetime.dayOfYear(new Date("2007-12-31T00:00:00.000Z")), 365); - - // Leap-year tests - assertEquals(datetime.dayOfYear(new Date("2008-01-01T00:00:00.000Z")), 1); - assertEquals(datetime.dayOfYear(new Date("2008-01-15T00:00:00.000Z")), 15); - assertEquals(datetime.dayOfYear(new Date("2008-02-01T00:00:00.000Z")), 32); - assertEquals(datetime.dayOfYear(new Date("2008-02-15T00:00:00.000Z")), 46); - assertEquals(datetime.dayOfYear(new Date("2008-03-01T00:00:00.000Z")), 61); - assertEquals(datetime.dayOfYear(new Date("2008-03-15T00:00:00.000Z")), 75); - assertEquals(datetime.dayOfYear(new Date("2008-04-01T00:00:00.000Z")), 92); - assertEquals(datetime.dayOfYear(new Date("2008-12-31T00:00:00.000Z")), 366); - - // Looks like leap-year (but isn't) tests - assertEquals(datetime.dayOfYear(new Date("1900-01-01T00:00:00.000Z")), 1); - assertEquals(datetime.dayOfYear(new Date("1900-01-15T00:00:00.000Z")), 15); - assertEquals(datetime.dayOfYear(new Date("1900-02-01T00:00:00.000Z")), 32); - assertEquals(datetime.dayOfYear(new Date("1900-02-15T00:00:00.000Z")), 46); - assertEquals(datetime.dayOfYear(new Date("1900-03-01T00:00:00.000Z")), 60); - assertEquals(datetime.dayOfYear(new Date("1900-03-15T00:00:00.000Z")), 74); - assertEquals(datetime.dayOfYear(new Date("1900-04-01T00:00:00.000Z")), 91); - assertEquals(datetime.dayOfYear(new Date("1900-12-31T00:00:00.000Z")), 365); - - // Year one tests (non-leap) - assertEquals(datetime.dayOfYear(new Date("0001-01-01T00:00:00.000Z")), 1); - assertEquals(datetime.dayOfYear(new Date("0001-01-15T00:00:00.000Z")), 15); - assertEquals(datetime.dayOfYear(new Date("0001-02-01T00:00:00.000Z")), 32); - assertEquals(datetime.dayOfYear(new Date("0001-02-15T00:00:00.000Z")), 46); - assertEquals(datetime.dayOfYear(new Date("0001-03-01T00:00:00.000Z")), 60); - assertEquals(datetime.dayOfYear(new Date("0001-03-15T00:00:00.000Z")), 74); - assertEquals(datetime.dayOfYear(new Date("0001-04-01T00:00:00.000Z")), 91); - assertEquals(datetime.dayOfYear(new Date("0001-12-31T00:00:00.000Z")), 365); - - // Year minus one tests (non-leap) - assertEquals( - datetime.dayOfYear(new Date("-000001-01-01T00:00:00.000Z")), - 1, - ); - assertEquals( - datetime.dayOfYear(new Date("-000001-01-15T00:00:00.000Z")), - 15, - ); - assertEquals( - datetime.dayOfYear(new Date("-000001-02-01T00:00:00.000Z")), - 32, - ); - assertEquals( - datetime.dayOfYear(new Date("-000001-02-15T00:00:00.000Z")), - 46, - ); - assertEquals( - datetime.dayOfYear(new Date("-000001-03-01T00:00:00.000Z")), - 60, - ); - assertEquals( - datetime.dayOfYear(new Date("-000001-03-15T00:00:00.000Z")), - 74, - ); - assertEquals( - datetime.dayOfYear(new Date("-000001-04-01T00:00:00.000Z")), - 91, - ); - assertEquals( - datetime.dayOfYear(new Date("-000001-12-31T00:00:00.000Z")), - 365, - ); - - // 400 BC tests (leap-year) - assertEquals( - datetime.dayOfYear(new Date("-000400-01-01T00:00:00.000Z")), - 1, - ); - assertEquals( - datetime.dayOfYear(new Date("-000400-01-15T00:00:00.000Z")), - 15, - ); - assertEquals( - datetime.dayOfYear(new Date("-000400-02-01T00:00:00.000Z")), - 32, - ); - assertEquals( - datetime.dayOfYear(new Date("-000400-02-15T00:00:00.000Z")), - 46, - ); - assertEquals( - datetime.dayOfYear(new Date("-000400-03-01T00:00:00.000Z")), - 61, - ); - assertEquals( - datetime.dayOfYear(new Date("-000400-03-15T00:00:00.000Z")), - 75, - ); - assertEquals( - datetime.dayOfYear(new Date("-000400-04-01T00:00:00.000Z")), - 92, - ); - assertEquals( - datetime.dayOfYear(new Date("-000400-12-31T00:00:00.000Z")), - 366, - ); - - // Special Cases - - // Gregorian calendar change (no effect) - assertEquals(datetime.dayOfYear(new Date("1582-10-04T03:24:00.000Z")), 277); - assertEquals(datetime.dayOfYear(new Date("1582-10-15T03:24:00.000Z")), 288); - }, -}); - -Deno.test({ - name: "[std/datetime] weekOfYear", - fn: () => { - assertEquals(datetime.weekOfYear(new Date("2020-01-05T03:00:00.000Z")), 1); - assertEquals(datetime.weekOfYear(new Date("2020-06-28T03:00:00.000Z")), 26); - - // iso weeks year starting sunday - assertEquals(datetime.weekOfYear(new Date(2012, 0, 1)), 52); - assertEquals(datetime.weekOfYear(new Date(2012, 0, 2)), 1); - assertEquals(datetime.weekOfYear(new Date(2012, 0, 8)), 1); - assertEquals(datetime.weekOfYear(new Date(2012, 0, 9)), 2); - assertEquals(datetime.weekOfYear(new Date(2012, 0, 15)), 2); - - // iso weeks year starting monday - assertEquals(datetime.weekOfYear(new Date(2007, 0, 1)), 1); - assertEquals(datetime.weekOfYear(new Date(2007, 0, 7)), 1); - assertEquals(datetime.weekOfYear(new Date(2007, 0, 8)), 2); - assertEquals(datetime.weekOfYear(new Date(2007, 0, 14)), 2); - assertEquals(datetime.weekOfYear(new Date(2007, 0, 15)), 3); - - // iso weeks year starting tuesday - assertEquals(datetime.weekOfYear(new Date(2007, 11, 31)), 1); - assertEquals(datetime.weekOfYear(new Date(2008, 0, 1)), 1); - assertEquals(datetime.weekOfYear(new Date(2008, 0, 6)), 1); - assertEquals(datetime.weekOfYear(new Date(2008, 0, 7)), 2); - assertEquals(datetime.weekOfYear(new Date(2008, 0, 13)), 2); - assertEquals(datetime.weekOfYear(new Date(2008, 0, 14)), 3); - - // iso weeks year starting wednesday - assertEquals(datetime.weekOfYear(new Date(2002, 11, 30)), 1); - assertEquals(datetime.weekOfYear(new Date(2003, 0, 1)), 1); - assertEquals(datetime.weekOfYear(new Date(2003, 0, 5)), 1); - assertEquals(datetime.weekOfYear(new Date(2003, 0, 6)), 2); - assertEquals(datetime.weekOfYear(new Date(2003, 0, 12)), 2); - assertEquals(datetime.weekOfYear(new Date(2003, 0, 13)), 3); - - // iso weeks year starting thursday - assertEquals(datetime.weekOfYear(new Date(2008, 11, 29)), 1); - assertEquals(datetime.weekOfYear(new Date(2009, 0, 1)), 1); - assertEquals(datetime.weekOfYear(new Date(2009, 0, 4)), 1); - assertEquals(datetime.weekOfYear(new Date(2009, 0, 5)), 2); - assertEquals(datetime.weekOfYear(new Date(2009, 0, 11)), 2); - assertEquals(datetime.weekOfYear(new Date(2009, 0, 13)), 3); - - // iso weeks year starting friday - assertEquals(datetime.weekOfYear(new Date(2009, 11, 28)), 53); - assertEquals(datetime.weekOfYear(new Date(2010, 0, 1)), 53); - assertEquals(datetime.weekOfYear(new Date(2010, 0, 3)), 53); - assertEquals(datetime.weekOfYear(new Date(2010, 0, 4)), 1); - assertEquals(datetime.weekOfYear(new Date(2010, 0, 10)), 1); - assertEquals(datetime.weekOfYear(new Date(2010, 0, 11)), 2); - - // iso weeks year starting saturday - assertEquals(datetime.weekOfYear(new Date(2010, 11, 27)), 52); - assertEquals(datetime.weekOfYear(new Date(2011, 0, 1)), 52); - assertEquals(datetime.weekOfYear(new Date(2011, 0, 2)), 52); - assertEquals(datetime.weekOfYear(new Date(2011, 0, 3)), 1); - assertEquals(datetime.weekOfYear(new Date(2011, 0, 9)), 1); - assertEquals(datetime.weekOfYear(new Date(2011, 0, 10)), 2); - }, -}); - -Deno.test({ - name: "[std/datetime] to IMF", - fn(): void { - const actual = datetime.toIMF(new Date(Date.UTC(1994, 3, 5, 15, 32))); - const expected = "Tue, 05 Apr 1994 15:32:00 GMT"; - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[std/datetime] to IMF 0", - fn(): void { - const actual = datetime.toIMF(new Date(0)); - const expected = "Thu, 01 Jan 1970 00:00:00 GMT"; - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[std/datetime] isLeap", - fn(): void { - assert(datetime.isLeap(1992)); - assert(datetime.isLeap(2000)); - assert(!datetime.isLeap(2003)); - assert(!datetime.isLeap(2007)); - }, -}); - -Deno.test({ - name: "[std/datetime] difference", - fn(): void { - const denoInit = new Date("2018/5/14"); - const denoReleaseV1 = new Date("2020/5/13"); - let difference = datetime.difference(denoReleaseV1, denoInit, { - units: ["days", "months", "years"], - }); - assertEquals(difference.days, 730); - assertEquals(difference.months, 23); - assertEquals(difference.years, 1); - - const birth = new Date("1998/2/23 10:10:10"); - const old = new Date("1998/2/23 11:11:11"); - difference = datetime.difference(birth, old, { - units: ["milliseconds", "minutes", "seconds", "hours"], - }); - assertEquals(difference.milliseconds, 3661000); - assertEquals(difference.seconds, 3661); - assertEquals(difference.minutes, 61); - assertEquals(difference.hours, 1); - }, -}); - -Deno.test({ - name: "[std/datetime] constants", - fn(): void { - assertEquals(datetime.SECOND, 1e3); - assertEquals(datetime.MINUTE, datetime.SECOND * 60); - assertEquals(datetime.HOUR, datetime.MINUTE * 60); - assertEquals(datetime.DAY, datetime.HOUR * 24); - assertEquals(datetime.WEEK, datetime.DAY * 7); - }, -}); diff --git a/std/datetime/tokenizer.ts b/std/datetime/tokenizer.ts deleted file mode 100644 index 9a9d0daa8a..0000000000 --- a/std/datetime/tokenizer.ts +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -export type Token = { - type: string; - value: string | number; - index: number; - [key: string]: unknown; -}; - -export interface ReceiverResult { - [name: string]: string | number | unknown; -} -export type CallbackResult = { - type: string; - value: string | number; - [key: string]: unknown; -}; -type CallbackFunction = (value: unknown) => CallbackResult; - -export type TestResult = { value: unknown; length: number } | undefined; -export type TestFunction = ( - string: string, -) => TestResult | undefined; - -export interface Rule { - test: TestFunction; - fn: CallbackFunction; -} - -export class Tokenizer { - rules: Rule[]; - - constructor(rules: Rule[] = []) { - this.rules = rules; - } - - addRule(test: TestFunction, fn: CallbackFunction): Tokenizer { - this.rules.push({ test, fn }); - return this; - } - - tokenize( - string: string, - receiver = (token: Token): ReceiverResult => token, - ): ReceiverResult[] { - function* generator(rules: Rule[]): IterableIterator { - let index = 0; - for (const rule of rules) { - const result = rule.test(string); - if (result) { - const { value, length } = result; - index += length; - string = string.slice(length); - const token = { ...rule.fn(value), index }; - yield receiver(token); - yield* generator(rules); - } - } - } - const tokenGenerator = generator(this.rules); - - const tokens: ReceiverResult[] = []; - - for (const token of tokenGenerator) { - tokens.push(token); - } - - if (string.length) { - throw new Error( - `parser error: string not fully parsed! ${string.slice(0, 25)}`, - ); - } - - return tokens; - } -} diff --git a/std/encoding/README.md b/std/encoding/README.md deleted file mode 100644 index 3c60c6fcd2..0000000000 --- a/std/encoding/README.md +++ /dev/null @@ -1,575 +0,0 @@ -# encoding - -Helper module for dealing with external data structures. - -- [`ascii85`](#ascii85) -- [`base32`](#base32) -- [`binary`](#binary) -- [`csv`](#csv) -- [`toml`](#toml) -- [`yaml`](#yaml) - -## Binary - -Implements equivalent methods to Go's `encoding/binary` package. - -Available Functions: - -```typescript -sizeof(dataType: RawTypes): number -getNBytes(r: Deno.Reader, n: number): Promise -varnum(b: Uint8Array, o: VarnumOptions = {}): number | null -varbig(b: Uint8Array, o: VarbigOptions = {}): bigint | null -putVarnum(b: Uint8Array, x: number, o: VarnumOptions = {}): number -putVarbig(b: Uint8Array, x: bigint, o: VarbigOptions = {}): number -readVarnum(r: Deno.Reader, o: VarnumOptions = {}): Promise -readVarbig(r: Deno.Reader, o: VarbigOptions = {}): Promise -writeVarnum(w: Deno.Writer, x: number, o: VarnumOptions = {}): Promise -writeVarbig(w: Deno.Writer, x: bigint, o: VarbigOptions = {}): Promise -``` - -## CSV - -### API - -#### `readMatrix` - -```ts -(reader: BufReader, opt: ReadOptions = { - comma: ",", - trimLeadingSpace: false, - lazyQuotes: false, -}): Promise -``` - -Parse the CSV from the `reader` with the options provided and return -`string[][]`. - -#### `parse` - -```ts -(input: string | BufReader, opt: ParseOptions = { skipFirstRow: false }): Promise -``` - -Parse the CSV string/buffer with the options provided. The result of this -function is as follows: - -- If you don't provide `opt.skipFirstRow`, `opt.parse`, and `opt.columns`, it - returns `string[][]`. -- If you provide `opt.skipFirstRow` or `opt.columns` but not `opt.parse`, it - returns `object[]`. -- If you provide `opt.parse`, it returns an array where each element is the - value returned from `opt.parse`. - -##### `ParseOptions` - -- **`skipFirstRow: boolean;`**: If you provide `skipFirstRow: true` and - `columns`, the first line will be skipped. If you provide `skipFirstRow: true` - but not `columns`, the first line will be skipped and used as header - definitions. -- **`columns: string[] | HeaderOptions[];`**: If you provide `string[]` or - `ColumnOptions[]`, those names will be used for header definition. -- **`parse?: (input: unknown) => unknown;`**: Parse function for the row, which - will be executed after parsing of all columns. Therefore if you don't provide - `skipFirstRow`, `columns`, and `parse` function, input will be `string[]`. - -##### `HeaderOptions` - -- **`name: string;`**: Name of the header to be used as property. -- **`parse?: (input: string) => unknown;`**: Parse function for the column. This - is executed on each entry of the header. This can be combined with the Parse - function of the rows. - -##### `ReadOptions` - -- **`comma?: string;`**: Character which separates values. Default: `","`. -- **`comment?: string;`**: Character to start a comment. Default: `"#"`. -- **`trimLeadingSpace?: boolean;`**: Flag to trim the leading space of the - value. Default: `false`. -- **`lazyQuotes?: boolean;`**: Allow unquoted quote in a quoted field or non - double quoted quotes in quoted field. Default: `false`. -- **`fieldsPerRecord?`**: Enabling the check of fields for each row. If == 0, - first row is used as referral for the number of fields. - -#### `stringify` - -```ts -(data: DataItem[], columns: Column[], options?: StringifyOptions): Promise -``` - -- **`data`** is the source data to stringify. It's an array of items which are - plain objects or arrays. - - `DataItem: Record | unknown[]` - - ```ts - const data = [ - { - name: "Deno", - repo: { org: "denoland", name: "deno" }, - runsOn: ["Rust", "TypeScript"], - }, - ]; - ``` - -- **`columns`** is a list of instructions for how to target and transform the - data for each column of output. This is also where you can provide an explicit - header name for the column. - - `Column`: - - - The most essential aspect of a column is accessing the property holding the - data for that column on each object in the data array. If that member is at - the top level, `Column` can simply be a property accessor, which is either a - `string` (if it's a plain object) or a `number` (if it's an array). - - ```ts - const columns = [ - "name", - ]; - ``` - - Each property accessor will be used as the header for the column: - - | name | - | :--: | - | Deno | - - - If the required data is not at the top level (it's nested in other - objects/arrays), then a simple property accessor won't work, so an array of - them will be required. - - ```ts - const columns = [ - ["repo", "name"], - ["repo", "org"], - ]; - ``` - - When using arrays of property accessors, the header names inherit the value - of the last accessor in each array: - - | name | org | - | :--: | :------: | - | deno | denoland | - - - If the data is not already in the required output format, or a different - column header is desired, then a `ColumnDetails` object type can be used for - each column: - - - **`fn?: (value: any) => string | Promise`** is an optional - function to transform the targeted data into the desired format - - - **`header?: string`** is the optional value to use for the column header - name - - - **`prop: PropertyAccessor | PropertyAccessor[]`** is the property accessor - (`string` or `number`) or array of property accessors used to access the - data on each object - - ```ts - const columns = [ - "name", - { - prop: ["runsOn", 0], - header: "language 1", - fn: (str: string) => str.toLowerCase(), - }, - { - prop: ["runsOn", 1], - header: "language 2", - fn: (str: string) => str.toLowerCase(), - }, - ]; - ``` - - | name | language 1 | language 2 | - | :--: | :--------: | :--------: | - | Deno | rust | typescript | - -- **`options`** are options for the delimiter-separated output. - - - **`headers?: boolean`**: Whether or not to include the row of headers. - Default: `true` - - - **`separator?: string`**: Delimiter used to separate values. Examples: - - `","` _comma_ (Default) - - `"\t"` _tab_ - - `"|"` _pipe_ - - etc. - -### Basic Usage - -```ts -import { parse } from "https://deno.land/std@$STD_VERSION/encoding/csv.ts"; -const string = "a,b,c\nd,e,f"; - -console.log( - await parse(string, { - skipFirstRow: false, - }), -); -// output: -// [["a", "b", "c"], ["d", "e", "f"]] -``` - -```ts -import { - Column, - stringify, -} from "https://deno.land/std@$STD_VERSION/encoding/csv.ts"; - -type Character = { - age: number; - name: { - first: string; - last: string; - }; -}; - -const data: Character[] = [ - { - age: 70, - name: { - first: "Rick", - last: "Sanchez", - }, - }, - { - age: 14, - name: { - first: "Morty", - last: "Smith", - }, - }, -]; - -let columns: Column[] = [ - ["name", "first"], - "age", -]; - -console.log(await stringify(data, columns)); -// first,age -// Rick,70 -// Morty,14 -// - -columns = [ - { - prop: "name", - fn: (name: Character["name"]) => `${name.first} ${name.last}`, - }, - { - prop: "age", - header: "is_adult", - fn: (age: Character["age"]) => String(age >= 18), - }, -]; - -console.log(await stringify(data, columns, { separator: "\t" })); -// name is_adult -// Rick Sanchez true -// Morty Smith false -// -``` - -## TOML - -This module parse TOML files. It follows as much as possible the -[TOML specs](https://toml.io/en/latest). Be sure to read the supported types as -not every specs is supported at the moment and the handling in TypeScript side -is a bit different. - -### Supported types and handling - -- :heavy_check_mark: [Keys](https://toml.io/en/latest#keys) -- :exclamation: [String](https://toml.io/en/latest#string) -- :heavy_check_mark: [Multiline String](https://toml.io/en/latest#string) -- :heavy_check_mark: [Literal String](https://toml.io/en/latest#string) -- :exclamation: [Integer](https://toml.io/en/latest#integer) -- :heavy_check_mark: [Float](https://toml.io/en/latest#float) -- :heavy_check_mark: [Boolean](https://toml.io/en/latest#boolean) -- :heavy_check_mark: - [Offset Date-time](https://toml.io/en/latest#offset-date-time) -- :heavy_check_mark: - [Local Date-time](https://toml.io/en/latest#local-date-time) -- :heavy_check_mark: [Local Date](https://toml.io/en/latest#local-date) -- :exclamation: [Local Time](https://toml.io/en/latest#local-time) -- :heavy_check_mark: [Table](https://toml.io/en/latest#table) -- :heavy_check_mark: [Inline Table](https://toml.io/en/latest#inline-table) -- :exclamation: [Array of Tables](https://toml.io/en/latest#array-of-tables) - -:exclamation: _Supported with warnings see [Warning](#Warning)._ - -#### :warning: Warning - -##### String - -- Regex : Due to the spec, there is no flag to detect regex properly in a TOML - declaration. So the regex is stored as string. - -##### Integer - -For **Binary** / **Octal** / **Hexadecimal** numbers, they are stored as string -to be not interpreted as Decimal. - -##### Local Time - -Because local time does not exist in JavaScript, the local time is stored as a -string. - -##### Inline Table - -Inline tables are supported. See below: - -```toml -animal = { type = { name = "pug" } } -## Output { animal: { type: { name: "pug" } } } -animal = { type.name = "pug" } -## Output { animal: { type : { name : "pug" } } -animal.as.leaders = "tosin" -## Output { animal: { as: { leaders: "tosin" } } } -"tosin.abasi" = "guitarist" -## Output { tosin.abasi: "guitarist" } -``` - -##### Array of Tables - -At the moment only simple declarations like below are supported: - -```toml -[[bin]] -name = "deno" -path = "cli/main.rs" - -[[bin]] -name = "deno_core" -path = "src/foo.rs" - -[[nib]] -name = "node" -path = "not_found" -``` - -will output: - -```json -{ - "bin": [ - { "name": "deno", "path": "cli/main.rs" }, - { "name": "deno_core", "path": "src/foo.rs" } - ], - "nib": [{ "name": "node", "path": "not_found" }] -} -``` - -### Basic usage - -```ts -import { - parse, - stringify, -} from "https://deno.land/std@$STD_VERSION/encoding/toml.ts"; -const obj = { - bin: [ - { name: "deno", path: "cli/main.rs" }, - { name: "deno_core", path: "src/foo.rs" }, - ], - nib: [{ name: "node", path: "not_found" }], -}; -const tomlString = stringify(obj); -console.log(tomlString); - -// => -// [[bin]] -// name = "deno" -// path = "cli/main.rs" - -// [[bin]] -// name = "deno_core" -// path = "src/foo.rs" - -// [[nib]] -// name = "node" -// path = "not_found" - -const tomlObject = parse(tomlString); -console.log(tomlObject); - -// => -// { -// bin: [ -// { name: "deno", path: "cli/main.rs" }, -// { name: "deno_core", path: "src/foo.rs" } -// ], -// nib: [ { name: "node", path: "not_found" } ] -// } -``` - -## YAML - -YAML parser / dumper for Deno. - -Heavily inspired from [`js-yaml`](https://github.com/nodeca/js-yaml). - -### Basic usage - -`parse` parses the yaml string, and `stringify` dumps the given object to YAML -string. - -```ts -import { - parse, - stringify, -} from "https://deno.land/std@$STD_VERSION/encoding/yaml.ts"; - -const data = parse(` -foo: bar -baz: - - qux - - quux -`); -console.log(data); -// => { foo: "bar", baz: [ "qux", "quux" ] } - -const yaml = stringify({ foo: "bar", baz: ["qux", "quux"] }); -console.log(yaml); -// => -// foo: bar -// baz: -// - qux -// - quux -``` - -If your YAML contains multiple documents in it, you can use `parseAll` for -handling it. - -```ts -import { parseAll } from "https://deno.land/std@$STD_VERSION/encoding/yaml.ts"; - -const data = parseAll(` ---- -id: 1 -name: Alice ---- -id: 2 -name: Bob ---- -id: 3 -name: Eve -`); -console.log(data); -// => [ { id: 1, name: "Alice" }, { id: 2, name: "Bob" }, { id: 3, name: "Eve" } ] -``` - -### API - -#### `parse(str: string, opts?: ParserOption): unknown` - -Parses the YAML string with a single document. - -#### `parseAll(str: string, iterator?: Function, opts?: ParserOption): unknown` - -Parses the YAML string with multiple documents. If the iterator is given, it's -applied to every document instead of returning the array of parsed objects. - -#### `stringify(obj: object, opts?: DumpOption): string` - -Serializes `object` as a YAML document. - -### :warning: Limitations - -- `binary` type is currently not stable. -- `function`, `regexp`, and `undefined` type are currently not supported. - -### More example - -See: https://github.com/nodeca/js-yaml - -## base32 - -[RFC4648 base32](https://tools.ietf.org/html/rfc4648#section-6) encoder/decoder -for Deno. - -### Basic usage - -`encode` encodes a `Uint8Array` to RFC4648 base32 representation, and `decode` -decodes the given RFC4648 base32 representation to a `Uint8Array`. - -```ts -import { - decode, - encode, -} from "https://deno.land/std@$STD_VERSION/encoding/base32.ts"; - -const b32Repr = "RC2E6GA="; - -const binaryData = decode(b32Repr); -console.log(binaryData); -// => Uint8Array [ 136, 180, 79, 24 ] - -console.log(encode(binaryData)); -// => RC2E6GA= -``` - -## ascii85 - -Ascii85/base85 encoder and decoder with support for multiple standards. - -### Basic usage - -`encode` encodes a `Uint8Array` to a ascii85 representation, and `decode` -decodes the given ascii85 representation to a `Uint8Array`. - -```ts -import { - decode, - encode, -} from "https://deno.land/std@$STD_VERSION/encoding/ascii85.ts"; - -const a85Repr = "LpTqp"; - -const binaryData = decode(a85Repr); -console.log(binaryData); -// => Uint8Array [ 136, 180, 79, 24 ] - -console.log(encode(binaryData)); -// => LpTqp -``` - -### Specifying a standard and delimiter - -By default all functions are using the most popular Adobe version of ascii85 and -not adding any delimiter. However, there are three more standards supported - -btoa (different delimiter and additional compression of 4 bytes equal to 32), -[Z85](https://rfc.zeromq.org/spec/32/) and -[RFC 1924](https://tools.ietf.org/html/rfc1924). It's possible to use a -different encoding by specifying it in `options` object as a second parameter. - -Similarly, it's possible to make `encode` add a delimiter (`<~` and `~>` for -Adobe, `xbtoa Begin` and `xbtoa End` with newlines between the delimiters and -encoded data for btoa. Checksums for btoa are not supported. Delimiters are not -supported by other encodings.) - -encoding examples: - -```ts -import { - decode, - encode, -} from "https://deno.land/std@$STD_VERSION/encoding/ascii85.ts"; -const binaryData = new Uint8Array([136, 180, 79, 24]); -console.log(encode(binaryData)); -// => LpTqp -console.log(encode(binaryData, { standard: "Adobe", delimiter: true })); -// => <~LpTqp~> -console.log(encode(binaryData, { standard: "btoa", delimiter: true })); -/* => xbtoa Begin -LpTqp -xbtoa End */ -console.log(encode(binaryData, { standard: "RFC 1924" })); -// => h_p`_ -console.log(encode(binaryData, { standard: "Z85" })); -// => H{P}{ -``` diff --git a/std/encoding/_yaml/dumper/dumper.ts b/std/encoding/_yaml/dumper/dumper.ts deleted file mode 100644 index 05dc562627..0000000000 --- a/std/encoding/_yaml/dumper/dumper.ts +++ /dev/null @@ -1,896 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { YAMLError } from "../error.ts"; -import type { RepresentFn, StyleVariant, Type } from "../type.ts"; -import * as common from "../utils.ts"; -import { DumperState, DumperStateOptions } from "./dumper_state.ts"; - -type Any = common.Any; -type ArrayObject = common.ArrayObject; - -const _toString = Object.prototype.toString; -const _hasOwnProperty = Object.prototype.hasOwnProperty; - -const CHAR_TAB = 0x09; /* Tab */ -const CHAR_LINE_FEED = 0x0a; /* LF */ -const CHAR_SPACE = 0x20; /* Space */ -const CHAR_EXCLAMATION = 0x21; /* ! */ -const CHAR_DOUBLE_QUOTE = 0x22; /* " */ -const CHAR_SHARP = 0x23; /* # */ -const CHAR_PERCENT = 0x25; /* % */ -const CHAR_AMPERSAND = 0x26; /* & */ -const CHAR_SINGLE_QUOTE = 0x27; /* ' */ -const CHAR_ASTERISK = 0x2a; /* * */ -const CHAR_COMMA = 0x2c; /* , */ -const CHAR_MINUS = 0x2d; /* - */ -const CHAR_COLON = 0x3a; /* : */ -const CHAR_GREATER_THAN = 0x3e; /* > */ -const CHAR_QUESTION = 0x3f; /* ? */ -const CHAR_COMMERCIAL_AT = 0x40; /* @ */ -const CHAR_LEFT_SQUARE_BRACKET = 0x5b; /* [ */ -const CHAR_RIGHT_SQUARE_BRACKET = 0x5d; /* ] */ -const CHAR_GRAVE_ACCENT = 0x60; /* ` */ -const CHAR_LEFT_CURLY_BRACKET = 0x7b; /* { */ -const CHAR_VERTICAL_LINE = 0x7c; /* | */ -const CHAR_RIGHT_CURLY_BRACKET = 0x7d; /* } */ - -const ESCAPE_SEQUENCES: { [char: number]: string } = {}; - -ESCAPE_SEQUENCES[0x00] = "\\0"; -ESCAPE_SEQUENCES[0x07] = "\\a"; -ESCAPE_SEQUENCES[0x08] = "\\b"; -ESCAPE_SEQUENCES[0x09] = "\\t"; -ESCAPE_SEQUENCES[0x0a] = "\\n"; -ESCAPE_SEQUENCES[0x0b] = "\\v"; -ESCAPE_SEQUENCES[0x0c] = "\\f"; -ESCAPE_SEQUENCES[0x0d] = "\\r"; -ESCAPE_SEQUENCES[0x1b] = "\\e"; -ESCAPE_SEQUENCES[0x22] = '\\"'; -ESCAPE_SEQUENCES[0x5c] = "\\\\"; -ESCAPE_SEQUENCES[0x85] = "\\N"; -ESCAPE_SEQUENCES[0xa0] = "\\_"; -ESCAPE_SEQUENCES[0x2028] = "\\L"; -ESCAPE_SEQUENCES[0x2029] = "\\P"; - -const DEPRECATED_BOOLEANS_SYNTAX = [ - "y", - "Y", - "yes", - "Yes", - "YES", - "on", - "On", - "ON", - "n", - "N", - "no", - "No", - "NO", - "off", - "Off", - "OFF", -]; - -function encodeHex(character: number): string { - const string = character.toString(16).toUpperCase(); - - let handle: string; - let length: number; - if (character <= 0xff) { - handle = "x"; - length = 2; - } else if (character <= 0xffff) { - handle = "u"; - length = 4; - } else if (character <= 0xffffffff) { - handle = "U"; - length = 8; - } else { - throw new YAMLError( - "code point within a string may not be greater than 0xFFFFFFFF", - ); - } - - return `\\${handle}${common.repeat("0", length - string.length)}${string}`; -} - -// Indents every line in a string. Empty lines (\n only) are not indented. -function indentString(string: string, spaces: number): string { - const ind = common.repeat(" ", spaces), - length = string.length; - let position = 0, - next = -1, - result = "", - line: string; - - while (position < length) { - next = string.indexOf("\n", position); - if (next === -1) { - line = string.slice(position); - position = length; - } else { - line = string.slice(position, next + 1); - position = next + 1; - } - - if (line.length && line !== "\n") result += ind; - - result += line; - } - - return result; -} - -function generateNextLine(state: DumperState, level: number): string { - return `\n${common.repeat(" ", state.indent * level)}`; -} - -function testImplicitResolving(state: DumperState, str: string): boolean { - let type: Type; - for ( - let index = 0, length = state.implicitTypes.length; - index < length; - index += 1 - ) { - type = state.implicitTypes[index]; - - if (type.resolve(str)) { - return true; - } - } - - return false; -} - -// [33] s-white ::= s-space | s-tab -function isWhitespace(c: number): boolean { - return c === CHAR_SPACE || c === CHAR_TAB; -} - -// Returns true if the character can be printed without escaping. -// From YAML 1.2: "any allowed characters known to be non-printable -// should also be escaped. [However,] This isn’t mandatory" -// Derived from nb-char - \t - #x85 - #xA0 - #x2028 - #x2029. -function isPrintable(c: number): boolean { - return ( - (0x00020 <= c && c <= 0x00007e) || - (0x000a1 <= c && c <= 0x00d7ff && c !== 0x2028 && c !== 0x2029) || - (0x0e000 <= c && c <= 0x00fffd && c !== 0xfeff) /* BOM */ || - (0x10000 <= c && c <= 0x10ffff) - ); -} - -// Simplified test for values allowed after the first character in plain style. -function isPlainSafe(c: number): boolean { - // Uses a subset of nb-char - c-flow-indicator - ":" - "#" - // where nb-char ::= c-printable - b-char - c-byte-order-mark. - return ( - isPrintable(c) && - c !== 0xfeff && - // - c-flow-indicator - c !== CHAR_COMMA && - c !== CHAR_LEFT_SQUARE_BRACKET && - c !== CHAR_RIGHT_SQUARE_BRACKET && - c !== CHAR_LEFT_CURLY_BRACKET && - c !== CHAR_RIGHT_CURLY_BRACKET && - // - ":" - "#" - c !== CHAR_COLON && - c !== CHAR_SHARP - ); -} - -// Simplified test for values allowed as the first character in plain style. -function isPlainSafeFirst(c: number): boolean { - // Uses a subset of ns-char - c-indicator - // where ns-char = nb-char - s-white. - return ( - isPrintable(c) && - c !== 0xfeff && - !isWhitespace(c) && // - s-white - // - (c-indicator ::= - // “-” | “?” | “:” | “,” | “[” | “]” | “{” | “}” - c !== CHAR_MINUS && - c !== CHAR_QUESTION && - c !== CHAR_COLON && - c !== CHAR_COMMA && - c !== CHAR_LEFT_SQUARE_BRACKET && - c !== CHAR_RIGHT_SQUARE_BRACKET && - c !== CHAR_LEFT_CURLY_BRACKET && - c !== CHAR_RIGHT_CURLY_BRACKET && - // | “#” | “&” | “*” | “!” | “|” | “>” | “'” | “"” - c !== CHAR_SHARP && - c !== CHAR_AMPERSAND && - c !== CHAR_ASTERISK && - c !== CHAR_EXCLAMATION && - c !== CHAR_VERTICAL_LINE && - c !== CHAR_GREATER_THAN && - c !== CHAR_SINGLE_QUOTE && - c !== CHAR_DOUBLE_QUOTE && - // | “%” | “@” | “`”) - c !== CHAR_PERCENT && - c !== CHAR_COMMERCIAL_AT && - c !== CHAR_GRAVE_ACCENT - ); -} - -// Determines whether block indentation indicator is required. -function needIndentIndicator(string: string): boolean { - const leadingSpaceRe = /^\n* /; - return leadingSpaceRe.test(string); -} - -const STYLE_PLAIN = 1, - STYLE_SINGLE = 2, - STYLE_LITERAL = 3, - STYLE_FOLDED = 4, - STYLE_DOUBLE = 5; - -// Determines which scalar styles are possible and returns the preferred style. -// lineWidth = -1 => no limit. -// Pre-conditions: str.length > 0. -// Post-conditions: -// STYLE_PLAIN or STYLE_SINGLE => no \n are in the string. -// STYLE_LITERAL => no lines are suitable for folding (or lineWidth is -1). -// STYLE_FOLDED => a line > lineWidth and can be folded (and lineWidth != -1). -function chooseScalarStyle( - string: string, - singleLineOnly: boolean, - indentPerLevel: number, - lineWidth: number, - testAmbiguousType: (...args: Any[]) => Any, -): number { - const shouldTrackWidth = lineWidth !== -1; - let hasLineBreak = false, - hasFoldableLine = false, // only checked if shouldTrackWidth - previousLineBreak = -1, // count the first line correctly - plain = isPlainSafeFirst(string.charCodeAt(0)) && - !isWhitespace(string.charCodeAt(string.length - 1)); - - let char: number, i: number; - if (singleLineOnly) { - // Case: no block styles. - // Check for disallowed characters to rule out plain and single. - for (i = 0; i < string.length; i++) { - char = string.charCodeAt(i); - if (!isPrintable(char)) { - return STYLE_DOUBLE; - } - plain = plain && isPlainSafe(char); - } - } else { - // Case: block styles permitted. - for (i = 0; i < string.length; i++) { - char = string.charCodeAt(i); - if (char === CHAR_LINE_FEED) { - hasLineBreak = true; - // Check if any line can be folded. - if (shouldTrackWidth) { - hasFoldableLine = hasFoldableLine || - // Foldable line = too long, and not more-indented. - (i - previousLineBreak - 1 > lineWidth && - string[previousLineBreak + 1] !== " "); - previousLineBreak = i; - } - } else if (!isPrintable(char)) { - return STYLE_DOUBLE; - } - plain = plain && isPlainSafe(char); - } - // in case the end is missing a \n - hasFoldableLine = hasFoldableLine || - (shouldTrackWidth && - i - previousLineBreak - 1 > lineWidth && - string[previousLineBreak + 1] !== " "); - } - // Although every style can represent \n without escaping, prefer block styles - // for multiline, since they're more readable and they don't add empty lines. - // Also prefer folding a super-long line. - if (!hasLineBreak && !hasFoldableLine) { - // Strings interpretable as another type have to be quoted; - // e.g. the string 'true' vs. the boolean true. - return plain && !testAmbiguousType(string) ? STYLE_PLAIN : STYLE_SINGLE; - } - // Edge case: block indentation indicator can only have one digit. - if (indentPerLevel > 9 && needIndentIndicator(string)) { - return STYLE_DOUBLE; - } - // At this point we know block styles are valid. - // Prefer literal style unless we want to fold. - return hasFoldableLine ? STYLE_FOLDED : STYLE_LITERAL; -} - -// Greedy line breaking. -// Picks the longest line under the limit each time, -// otherwise settles for the shortest line over the limit. -// NB. More-indented lines *cannot* be folded, as that would add an extra \n. -function foldLine(line: string, width: number): string { - if (line === "" || line[0] === " ") return line; - - // Since a more-indented line adds a \n, breaks can't be followed by a space. - const breakRe = / [^ ]/g; // note: the match index will always be <= length-2. - let match; - // start is an inclusive index. end, curr, and next are exclusive. - let start = 0, - end, - curr = 0, - next = 0; - let result = ""; - - // Invariants: 0 <= start <= length-1. - // 0 <= curr <= next <= max(0, length-2). curr - start <= width. - // Inside the loop: - // A match implies length >= 2, so curr and next are <= length-2. - // tslint:disable-next-line:no-conditional-assignment - while ((match = breakRe.exec(line))) { - next = match.index; - // maintain invariant: curr - start <= width - if (next - start > width) { - end = curr > start ? curr : next; // derive end <= length-2 - result += `\n${line.slice(start, end)}`; - // skip the space that was output as \n - start = end + 1; // derive start <= length-1 - } - curr = next; - } - - // By the invariants, start <= length-1, so there is something left over. - // It is either the whole string or a part starting from non-whitespace. - result += "\n"; - // Insert a break if the remainder is too long and there is a break available. - if (line.length - start > width && curr > start) { - result += `${line.slice(start, curr)}\n${line.slice(curr + 1)}`; - } else { - result += line.slice(start); - } - - return result.slice(1); // drop extra \n joiner -} - -// (See the note for writeScalar.) -function dropEndingNewline(string: string): string { - return string[string.length - 1] === "\n" ? string.slice(0, -1) : string; -} - -// Note: a long line without a suitable break point will exceed the width limit. -// Pre-conditions: every char in str isPrintable, str.length > 0, width > 0. -function foldString(string: string, width: number): string { - // In folded style, $k$ consecutive newlines output as $k+1$ newlines— - // unless they're before or after a more-indented line, or at the very - // beginning or end, in which case $k$ maps to $k$. - // Therefore, parse each chunk as newline(s) followed by a content line. - const lineRe = /(\n+)([^\n]*)/g; - - // first line (possibly an empty line) - let result = ((): string => { - let nextLF = string.indexOf("\n"); - nextLF = nextLF !== -1 ? nextLF : string.length; - lineRe.lastIndex = nextLF; - // eslint-disable-next-line @typescript-eslint/no-use-before-define - return foldLine(string.slice(0, nextLF), width); - })(); - // If we haven't reached the first content line yet, don't add an extra \n. - let prevMoreIndented = string[0] === "\n" || string[0] === " "; - let moreIndented; - - // rest of the lines - let match; - // tslint:disable-next-line:no-conditional-assignment - while ((match = lineRe.exec(string))) { - const prefix = match[1], - line = match[2]; - moreIndented = line[0] === " "; - result += prefix + - (!prevMoreIndented && !moreIndented && line !== "" ? "\n" : "") + - // eslint-disable-next-line @typescript-eslint/no-use-before-define - foldLine(line, width); - prevMoreIndented = moreIndented; - } - - return result; -} - -// Escapes a double-quoted string. -function escapeString(string: string): string { - let result = ""; - let char, nextChar; - let escapeSeq; - - for (let i = 0; i < string.length; i++) { - char = string.charCodeAt(i); - // Check for surrogate pairs (reference Unicode 3.0 section "3.7 Surrogates"). - if (char >= 0xd800 && char <= 0xdbff /* high surrogate */) { - nextChar = string.charCodeAt(i + 1); - if (nextChar >= 0xdc00 && nextChar <= 0xdfff /* low surrogate */) { - // Combine the surrogate pair and store it escaped. - result += encodeHex( - (char - 0xd800) * 0x400 + nextChar - 0xdc00 + 0x10000, - ); - // Advance index one extra since we already used that char here. - i++; - continue; - } - } - escapeSeq = ESCAPE_SEQUENCES[char]; - result += !escapeSeq && isPrintable(char) - ? string[i] - : escapeSeq || encodeHex(char); - } - - return result; -} - -// Pre-conditions: string is valid for a block scalar, 1 <= indentPerLevel <= 9. -function blockHeader(string: string, indentPerLevel: number): string { - const indentIndicator = needIndentIndicator(string) - ? String(indentPerLevel) - : ""; - - // note the special case: the string '\n' counts as a "trailing" empty line. - const clip = string[string.length - 1] === "\n"; - const keep = clip && (string[string.length - 2] === "\n" || string === "\n"); - const chomp = keep ? "+" : clip ? "" : "-"; - - return `${indentIndicator}${chomp}\n`; -} - -// Note: line breaking/folding is implemented for only the folded style. -// NB. We drop the last trailing newline (if any) of a returned block scalar -// since the dumper adds its own newline. This always works: -// • No ending newline => unaffected; already using strip "-" chomping. -// • Ending newline => removed then restored. -// Importantly, this keeps the "+" chomp indicator from gaining an extra line. -function writeScalar( - state: DumperState, - string: string, - level: number, - iskey: boolean, -): void { - state.dump = ((): string => { - if (string.length === 0) { - return "''"; - } - if ( - !state.noCompatMode && - DEPRECATED_BOOLEANS_SYNTAX.indexOf(string) !== -1 - ) { - return `'${string}'`; - } - - const indent = state.indent * Math.max(1, level); // no 0-indent scalars - // As indentation gets deeper, let the width decrease monotonically - // to the lower bound min(state.lineWidth, 40). - // Note that this implies - // state.lineWidth ≤ 40 + state.indent: width is fixed at the lower bound. - // state.lineWidth > 40 + state.indent: width decreases until the lower - // bound. - // This behaves better than a constant minimum width which disallows - // narrower options, or an indent threshold which causes the width - // to suddenly increase. - const lineWidth = state.lineWidth === -1 - ? -1 - : Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent); - - // Without knowing if keys are implicit/explicit, - // assume implicit for safety. - const singleLineOnly = iskey || - // No block styles in flow mode. - (state.flowLevel > -1 && level >= state.flowLevel); - function testAmbiguity(str: string): boolean { - return testImplicitResolving(state, str); - } - - switch ( - chooseScalarStyle( - string, - singleLineOnly, - state.indent, - lineWidth, - testAmbiguity, - ) - ) { - case STYLE_PLAIN: - return string; - case STYLE_SINGLE: - return `'${string.replace(/'/g, "''")}'`; - case STYLE_LITERAL: - return `|${blockHeader(string, state.indent)}${ - dropEndingNewline( - indentString(string, indent), - ) - }`; - case STYLE_FOLDED: - return `>${blockHeader(string, state.indent)}${ - dropEndingNewline( - indentString(foldString(string, lineWidth), indent), - ) - }`; - case STYLE_DOUBLE: - return `"${escapeString(string)}"`; - default: - throw new YAMLError("impossible error: invalid scalar style"); - } - })(); -} - -function writeFlowSequence( - state: DumperState, - level: number, - object: Any, -): void { - let _result = ""; - const _tag = state.tag; - - for (let index = 0, length = object.length; index < length; index += 1) { - // Write only valid elements. - // eslint-disable-next-line @typescript-eslint/no-use-before-define - if (writeNode(state, level, object[index], false, false)) { - if (index !== 0) _result += `,${!state.condenseFlow ? " " : ""}`; - _result += state.dump; - } - } - - state.tag = _tag; - state.dump = `[${_result}]`; -} - -function writeBlockSequence( - state: DumperState, - level: number, - object: Any, - compact = false, -): void { - let _result = ""; - const _tag = state.tag; - - for (let index = 0, length = object.length; index < length; index += 1) { - // Write only valid elements. - // eslint-disable-next-line @typescript-eslint/no-use-before-define - if (writeNode(state, level + 1, object[index], true, true)) { - if (!compact || index !== 0) { - _result += generateNextLine(state, level); - } - - if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) { - _result += "-"; - } else { - _result += "- "; - } - - _result += state.dump; - } - } - - state.tag = _tag; - state.dump = _result || "[]"; // Empty sequence if no valid values. -} - -function writeFlowMapping( - state: DumperState, - level: number, - object: Any, -): void { - let _result = ""; - const _tag = state.tag, - objectKeyList = Object.keys(object); - - let pairBuffer: string, objectKey: string, objectValue: Any; - for ( - let index = 0, length = objectKeyList.length; - index < length; - index += 1 - ) { - pairBuffer = state.condenseFlow ? '"' : ""; - - if (index !== 0) pairBuffer += ", "; - - objectKey = objectKeyList[index]; - objectValue = object[objectKey]; - - // eslint-disable-next-line @typescript-eslint/no-use-before-define - if (!writeNode(state, level, objectKey, false, false)) { - continue; // Skip this pair because of invalid key; - } - - if (state.dump.length > 1024) pairBuffer += "? "; - - pairBuffer += `${state.dump}${state.condenseFlow ? '"' : ""}:${ - state.condenseFlow ? "" : " " - }`; - - // eslint-disable-next-line @typescript-eslint/no-use-before-define - if (!writeNode(state, level, objectValue, false, false)) { - continue; // Skip this pair because of invalid value. - } - - pairBuffer += state.dump; - - // Both key and value are valid. - _result += pairBuffer; - } - - state.tag = _tag; - state.dump = `{${_result}}`; -} - -function writeBlockMapping( - state: DumperState, - level: number, - object: Any, - compact = false, -): void { - const _tag = state.tag, - objectKeyList = Object.keys(object); - let _result = ""; - - // Allow sorting keys so that the output file is deterministic - if (state.sortKeys === true) { - // Default sorting - objectKeyList.sort(); - } else if (typeof state.sortKeys === "function") { - // Custom sort function - objectKeyList.sort(state.sortKeys); - } else if (state.sortKeys) { - // Something is wrong - throw new YAMLError("sortKeys must be a boolean or a function"); - } - - let pairBuffer = "", - objectKey: string, - objectValue: Any, - explicitPair: boolean; - for ( - let index = 0, length = objectKeyList.length; - index < length; - index += 1 - ) { - pairBuffer = ""; - - if (!compact || index !== 0) { - pairBuffer += generateNextLine(state, level); - } - - objectKey = objectKeyList[index]; - objectValue = object[objectKey]; - - // eslint-disable-next-line @typescript-eslint/no-use-before-define - if (!writeNode(state, level + 1, objectKey, true, true, true)) { - continue; // Skip this pair because of invalid key. - } - - explicitPair = (state.tag !== null && state.tag !== "?") || - (state.dump && state.dump.length > 1024); - - if (explicitPair) { - if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) { - pairBuffer += "?"; - } else { - pairBuffer += "? "; - } - } - - pairBuffer += state.dump; - - if (explicitPair) { - pairBuffer += generateNextLine(state, level); - } - - // eslint-disable-next-line @typescript-eslint/no-use-before-define - if (!writeNode(state, level + 1, objectValue, true, explicitPair)) { - continue; // Skip this pair because of invalid value. - } - - if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) { - pairBuffer += ":"; - } else { - pairBuffer += ": "; - } - - pairBuffer += state.dump; - - // Both key and value are valid. - _result += pairBuffer; - } - - state.tag = _tag; - state.dump = _result || "{}"; // Empty mapping if no valid pairs. -} - -function detectType( - state: DumperState, - object: Any, - explicit = false, -): boolean { - const typeList = explicit ? state.explicitTypes : state.implicitTypes; - - let type: Type; - let style: StyleVariant; - let _result: string; - for (let index = 0, length = typeList.length; index < length; index += 1) { - type = typeList[index]; - - if ( - (type.instanceOf || type.predicate) && - (!type.instanceOf || - (typeof object === "object" && object instanceof type.instanceOf)) && - (!type.predicate || type.predicate(object)) - ) { - state.tag = explicit ? type.tag : "?"; - - if (type.represent) { - style = state.styleMap[type.tag] || type.defaultStyle; - - if (_toString.call(type.represent) === "[object Function]") { - _result = (type.represent as RepresentFn)(object, style); - } else if (_hasOwnProperty.call(type.represent, style)) { - _result = (type.represent as ArrayObject)[style]( - object, - style, - ); - } else { - throw new YAMLError( - `!<${type.tag}> tag resolver accepts not "${style}" style`, - ); - } - - state.dump = _result; - } - - return true; - } - } - - return false; -} - -// Serializes `object` and writes it to global `result`. -// Returns true on success, or false on invalid object. -// -function writeNode( - state: DumperState, - level: number, - object: Any, - block: boolean, - compact: boolean, - iskey = false, -): boolean { - state.tag = null; - state.dump = object; - - if (!detectType(state, object, false)) { - detectType(state, object, true); - } - - const type = _toString.call(state.dump); - - if (block) { - block = state.flowLevel < 0 || state.flowLevel > level; - } - - const objectOrArray = type === "[object Object]" || type === "[object Array]"; - - let duplicateIndex = -1; - let duplicate = false; - if (objectOrArray) { - duplicateIndex = state.duplicates.indexOf(object); - duplicate = duplicateIndex !== -1; - } - - if ( - (state.tag !== null && state.tag !== "?") || - duplicate || - (state.indent !== 2 && level > 0) - ) { - compact = false; - } - - if (duplicate && state.usedDuplicates[duplicateIndex]) { - state.dump = `*ref_${duplicateIndex}`; - } else { - if (objectOrArray && duplicate && !state.usedDuplicates[duplicateIndex]) { - state.usedDuplicates[duplicateIndex] = true; - } - if (type === "[object Object]") { - if (block && Object.keys(state.dump).length !== 0) { - writeBlockMapping(state, level, state.dump, compact); - if (duplicate) { - state.dump = `&ref_${duplicateIndex}${state.dump}`; - } - } else { - writeFlowMapping(state, level, state.dump); - if (duplicate) { - state.dump = `&ref_${duplicateIndex} ${state.dump}`; - } - } - } else if (type === "[object Array]") { - const arrayLevel = state.noArrayIndent && level > 0 ? level - 1 : level; - if (block && state.dump.length !== 0) { - writeBlockSequence(state, arrayLevel, state.dump, compact); - if (duplicate) { - state.dump = `&ref_${duplicateIndex}${state.dump}`; - } - } else { - writeFlowSequence(state, arrayLevel, state.dump); - if (duplicate) { - state.dump = `&ref_${duplicateIndex} ${state.dump}`; - } - } - } else if (type === "[object String]") { - if (state.tag !== "?") { - writeScalar(state, state.dump, level, iskey); - } - } else { - if (state.skipInvalid) return false; - throw new YAMLError(`unacceptable kind of an object to dump ${type}`); - } - - if (state.tag !== null && state.tag !== "?") { - state.dump = `!<${state.tag}> ${state.dump}`; - } - } - - return true; -} - -function inspectNode( - object: Any, - objects: Any[], - duplicatesIndexes: number[], -): void { - if (object !== null && typeof object === "object") { - const index = objects.indexOf(object); - if (index !== -1) { - if (duplicatesIndexes.indexOf(index) === -1) { - duplicatesIndexes.push(index); - } - } else { - objects.push(object); - - if (Array.isArray(object)) { - for (let idx = 0, length = object.length; idx < length; idx += 1) { - inspectNode(object[idx], objects, duplicatesIndexes); - } - } else { - const objectKeyList = Object.keys(object); - - for ( - let idx = 0, length = objectKeyList.length; - idx < length; - idx += 1 - ) { - inspectNode(object[objectKeyList[idx]], objects, duplicatesIndexes); - } - } - } - } -} - -function getDuplicateReferences( - object: Record, - state: DumperState, -): void { - const objects: Any[] = [], - duplicatesIndexes: number[] = []; - - inspectNode(object, objects, duplicatesIndexes); - - const length = duplicatesIndexes.length; - for (let index = 0; index < length; index += 1) { - state.duplicates.push(objects[duplicatesIndexes[index]]); - } - state.usedDuplicates = new Array(length); -} - -export function dump(input: Any, options?: DumperStateOptions): string { - options = options || {}; - - const state = new DumperState(options); - - if (!state.noRefs) getDuplicateReferences(input, state); - - if (writeNode(state, 0, input, true, true)) return `${state.dump}\n`; - - return ""; -} diff --git a/std/encoding/_yaml/dumper/dumper_state.ts b/std/encoding/_yaml/dumper/dumper_state.ts deleted file mode 100644 index 6861e7a437..0000000000 --- a/std/encoding/_yaml/dumper/dumper_state.ts +++ /dev/null @@ -1,141 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import type { Schema, SchemaDefinition } from "../schema.ts"; -import { State } from "../state.ts"; -import type { StyleVariant, Type } from "../type.ts"; -import type { Any, ArrayObject } from "../utils.ts"; - -const _hasOwnProperty = Object.prototype.hasOwnProperty; - -function compileStyleMap( - schema: Schema, - map?: ArrayObject | null, -): ArrayObject { - if (typeof map === "undefined" || map === null) return {}; - - let type: Type; - const result: ArrayObject = {}; - const keys = Object.keys(map); - let tag: string, style: StyleVariant; - for (let index = 0, length = keys.length; index < length; index += 1) { - tag = keys[index]; - style = String(map[tag]) as StyleVariant; - if (tag.slice(0, 2) === "!!") { - tag = `tag:yaml.org,2002:${tag.slice(2)}`; - } - type = schema.compiledTypeMap.fallback[tag]; - - if ( - type && - typeof type.styleAliases !== "undefined" && - _hasOwnProperty.call(type.styleAliases, style) - ) { - style = type.styleAliases[style]; - } - - result[tag] = style; - } - - return result; -} - -export interface DumperStateOptions { - /** indentation width to use (in spaces). */ - indent?: number; - /** when true, will not add an indentation level to array elements */ - noArrayIndent?: boolean; - /** - * do not throw on invalid types (like function in the safe schema) - * and skip pairs and single values with such types. - */ - skipInvalid?: boolean; - /** - * specifies level of nesting, when to switch from - * block to flow style for collections. -1 means block style everywhere - */ - flowLevel?: number; - /** Each tag may have own set of styles. - "tag" => "style" map. */ - styles?: ArrayObject | null; - /** specifies a schema to use. */ - schema?: SchemaDefinition; - /** - * If true, sort keys when dumping YAML in ascending, ASCII character order. - * If a function, use the function to sort the keys. (default: false) - * If a function is specified, the function must return a negative value - * if first argument is less than second argument, zero if they're equal - * and a positive value otherwise. - */ - sortKeys?: boolean | ((a: string, b: string) => number); - /** set max line width. (default: 80) */ - lineWidth?: number; - /** - * if true, don't convert duplicate objects - * into references (default: false) - */ - noRefs?: boolean; - /** - * if true don't try to be compatible with older yaml versions. - * Currently: don't quote "yes", "no" and so on, - * as required for YAML 1.1 (default: false) - */ - noCompatMode?: boolean; - /** - * if true flow sequences will be condensed, omitting the - * space between `key: value` or `a, b`. Eg. `'[a,b]'` or `{a:{b:c}}`. - * Can be useful when using yaml for pretty URL query params - * as spaces are %-encoded. (default: false). - */ - condenseFlow?: boolean; -} - -export class DumperState extends State { - public indent: number; - public noArrayIndent: boolean; - public skipInvalid: boolean; - public flowLevel: number; - public sortKeys: boolean | ((a: Any, b: Any) => number); - public lineWidth: number; - public noRefs: boolean; - public noCompatMode: boolean; - public condenseFlow: boolean; - public implicitTypes: Type[]; - public explicitTypes: Type[]; - public tag: string | null = null; - public result = ""; - public duplicates: Any[] = []; - public usedDuplicates: Any[] = []; // changed from null to [] - public styleMap: ArrayObject; - public dump: Any; - - constructor({ - schema, - indent = 2, - noArrayIndent = false, - skipInvalid = false, - flowLevel = -1, - styles = null, - sortKeys = false, - lineWidth = 80, - noRefs = false, - noCompatMode = false, - condenseFlow = false, - }: DumperStateOptions) { - super(schema); - this.indent = Math.max(1, indent); - this.noArrayIndent = noArrayIndent; - this.skipInvalid = skipInvalid; - this.flowLevel = flowLevel; - this.styleMap = compileStyleMap(this.schema as Schema, styles); - this.sortKeys = sortKeys; - this.lineWidth = lineWidth; - this.noRefs = noRefs; - this.noCompatMode = noCompatMode; - this.condenseFlow = condenseFlow; - - this.implicitTypes = (this.schema as Schema).compiledImplicit; - this.explicitTypes = (this.schema as Schema).compiledExplicit; - } -} diff --git a/std/encoding/_yaml/error.ts b/std/encoding/_yaml/error.ts deleted file mode 100644 index 6b1e359b67..0000000000 --- a/std/encoding/_yaml/error.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import type { Mark } from "./mark.ts"; - -export class YAMLError extends Error { - constructor( - message = "(unknown reason)", - protected mark: Mark | string = "", - ) { - super(`${message} ${mark}`); - this.name = this.constructor.name; - } - - public toString(_compact: boolean): string { - return `${this.name}: ${this.message} ${this.mark}`; - } -} diff --git a/std/encoding/_yaml/example/dump.ts b/std/encoding/_yaml/example/dump.ts deleted file mode 100644 index 3304474f24..0000000000 --- a/std/encoding/_yaml/example/dump.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { stringify } from "../../yaml.ts"; - -console.log( - stringify({ - foo: { - bar: true, - test: [ - "a", - "b", - { - a: false, - }, - { - a: false, - }, - ], - }, - test: "foobar", - }), -); diff --git a/std/encoding/_yaml/example/inout.ts b/std/encoding/_yaml/example/inout.ts deleted file mode 100644 index fd4e1e6c2b..0000000000 --- a/std/encoding/_yaml/example/inout.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { parse, stringify } from "../../yaml.ts"; - -const test = { - foo: { - bar: true, - test: [ - "a", - "b", - { - a: false, - }, - { - a: false, - }, - ], - }, - test: "foobar", -}; - -const string = stringify(test); -if (Deno.inspect(test) === Deno.inspect(parse(string))) { - console.log("In-Out as expected."); -} else { - console.log("Something went wrong."); -} diff --git a/std/encoding/_yaml/example/parse.ts b/std/encoding/_yaml/example/parse.ts deleted file mode 100644 index b4da86aac6..0000000000 --- a/std/encoding/_yaml/example/parse.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { parse } from "../../yaml.ts"; - -const result = parse(` -test: toto -foo: - bar: True - baz: 1 - qux: ~ -`); -console.log(result); - -const expected = '{ test: "toto", foo: { bar: true, baz: 1, qux: null } }'; -if (Deno.inspect(result) === expected) { - console.log("Output is as expected."); -} else { - console.error("Error during parse. Output is not as expect.", expected); -} diff --git a/std/encoding/_yaml/example/sample_document.ts b/std/encoding/_yaml/example/sample_document.ts deleted file mode 100644 index 695744322f..0000000000 --- a/std/encoding/_yaml/example/sample_document.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { parse } from "../../yaml.ts"; - -(() => { - const yml = Deno.readFileSync(`${Deno.cwd()}/example/sample_document.yml`); - - const document = new TextDecoder().decode(yml); - // deno-lint-ignore no-explicit-any - const obj = parse(document) as Record; - console.log(obj); - - let i = 0; - for (const o of Object.values(obj)) { - console.log(`======${i}`); - for (const [key, value] of Object.entries(o)) { - console.log(key, value); - } - i++; - } -})(); diff --git a/std/encoding/_yaml/example/sample_document.yml b/std/encoding/_yaml/example/sample_document.yml deleted file mode 100644 index 1f3c2eb3e4..0000000000 --- a/std/encoding/_yaml/example/sample_document.yml +++ /dev/null @@ -1,197 +0,0 @@ ---- -# Collection Types ############################################################# -################################################################################ - -# http://yaml.org/type/map.html -----------------------------------------------# - -map: - # Unordered set of key: value pairs. - Block style: !!map - Clark : Evans - Ingy : döt Net - Oren : Ben-Kiki - Flow style: !!map { Clark: Evans, Ingy: döt Net, Oren: Ben-Kiki } - -# http://yaml.org/type/omap.html ----------------------------------------------# - -omap: - # Explicitly typed ordered map (dictionary). - Bestiary: !!omap - - aardvark: African pig-like ant eater. Ugly. - - anteater: South-American ant eater. Two species. - - anaconda: South-American constrictor snake. Scaly. - # Etc. - # Flow style - Numbers: !!omap [ one: 1, two: 2, three : 3 ] - -# http://yaml.org/type/pairs.html ---------------------------------------------# - -pairs: - # Explicitly typed pairs. - Block tasks: !!pairs - - meeting: with team. - - meeting: with boss. - - break: lunch. - - meeting: with client. - Flow tasks: !!pairs [ meeting: with team, meeting: with boss ] - -# http://yaml.org/type/set.html -----------------------------------------------# - -set: - # Explicitly typed set. - baseball players: !!set - ? Mark McGwire - ? Sammy Sosa - ? Ken Griffey - # Flow style - baseball teams: !!set { Boston Red Sox, Detroit Tigers, New York Yankees } - -# http://yaml.org/type/seq.html -----------------------------------------------# - -seq: - # Ordered sequence of nodes - Block style: !!seq - - Mercury # Rotates - no light/dark sides. - - Venus # Deadliest. Aptly named. - - Earth # Mostly dirt. - - Mars # Seems empty. - - Jupiter # The king. - - Saturn # Pretty. - - Uranus # Where the sun hardly shines. - - Neptune # Boring. No rings. - - Pluto # You call this a planet? - Flow style: !!seq [ Mercury, Venus, Earth, Mars, # Rocks - Jupiter, Saturn, Uranus, Neptune, # Gas - Pluto ] # Overrated - - -# Scalar Types ################################################################# -################################################################################ - -# http://yaml.org/type/binary.html --------------------------------------------# - -binary: - canonical: !!binary "\ - R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5\ - OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+\ - +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC\ - AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=" - generic: !!binary | - R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5 - OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+ - +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC - AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs= - description: - The binary value above is a tiny arrow encoded as a gif image. - -# http://yaml.org/type/bool.html ----------------------------------------------# - -bool: - - true - - True - - TRUE - - false - - False - - FALSE - -# http://yaml.org/type/float.html ---------------------------------------------# - -float: - canonical: 6.8523015e+5 - exponential: 685.230_15e+03 - fixed: 685_230.15 - sexagesimal: 190:20:30.15 - negative infinity: -.inf - not a number: .NaN - -# http://yaml.org/type/int.html -----------------------------------------------# - -int: - canonical: 685230 - decimal: +685_230 - octal: 02472256 - hexadecimal: 0x_0A_74_AE - binary: 0b1010_0111_0100_1010_1110 - sexagesimal: 190:20:30 - -# http://yaml.org/type/merge.html ---------------------------------------------# - -merge: - - &CENTER { x: 1, y: 2 } - - &LEFT { x: 0, y: 2 } - - &BIG { r: 10 } - - &SMALL { r: 1 } - - # All the following maps are equal: - - - # Explicit keys - x: 1 - y: 2 - r: 10 - label: nothing - - - # Merge one map - << : *CENTER - r: 10 - label: center - - - # Merge multiple maps - << : [ *CENTER, *BIG ] - label: center/big - - - # Override - << : [ *BIG, *LEFT, *SMALL ] - x: 1 - label: big/left/small - -# http://yaml.org/type/null.html ----------------------------------------------# - -null: - # This mapping has four keys, - # one has a value. - empty: - canonical: ~ - english: null - ~: null key - # This sequence has five - # entries, two have values. - sparse: - - ~ - - 2nd entry - - - - 4th entry - - Null - -# http://yaml.org/type/str.html -----------------------------------------------# - -string: abcd - -# http://yaml.org/type/timestamp.html -----------------------------------------# - -timestamp: - canonical: 2001-12-15T02:59:43.1Z - valid iso8601: 2001-12-14t21:59:43.10-05:00 - space separated: 2001-12-14 21:59:43.10 -5 - no time zone (Z): 2001-12-15 2:59:43.10 - date (00:00:00Z): 2002-12-14 - - -# JavaScript Specific Types #################################################### -################################################################################ - -# https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/RegExp - -# regexp: -# simple: !!js/regexp foobar -# modifiers: !!js/regexp /foobar/mi - -# https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/undefined - -# undefined: !!js/undefined ~ - -# https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function - -# function: !!js/function > -# function foobar() { -# return 'Wow! JS-YAML Rocks!'; -# } diff --git a/std/encoding/_yaml/loader/loader.ts b/std/encoding/_yaml/loader/loader.ts deleted file mode 100644 index 322f5ce0d3..0000000000 --- a/std/encoding/_yaml/loader/loader.ts +++ /dev/null @@ -1,1798 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { YAMLError } from "../error.ts"; -import { Mark } from "../mark.ts"; -import type { Type } from "../type.ts"; -import * as common from "../utils.ts"; -import { LoaderState, LoaderStateOptions, ResultType } from "./loader_state.ts"; - -type Any = common.Any; -type ArrayObject = common.ArrayObject; - -const _hasOwnProperty = Object.prototype.hasOwnProperty; - -const CONTEXT_FLOW_IN = 1; -const CONTEXT_FLOW_OUT = 2; -const CONTEXT_BLOCK_IN = 3; -const CONTEXT_BLOCK_OUT = 4; - -const CHOMPING_CLIP = 1; -const CHOMPING_STRIP = 2; -const CHOMPING_KEEP = 3; - -const PATTERN_NON_PRINTABLE = - // deno-lint-ignore no-control-regex - /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/; -const PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/; -const PATTERN_FLOW_INDICATORS = /[,\[\]\{\}]/; -const PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i; -const PATTERN_TAG_URI = - /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i; - -function _class(obj: unknown): string { - return Object.prototype.toString.call(obj); -} - -function isEOL(c: number): boolean { - return c === 0x0a || /* LF */ c === 0x0d /* CR */; -} - -function isWhiteSpace(c: number): boolean { - return c === 0x09 || /* Tab */ c === 0x20 /* Space */; -} - -function isWsOrEol(c: number): boolean { - return ( - c === 0x09 /* Tab */ || - c === 0x20 /* Space */ || - c === 0x0a /* LF */ || - c === 0x0d /* CR */ - ); -} - -function isFlowIndicator(c: number): boolean { - return ( - c === 0x2c /* , */ || - c === 0x5b /* [ */ || - c === 0x5d /* ] */ || - c === 0x7b /* { */ || - c === 0x7d /* } */ - ); -} - -function fromHexCode(c: number): number { - if (0x30 <= /* 0 */ c && c <= 0x39 /* 9 */) { - return c - 0x30; - } - - const lc = c | 0x20; - - if (0x61 <= /* a */ lc && lc <= 0x66 /* f */) { - return lc - 0x61 + 10; - } - - return -1; -} - -function escapedHexLen(c: number): number { - if (c === 0x78 /* x */) { - return 2; - } - if (c === 0x75 /* u */) { - return 4; - } - if (c === 0x55 /* U */) { - return 8; - } - return 0; -} - -function fromDecimalCode(c: number): number { - if (0x30 <= /* 0 */ c && c <= 0x39 /* 9 */) { - return c - 0x30; - } - - return -1; -} - -function simpleEscapeSequence(c: number): string { - /* eslint:disable:prettier */ - return c === 0x30 /* 0 */ - ? "\x00" - : c === 0x61 /* a */ - ? "\x07" - : c === 0x62 /* b */ - ? "\x08" - : c === 0x74 /* t */ - ? "\x09" - : c === 0x09 /* Tab */ - ? "\x09" - : c === 0x6e /* n */ - ? "\x0A" - : c === 0x76 /* v */ - ? "\x0B" - : c === 0x66 /* f */ - ? "\x0C" - : c === 0x72 /* r */ - ? "\x0D" - : c === 0x65 /* e */ - ? "\x1B" - : c === 0x20 /* Space */ - ? " " - : c === 0x22 /* " */ - ? "\x22" - : c === 0x2f /* / */ - ? "/" - : c === 0x5c /* \ */ - ? "\x5C" - : c === 0x4e /* N */ - ? "\x85" - : c === 0x5f /* _ */ - ? "\xA0" - : c === 0x4c /* L */ - ? "\u2028" - : c === 0x50 /* P */ - ? "\u2029" - : ""; - /* eslint:enable:prettier */ -} - -function charFromCodepoint(c: number): string { - if (c <= 0xffff) { - return String.fromCharCode(c); - } - // Encode UTF-16 surrogate pair - // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF - return String.fromCharCode( - ((c - 0x010000) >> 10) + 0xd800, - ((c - 0x010000) & 0x03ff) + 0xdc00, - ); -} - -const simpleEscapeCheck = new Array(256); // integer, for fast access -const simpleEscapeMap = new Array(256); -for (let i = 0; i < 256; i++) { - simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0; - simpleEscapeMap[i] = simpleEscapeSequence(i); -} - -function generateError(state: LoaderState, message: string): YAMLError { - return new YAMLError( - message, - new Mark( - state.filename as string, - state.input, - state.position, - state.line, - state.position - state.lineStart, - ), - ); -} - -function throwError(state: LoaderState, message: string): never { - throw generateError(state, message); -} - -function throwWarning(state: LoaderState, message: string): void { - if (state.onWarning) { - state.onWarning.call(null, generateError(state, message)); - } -} - -interface DirectiveHandlers { - [directive: string]: ( - state: LoaderState, - name: string, - ...args: string[] - ) => void; -} - -const directiveHandlers: DirectiveHandlers = { - YAML(state, _name, ...args: string[]) { - if (state.version !== null) { - return throwError(state, "duplication of %YAML directive"); - } - - if (args.length !== 1) { - return throwError(state, "YAML directive accepts exactly one argument"); - } - - const match = /^([0-9]+)\.([0-9]+)$/.exec(args[0]); - if (match === null) { - return throwError(state, "ill-formed argument of the YAML directive"); - } - - const major = parseInt(match[1], 10); - const minor = parseInt(match[2], 10); - if (major !== 1) { - return throwError(state, "unacceptable YAML version of the document"); - } - - state.version = args[0]; - state.checkLineBreaks = minor < 2; - if (minor !== 1 && minor !== 2) { - return throwWarning(state, "unsupported YAML version of the document"); - } - }, - - TAG(state, _name, ...args: string[]): void { - if (args.length !== 2) { - return throwError(state, "TAG directive accepts exactly two arguments"); - } - - const handle = args[0]; - const prefix = args[1]; - - if (!PATTERN_TAG_HANDLE.test(handle)) { - return throwError( - state, - "ill-formed tag handle (first argument) of the TAG directive", - ); - } - - if (_hasOwnProperty.call(state.tagMap, handle)) { - return throwError( - state, - `there is a previously declared suffix for "${handle}" tag handle`, - ); - } - - if (!PATTERN_TAG_URI.test(prefix)) { - return throwError( - state, - "ill-formed tag prefix (second argument) of the TAG directive", - ); - } - - if (typeof state.tagMap === "undefined") { - state.tagMap = {}; - } - state.tagMap[handle] = prefix; - }, -}; - -function captureSegment( - state: LoaderState, - start: number, - end: number, - checkJson: boolean, -): void { - let result: string; - if (start < end) { - result = state.input.slice(start, end); - - if (checkJson) { - for ( - let position = 0, length = result.length; - position < length; - position++ - ) { - const character = result.charCodeAt(position); - if ( - !(character === 0x09 || (0x20 <= character && character <= 0x10ffff)) - ) { - return throwError(state, "expected valid JSON character"); - } - } - } else if (PATTERN_NON_PRINTABLE.test(result)) { - return throwError(state, "the stream contains non-printable characters"); - } - - state.result += result; - } -} - -function mergeMappings( - state: LoaderState, - destination: ArrayObject, - source: ArrayObject, - overridableKeys: ArrayObject, -): void { - if (!common.isObject(source)) { - return throwError( - state, - "cannot merge mappings; the provided source object is unacceptable", - ); - } - - const keys = Object.keys(source); - for (let i = 0, len = keys.length; i < len; i++) { - const key = keys[i]; - if (!_hasOwnProperty.call(destination, key)) { - destination[key] = (source as ArrayObject)[key]; - overridableKeys[key] = true; - } - } -} - -function storeMappingPair( - state: LoaderState, - result: ArrayObject | null, - overridableKeys: ArrayObject, - keyTag: string | null, - keyNode: Any, - valueNode: unknown, - startLine?: number, - startPos?: number, -): ArrayObject { - // The output is a plain object here, so keys can only be strings. - // We need to convert keyNode to a string, but doing so can hang the process - // (deeply nested arrays that explode exponentially using aliases). - if (Array.isArray(keyNode)) { - keyNode = Array.prototype.slice.call(keyNode); - - for (let index = 0, quantity = keyNode.length; index < quantity; index++) { - if (Array.isArray(keyNode[index])) { - return throwError(state, "nested arrays are not supported inside keys"); - } - - if ( - typeof keyNode === "object" && - _class(keyNode[index]) === "[object Object]" - ) { - keyNode[index] = "[object Object]"; - } - } - } - - // Avoid code execution in load() via toString property - // (still use its own toString for arrays, timestamps, - // and whatever user schema extensions happen to have @@toStringTag) - if (typeof keyNode === "object" && _class(keyNode) === "[object Object]") { - keyNode = "[object Object]"; - } - - keyNode = String(keyNode); - - if (result === null) { - result = {}; - } - - if (keyTag === "tag:yaml.org,2002:merge") { - if (Array.isArray(valueNode)) { - for ( - let index = 0, quantity = valueNode.length; - index < quantity; - index++ - ) { - mergeMappings(state, result, valueNode[index], overridableKeys); - } - } else { - mergeMappings(state, result, valueNode as ArrayObject, overridableKeys); - } - } else { - if ( - !state.json && - !_hasOwnProperty.call(overridableKeys, keyNode) && - _hasOwnProperty.call(result, keyNode) - ) { - state.line = startLine || state.line; - state.position = startPos || state.position; - return throwError(state, "duplicated mapping key"); - } - result[keyNode] = valueNode; - delete overridableKeys[keyNode]; - } - - return result; -} - -function readLineBreak(state: LoaderState): void { - const ch = state.input.charCodeAt(state.position); - - if (ch === 0x0a /* LF */) { - state.position++; - } else if (ch === 0x0d /* CR */) { - state.position++; - if (state.input.charCodeAt(state.position) === 0x0a /* LF */) { - state.position++; - } - } else { - return throwError(state, "a line break is expected"); - } - - state.line += 1; - state.lineStart = state.position; -} - -function skipSeparationSpace( - state: LoaderState, - allowComments: boolean, - checkIndent: number, -): number { - let lineBreaks = 0, - ch = state.input.charCodeAt(state.position); - - while (ch !== 0) { - while (isWhiteSpace(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (allowComments && ch === 0x23 /* # */) { - do { - ch = state.input.charCodeAt(++state.position); - } while (ch !== 0x0a && /* LF */ ch !== 0x0d && /* CR */ ch !== 0); - } - - if (isEOL(ch)) { - readLineBreak(state); - - ch = state.input.charCodeAt(state.position); - lineBreaks++; - state.lineIndent = 0; - - while (ch === 0x20 /* Space */) { - state.lineIndent++; - ch = state.input.charCodeAt(++state.position); - } - } else { - break; - } - } - - if ( - checkIndent !== -1 && - lineBreaks !== 0 && - state.lineIndent < checkIndent - ) { - throwWarning(state, "deficient indentation"); - } - - return lineBreaks; -} - -function testDocumentSeparator(state: LoaderState): boolean { - let _position = state.position; - let ch = state.input.charCodeAt(_position); - - // Condition state.position === state.lineStart is tested - // in parent on each call, for efficiency. No needs to test here again. - if ( - (ch === 0x2d || /* - */ ch === 0x2e) /* . */ && - ch === state.input.charCodeAt(_position + 1) && - ch === state.input.charCodeAt(_position + 2) - ) { - _position += 3; - - ch = state.input.charCodeAt(_position); - - if (ch === 0 || isWsOrEol(ch)) { - return true; - } - } - - return false; -} - -function writeFoldedLines(state: LoaderState, count: number): void { - if (count === 1) { - state.result += " "; - } else if (count > 1) { - state.result += common.repeat("\n", count - 1); - } -} - -function readPlainScalar( - state: LoaderState, - nodeIndent: number, - withinFlowCollection: boolean, -): boolean { - const kind = state.kind; - const result = state.result; - let ch = state.input.charCodeAt(state.position); - - if ( - isWsOrEol(ch) || - isFlowIndicator(ch) || - ch === 0x23 /* # */ || - ch === 0x26 /* & */ || - ch === 0x2a /* * */ || - ch === 0x21 /* ! */ || - ch === 0x7c /* | */ || - ch === 0x3e /* > */ || - ch === 0x27 /* ' */ || - ch === 0x22 /* " */ || - ch === 0x25 /* % */ || - ch === 0x40 /* @ */ || - ch === 0x60 /* ` */ - ) { - return false; - } - - let following: number; - if (ch === 0x3f || /* ? */ ch === 0x2d /* - */) { - following = state.input.charCodeAt(state.position + 1); - - if ( - isWsOrEol(following) || - (withinFlowCollection && isFlowIndicator(following)) - ) { - return false; - } - } - - state.kind = "scalar"; - state.result = ""; - let captureEnd: number, - captureStart = (captureEnd = state.position); - let hasPendingContent = false; - let line = 0; - while (ch !== 0) { - if (ch === 0x3a /* : */) { - following = state.input.charCodeAt(state.position + 1); - - if ( - isWsOrEol(following) || - (withinFlowCollection && isFlowIndicator(following)) - ) { - break; - } - } else if (ch === 0x23 /* # */) { - const preceding = state.input.charCodeAt(state.position - 1); - - if (isWsOrEol(preceding)) { - break; - } - } else if ( - (state.position === state.lineStart && testDocumentSeparator(state)) || - (withinFlowCollection && isFlowIndicator(ch)) - ) { - break; - } else if (isEOL(ch)) { - line = state.line; - const lineStart = state.lineStart; - const lineIndent = state.lineIndent; - skipSeparationSpace(state, false, -1); - - if (state.lineIndent >= nodeIndent) { - hasPendingContent = true; - ch = state.input.charCodeAt(state.position); - continue; - } else { - state.position = captureEnd; - state.line = line; - state.lineStart = lineStart; - state.lineIndent = lineIndent; - break; - } - } - - if (hasPendingContent) { - captureSegment(state, captureStart, captureEnd, false); - writeFoldedLines(state, state.line - line); - captureStart = captureEnd = state.position; - hasPendingContent = false; - } - - if (!isWhiteSpace(ch)) { - captureEnd = state.position + 1; - } - - ch = state.input.charCodeAt(++state.position); - } - - captureSegment(state, captureStart, captureEnd, false); - - if (state.result) { - return true; - } - - state.kind = kind; - state.result = result; - return false; -} - -function readSingleQuotedScalar( - state: LoaderState, - nodeIndent: number, -): boolean { - let ch, captureStart, captureEnd; - - ch = state.input.charCodeAt(state.position); - - if (ch !== 0x27 /* ' */) { - return false; - } - - state.kind = "scalar"; - state.result = ""; - state.position++; - captureStart = captureEnd = state.position; - - while ((ch = state.input.charCodeAt(state.position)) !== 0) { - if (ch === 0x27 /* ' */) { - captureSegment(state, captureStart, state.position, true); - ch = state.input.charCodeAt(++state.position); - - if (ch === 0x27 /* ' */) { - captureStart = state.position; - state.position++; - captureEnd = state.position; - } else { - return true; - } - } else if (isEOL(ch)) { - captureSegment(state, captureStart, captureEnd, true); - writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); - captureStart = captureEnd = state.position; - } else if ( - state.position === state.lineStart && - testDocumentSeparator(state) - ) { - return throwError( - state, - "unexpected end of the document within a single quoted scalar", - ); - } else { - state.position++; - captureEnd = state.position; - } - } - - return throwError( - state, - "unexpected end of the stream within a single quoted scalar", - ); -} - -function readDoubleQuotedScalar( - state: LoaderState, - nodeIndent: number, -): boolean { - let ch = state.input.charCodeAt(state.position); - - if (ch !== 0x22 /* " */) { - return false; - } - - state.kind = "scalar"; - state.result = ""; - state.position++; - let captureEnd: number, - captureStart = (captureEnd = state.position); - let tmp: number; - while ((ch = state.input.charCodeAt(state.position)) !== 0) { - if (ch === 0x22 /* " */) { - captureSegment(state, captureStart, state.position, true); - state.position++; - return true; - } - if (ch === 0x5c /* \ */) { - captureSegment(state, captureStart, state.position, true); - ch = state.input.charCodeAt(++state.position); - - if (isEOL(ch)) { - skipSeparationSpace(state, false, nodeIndent); - - // TODO(bartlomieju): rework to inline fn with no type cast? - } else if (ch < 256 && simpleEscapeCheck[ch]) { - state.result += simpleEscapeMap[ch]; - state.position++; - } else if ((tmp = escapedHexLen(ch)) > 0) { - let hexLength = tmp; - let hexResult = 0; - - for (; hexLength > 0; hexLength--) { - ch = state.input.charCodeAt(++state.position); - - if ((tmp = fromHexCode(ch)) >= 0) { - hexResult = (hexResult << 4) + tmp; - } else { - return throwError(state, "expected hexadecimal character"); - } - } - - state.result += charFromCodepoint(hexResult); - - state.position++; - } else { - return throwError(state, "unknown escape sequence"); - } - - captureStart = captureEnd = state.position; - } else if (isEOL(ch)) { - captureSegment(state, captureStart, captureEnd, true); - writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); - captureStart = captureEnd = state.position; - } else if ( - state.position === state.lineStart && - testDocumentSeparator(state) - ) { - return throwError( - state, - "unexpected end of the document within a double quoted scalar", - ); - } else { - state.position++; - captureEnd = state.position; - } - } - - return throwError( - state, - "unexpected end of the stream within a double quoted scalar", - ); -} - -function readFlowCollection(state: LoaderState, nodeIndent: number): boolean { - let ch = state.input.charCodeAt(state.position); - let terminator: number; - let isMapping = true; - let result: ResultType = {}; - if (ch === 0x5b /* [ */) { - terminator = 0x5d; /* ] */ - isMapping = false; - result = []; - } else if (ch === 0x7b /* { */) { - terminator = 0x7d; /* } */ - } else { - return false; - } - - if ( - state.anchor !== null && - typeof state.anchor != "undefined" && - typeof state.anchorMap != "undefined" - ) { - state.anchorMap[state.anchor] = result; - } - - ch = state.input.charCodeAt(++state.position); - - const tag = state.tag, - anchor = state.anchor; - let readNext = true; - let valueNode, - keyNode, - keyTag: string | null = (keyNode = valueNode = null), - isExplicitPair: boolean, - isPair = (isExplicitPair = false); - let following = 0, - line = 0; - const overridableKeys: ArrayObject = {}; - while (ch !== 0) { - skipSeparationSpace(state, true, nodeIndent); - - ch = state.input.charCodeAt(state.position); - - if (ch === terminator) { - state.position++; - state.tag = tag; - state.anchor = anchor; - state.kind = isMapping ? "mapping" : "sequence"; - state.result = result; - return true; - } - if (!readNext) { - return throwError(state, "missed comma between flow collection entries"); - } - - keyTag = keyNode = valueNode = null; - isPair = isExplicitPair = false; - - if (ch === 0x3f /* ? */) { - following = state.input.charCodeAt(state.position + 1); - - if (isWsOrEol(following)) { - isPair = isExplicitPair = true; - state.position++; - skipSeparationSpace(state, true, nodeIndent); - } - } - - line = state.line; - // eslint-disable-next-line @typescript-eslint/no-use-before-define - composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); - keyTag = state.tag || null; - keyNode = state.result; - skipSeparationSpace(state, true, nodeIndent); - - ch = state.input.charCodeAt(state.position); - - if ((isExplicitPair || state.line === line) && ch === 0x3a /* : */) { - isPair = true; - ch = state.input.charCodeAt(++state.position); - skipSeparationSpace(state, true, nodeIndent); - // eslint-disable-next-line @typescript-eslint/no-use-before-define - composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); - valueNode = state.result; - } - - if (isMapping) { - storeMappingPair( - state, - result, - overridableKeys, - keyTag, - keyNode, - valueNode, - ); - } else if (isPair) { - (result as ArrayObject[]).push( - storeMappingPair( - state, - null, - overridableKeys, - keyTag, - keyNode, - valueNode, - ), - ); - } else { - (result as ResultType[]).push(keyNode as ResultType); - } - - skipSeparationSpace(state, true, nodeIndent); - - ch = state.input.charCodeAt(state.position); - - if (ch === 0x2c /* , */) { - readNext = true; - ch = state.input.charCodeAt(++state.position); - } else { - readNext = false; - } - } - - return throwError( - state, - "unexpected end of the stream within a flow collection", - ); -} - -function readBlockScalar(state: LoaderState, nodeIndent: number): boolean { - let chomping = CHOMPING_CLIP, - didReadContent = false, - detectedIndent = false, - textIndent = nodeIndent, - emptyLines = 0, - atMoreIndented = false; - - let ch = state.input.charCodeAt(state.position); - - let folding = false; - if (ch === 0x7c /* | */) { - folding = false; - } else if (ch === 0x3e /* > */) { - folding = true; - } else { - return false; - } - - state.kind = "scalar"; - state.result = ""; - - let tmp = 0; - while (ch !== 0) { - ch = state.input.charCodeAt(++state.position); - - if (ch === 0x2b || /* + */ ch === 0x2d /* - */) { - if (CHOMPING_CLIP === chomping) { - chomping = ch === 0x2b /* + */ ? CHOMPING_KEEP : CHOMPING_STRIP; - } else { - return throwError(state, "repeat of a chomping mode identifier"); - } - } else if ((tmp = fromDecimalCode(ch)) >= 0) { - if (tmp === 0) { - return throwError( - state, - "bad explicit indentation width of a block scalar; it cannot be less than one", - ); - } else if (!detectedIndent) { - textIndent = nodeIndent + tmp - 1; - detectedIndent = true; - } else { - return throwError(state, "repeat of an indentation width identifier"); - } - } else { - break; - } - } - - if (isWhiteSpace(ch)) { - do { - ch = state.input.charCodeAt(++state.position); - } while (isWhiteSpace(ch)); - - if (ch === 0x23 /* # */) { - do { - ch = state.input.charCodeAt(++state.position); - } while (!isEOL(ch) && ch !== 0); - } - } - - while (ch !== 0) { - readLineBreak(state); - state.lineIndent = 0; - - ch = state.input.charCodeAt(state.position); - - while ( - (!detectedIndent || state.lineIndent < textIndent) && - ch === 0x20 /* Space */ - ) { - state.lineIndent++; - ch = state.input.charCodeAt(++state.position); - } - - if (!detectedIndent && state.lineIndent > textIndent) { - textIndent = state.lineIndent; - } - - if (isEOL(ch)) { - emptyLines++; - continue; - } - - // End of the scalar. - if (state.lineIndent < textIndent) { - // Perform the chomping. - if (chomping === CHOMPING_KEEP) { - state.result += common.repeat( - "\n", - didReadContent ? 1 + emptyLines : emptyLines, - ); - } else if (chomping === CHOMPING_CLIP) { - if (didReadContent) { - // i.e. only if the scalar is not empty. - state.result += "\n"; - } - } - - // Break this `while` cycle and go to the function's epilogue. - break; - } - - // Folded style: use fancy rules to handle line breaks. - if (folding) { - // Lines starting with white space characters (more-indented lines) are not folded. - if (isWhiteSpace(ch)) { - atMoreIndented = true; - // except for the first content line (cf. Example 8.1) - state.result += common.repeat( - "\n", - didReadContent ? 1 + emptyLines : emptyLines, - ); - - // End of more-indented block. - } else if (atMoreIndented) { - atMoreIndented = false; - state.result += common.repeat("\n", emptyLines + 1); - - // Just one line break - perceive as the same line. - } else if (emptyLines === 0) { - if (didReadContent) { - // i.e. only if we have already read some scalar content. - state.result += " "; - } - - // Several line breaks - perceive as different lines. - } else { - state.result += common.repeat("\n", emptyLines); - } - - // Literal style: just add exact number of line breaks between content lines. - } else { - // Keep all line breaks except the header line break. - state.result += common.repeat( - "\n", - didReadContent ? 1 + emptyLines : emptyLines, - ); - } - - didReadContent = true; - detectedIndent = true; - emptyLines = 0; - const captureStart = state.position; - - while (!isEOL(ch) && ch !== 0) { - ch = state.input.charCodeAt(++state.position); - } - - captureSegment(state, captureStart, state.position, false); - } - - return true; -} - -function readBlockSequence(state: LoaderState, nodeIndent: number): boolean { - let line: number, - following: number, - detected = false, - ch: number; - const tag = state.tag, - anchor = state.anchor, - result: unknown[] = []; - - if ( - state.anchor !== null && - typeof state.anchor !== "undefined" && - typeof state.anchorMap !== "undefined" - ) { - state.anchorMap[state.anchor] = result; - } - - ch = state.input.charCodeAt(state.position); - - while (ch !== 0) { - if (ch !== 0x2d /* - */) { - break; - } - - following = state.input.charCodeAt(state.position + 1); - - if (!isWsOrEol(following)) { - break; - } - - detected = true; - state.position++; - - if (skipSeparationSpace(state, true, -1)) { - if (state.lineIndent <= nodeIndent) { - result.push(null); - ch = state.input.charCodeAt(state.position); - continue; - } - } - - line = state.line; - // eslint-disable-next-line @typescript-eslint/no-use-before-define - composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true); - result.push(state.result); - skipSeparationSpace(state, true, -1); - - ch = state.input.charCodeAt(state.position); - - if ((state.line === line || state.lineIndent > nodeIndent) && ch !== 0) { - return throwError(state, "bad indentation of a sequence entry"); - } else if (state.lineIndent < nodeIndent) { - break; - } - } - - if (detected) { - state.tag = tag; - state.anchor = anchor; - state.kind = "sequence"; - state.result = result; - return true; - } - return false; -} - -function readBlockMapping( - state: LoaderState, - nodeIndent: number, - flowIndent: number, -): boolean { - const tag = state.tag, - anchor = state.anchor, - result = {}, - overridableKeys = {}; - let following: number, - allowCompact = false, - line: number, - pos: number, - keyTag = null, - keyNode = null, - valueNode = null, - atExplicitKey = false, - detected = false, - ch: number; - - if ( - state.anchor !== null && - typeof state.anchor !== "undefined" && - typeof state.anchorMap !== "undefined" - ) { - state.anchorMap[state.anchor] = result; - } - - ch = state.input.charCodeAt(state.position); - - while (ch !== 0) { - following = state.input.charCodeAt(state.position + 1); - line = state.line; // Save the current line. - pos = state.position; - - // - // Explicit notation case. There are two separate blocks: - // first for the key (denoted by "?") and second for the value (denoted by ":") - // - if ((ch === 0x3f || /* ? */ ch === 0x3a) && /* : */ isWsOrEol(following)) { - if (ch === 0x3f /* ? */) { - if (atExplicitKey) { - storeMappingPair( - state, - result, - overridableKeys, - keyTag as string, - keyNode, - null, - ); - keyTag = keyNode = valueNode = null; - } - - detected = true; - atExplicitKey = true; - allowCompact = true; - } else if (atExplicitKey) { - // i.e. 0x3A/* : */ === character after the explicit key. - atExplicitKey = false; - allowCompact = true; - } else { - return throwError( - state, - "incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line", - ); - } - - state.position += 1; - ch = following; - - // - // Implicit notation case. Flow-style node as the key first, then ":", and the value. - // - // eslint-disable-next-line @typescript-eslint/no-use-before-define - } else if (composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) { - if (state.line === line) { - ch = state.input.charCodeAt(state.position); - - while (isWhiteSpace(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (ch === 0x3a /* : */) { - ch = state.input.charCodeAt(++state.position); - - if (!isWsOrEol(ch)) { - return throwError( - state, - "a whitespace character is expected after the key-value separator within a block mapping", - ); - } - - if (atExplicitKey) { - storeMappingPair( - state, - result, - overridableKeys, - keyTag as string, - keyNode, - null, - ); - keyTag = keyNode = valueNode = null; - } - - detected = true; - atExplicitKey = false; - allowCompact = false; - keyTag = state.tag; - keyNode = state.result; - } else if (detected) { - return throwError( - state, - "can not read an implicit mapping pair; a colon is missed", - ); - } else { - state.tag = tag; - state.anchor = anchor; - return true; // Keep the result of `composeNode`. - } - } else if (detected) { - return throwError( - state, - "can not read a block mapping entry; a multiline key may not be an implicit key", - ); - } else { - state.tag = tag; - state.anchor = anchor; - return true; // Keep the result of `composeNode`. - } - } else { - break; // Reading is done. Go to the epilogue. - } - - // - // Common reading code for both explicit and implicit notations. - // - if (state.line === line || state.lineIndent > nodeIndent) { - if ( - // eslint-disable-next-line @typescript-eslint/no-use-before-define - composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact) - ) { - if (atExplicitKey) { - keyNode = state.result; - } else { - valueNode = state.result; - } - } - - if (!atExplicitKey) { - storeMappingPair( - state, - result, - overridableKeys, - keyTag as string, - keyNode, - valueNode, - line, - pos, - ); - keyTag = keyNode = valueNode = null; - } - - skipSeparationSpace(state, true, -1); - ch = state.input.charCodeAt(state.position); - } - - if (state.lineIndent > nodeIndent && ch !== 0) { - return throwError(state, "bad indentation of a mapping entry"); - } else if (state.lineIndent < nodeIndent) { - break; - } - } - - // - // Epilogue. - // - - // Special case: last mapping's node contains only the key in explicit notation. - if (atExplicitKey) { - storeMappingPair( - state, - result, - overridableKeys, - keyTag as string, - keyNode, - null, - ); - } - - // Expose the resulting mapping. - if (detected) { - state.tag = tag; - state.anchor = anchor; - state.kind = "mapping"; - state.result = result; - } - - return detected; -} - -function readTagProperty(state: LoaderState): boolean { - let position: number, - isVerbatim = false, - isNamed = false, - tagHandle = "", - tagName: string, - ch: number; - - ch = state.input.charCodeAt(state.position); - - if (ch !== 0x21 /* ! */) return false; - - if (state.tag !== null) { - return throwError(state, "duplication of a tag property"); - } - - ch = state.input.charCodeAt(++state.position); - - if (ch === 0x3c /* < */) { - isVerbatim = true; - ch = state.input.charCodeAt(++state.position); - } else if (ch === 0x21 /* ! */) { - isNamed = true; - tagHandle = "!!"; - ch = state.input.charCodeAt(++state.position); - } else { - tagHandle = "!"; - } - - position = state.position; - - if (isVerbatim) { - do { - ch = state.input.charCodeAt(++state.position); - } while (ch !== 0 && ch !== 0x3e /* > */); - - if (state.position < state.length) { - tagName = state.input.slice(position, state.position); - ch = state.input.charCodeAt(++state.position); - } else { - return throwError( - state, - "unexpected end of the stream within a verbatim tag", - ); - } - } else { - while (ch !== 0 && !isWsOrEol(ch)) { - if (ch === 0x21 /* ! */) { - if (!isNamed) { - tagHandle = state.input.slice(position - 1, state.position + 1); - - if (!PATTERN_TAG_HANDLE.test(tagHandle)) { - return throwError( - state, - "named tag handle cannot contain such characters", - ); - } - - isNamed = true; - position = state.position + 1; - } else { - return throwError( - state, - "tag suffix cannot contain exclamation marks", - ); - } - } - - ch = state.input.charCodeAt(++state.position); - } - - tagName = state.input.slice(position, state.position); - - if (PATTERN_FLOW_INDICATORS.test(tagName)) { - return throwError( - state, - "tag suffix cannot contain flow indicator characters", - ); - } - } - - if (tagName && !PATTERN_TAG_URI.test(tagName)) { - return throwError( - state, - `tag name cannot contain such characters: ${tagName}`, - ); - } - - if (isVerbatim) { - state.tag = tagName; - } else if ( - typeof state.tagMap !== "undefined" && - _hasOwnProperty.call(state.tagMap, tagHandle) - ) { - state.tag = state.tagMap[tagHandle] + tagName; - } else if (tagHandle === "!") { - state.tag = `!${tagName}`; - } else if (tagHandle === "!!") { - state.tag = `tag:yaml.org,2002:${tagName}`; - } else { - return throwError(state, `undeclared tag handle "${tagHandle}"`); - } - - return true; -} - -function readAnchorProperty(state: LoaderState): boolean { - let ch = state.input.charCodeAt(state.position); - if (ch !== 0x26 /* & */) return false; - - if (state.anchor !== null) { - return throwError(state, "duplication of an anchor property"); - } - ch = state.input.charCodeAt(++state.position); - - const position = state.position; - while (ch !== 0 && !isWsOrEol(ch) && !isFlowIndicator(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (state.position === position) { - return throwError( - state, - "name of an anchor node must contain at least one character", - ); - } - - state.anchor = state.input.slice(position, state.position); - return true; -} - -function readAlias(state: LoaderState): boolean { - let ch = state.input.charCodeAt(state.position); - - if (ch !== 0x2a /* * */) return false; - - ch = state.input.charCodeAt(++state.position); - const _position = state.position; - - while (ch !== 0 && !isWsOrEol(ch) && !isFlowIndicator(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (state.position === _position) { - return throwError( - state, - "name of an alias node must contain at least one character", - ); - } - - const alias = state.input.slice(_position, state.position); - if ( - typeof state.anchorMap !== "undefined" && - !Object.prototype.hasOwnProperty.call(state.anchorMap, alias) - ) { - return throwError(state, `unidentified alias "${alias}"`); - } - - if (typeof state.anchorMap !== "undefined") { - state.result = state.anchorMap[alias]; - } - skipSeparationSpace(state, true, -1); - return true; -} - -function composeNode( - state: LoaderState, - parentIndent: number, - nodeContext: number, - allowToSeek: boolean, - allowCompact: boolean, -): boolean { - let allowBlockScalars: boolean, - allowBlockCollections: boolean, - indentStatus = 1, // 1: this>parent, 0: this=parent, -1: this parentIndent) { - indentStatus = 1; - } else if (state.lineIndent === parentIndent) { - indentStatus = 0; - } else if (state.lineIndent < parentIndent) { - indentStatus = -1; - } - } - } - - if (indentStatus === 1) { - while (readTagProperty(state) || readAnchorProperty(state)) { - if (skipSeparationSpace(state, true, -1)) { - atNewLine = true; - allowBlockCollections = allowBlockStyles; - - if (state.lineIndent > parentIndent) { - indentStatus = 1; - } else if (state.lineIndent === parentIndent) { - indentStatus = 0; - } else if (state.lineIndent < parentIndent) { - indentStatus = -1; - } - } else { - allowBlockCollections = false; - } - } - } - - if (allowBlockCollections) { - allowBlockCollections = atNewLine || allowCompact; - } - - if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) { - const cond = CONTEXT_FLOW_IN === nodeContext || - CONTEXT_FLOW_OUT === nodeContext; - flowIndent = cond ? parentIndent : parentIndent + 1; - - blockIndent = state.position - state.lineStart; - - if (indentStatus === 1) { - if ( - (allowBlockCollections && - (readBlockSequence(state, blockIndent) || - readBlockMapping(state, blockIndent, flowIndent))) || - readFlowCollection(state, flowIndent) - ) { - hasContent = true; - } else { - if ( - (allowBlockScalars && readBlockScalar(state, flowIndent)) || - readSingleQuotedScalar(state, flowIndent) || - readDoubleQuotedScalar(state, flowIndent) - ) { - hasContent = true; - } else if (readAlias(state)) { - hasContent = true; - - if (state.tag !== null || state.anchor !== null) { - return throwError( - state, - "alias node should not have Any properties", - ); - } - } else if ( - readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext) - ) { - hasContent = true; - - if (state.tag === null) { - state.tag = "?"; - } - } - - if (state.anchor !== null && typeof state.anchorMap !== "undefined") { - state.anchorMap[state.anchor] = state.result; - } - } - } else if (indentStatus === 0) { - // Special case: block sequences are allowed to have same indentation level as the parent. - // http://www.yaml.org/spec/1.2/spec.html#id2799784 - hasContent = allowBlockCollections && - readBlockSequence(state, blockIndent); - } - } - - if (state.tag !== null && state.tag !== "!") { - if (state.tag === "?") { - for ( - let typeIndex = 0, typeQuantity = state.implicitTypes.length; - typeIndex < typeQuantity; - typeIndex++ - ) { - type = state.implicitTypes[typeIndex]; - - // Implicit resolving is not allowed for non-scalar types, and '?' - // non-specific tag is only assigned to plain scalars. So, it isn't - // needed to check for 'kind' conformity. - - if (type.resolve(state.result)) { - // `state.result` updated in resolver if matched - state.result = type.construct(state.result); - state.tag = type.tag; - if (state.anchor !== null && typeof state.anchorMap !== "undefined") { - state.anchorMap[state.anchor] = state.result; - } - break; - } - } - } else if ( - _hasOwnProperty.call(state.typeMap[state.kind || "fallback"], state.tag) - ) { - type = state.typeMap[state.kind || "fallback"][state.tag]; - - if (state.result !== null && type.kind !== state.kind) { - return throwError( - state, - `unacceptable node kind for !<${state.tag}> tag; it should be "${type.kind}", not "${state.kind}"`, - ); - } - - if (!type.resolve(state.result)) { - // `state.result` updated in resolver if matched - return throwError( - state, - `cannot resolve a node with !<${state.tag}> explicit tag`, - ); - } else { - state.result = type.construct(state.result); - if (state.anchor !== null && typeof state.anchorMap !== "undefined") { - state.anchorMap[state.anchor] = state.result; - } - } - } else { - return throwError(state, `unknown tag !<${state.tag}>`); - } - } - - if (state.listener && state.listener !== null) { - state.listener("close", state); - } - return state.tag !== null || state.anchor !== null || hasContent; -} - -function readDocument(state: LoaderState): void { - const documentStart = state.position; - let position: number, - directiveName: string, - directiveArgs: string[], - hasDirectives = false, - ch: number; - - state.version = null; - state.checkLineBreaks = state.legacy; - state.tagMap = {}; - state.anchorMap = {}; - - while ((ch = state.input.charCodeAt(state.position)) !== 0) { - skipSeparationSpace(state, true, -1); - - ch = state.input.charCodeAt(state.position); - - if (state.lineIndent > 0 || ch !== 0x25 /* % */) { - break; - } - - hasDirectives = true; - ch = state.input.charCodeAt(++state.position); - position = state.position; - - while (ch !== 0 && !isWsOrEol(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - directiveName = state.input.slice(position, state.position); - directiveArgs = []; - - if (directiveName.length < 1) { - return throwError( - state, - "directive name must not be less than one character in length", - ); - } - - while (ch !== 0) { - while (isWhiteSpace(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (ch === 0x23 /* # */) { - do { - ch = state.input.charCodeAt(++state.position); - } while (ch !== 0 && !isEOL(ch)); - break; - } - - if (isEOL(ch)) break; - - position = state.position; - - while (ch !== 0 && !isWsOrEol(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - directiveArgs.push(state.input.slice(position, state.position)); - } - - if (ch !== 0) readLineBreak(state); - - if (_hasOwnProperty.call(directiveHandlers, directiveName)) { - directiveHandlers[directiveName](state, directiveName, ...directiveArgs); - } else { - throwWarning(state, `unknown document directive "${directiveName}"`); - } - } - - skipSeparationSpace(state, true, -1); - - if ( - state.lineIndent === 0 && - state.input.charCodeAt(state.position) === 0x2d /* - */ && - state.input.charCodeAt(state.position + 1) === 0x2d /* - */ && - state.input.charCodeAt(state.position + 2) === 0x2d /* - */ - ) { - state.position += 3; - skipSeparationSpace(state, true, -1); - } else if (hasDirectives) { - return throwError(state, "directives end mark is expected"); - } - - composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true); - skipSeparationSpace(state, true, -1); - - if ( - state.checkLineBreaks && - PATTERN_NON_ASCII_LINE_BREAKS.test( - state.input.slice(documentStart, state.position), - ) - ) { - throwWarning(state, "non-ASCII line breaks are interpreted as content"); - } - - state.documents.push(state.result); - - if (state.position === state.lineStart && testDocumentSeparator(state)) { - if (state.input.charCodeAt(state.position) === 0x2e /* . */) { - state.position += 3; - skipSeparationSpace(state, true, -1); - } - return; - } - - if (state.position < state.length - 1) { - return throwError( - state, - "end of the stream or a document separator is expected", - ); - } else { - return; - } -} - -function loadDocuments(input: string, options?: LoaderStateOptions): unknown[] { - input = String(input); - options = options || {}; - - if (input.length !== 0) { - // Add tailing `\n` if not exists - if ( - input.charCodeAt(input.length - 1) !== 0x0a /* LF */ && - input.charCodeAt(input.length - 1) !== 0x0d /* CR */ - ) { - input += "\n"; - } - - // Strip BOM - if (input.charCodeAt(0) === 0xfeff) { - input = input.slice(1); - } - } - - const state = new LoaderState(input, options); - - // Use 0 as string terminator. That significantly simplifies bounds check. - state.input += "\0"; - - while (state.input.charCodeAt(state.position) === 0x20 /* Space */) { - state.lineIndent += 1; - state.position += 1; - } - - while (state.position < state.length - 1) { - readDocument(state); - } - - return state.documents; -} - -export type CbFunction = (doc: unknown) => void; -function isCbFunction(fn: unknown): fn is CbFunction { - return typeof fn === "function"; -} - -export function loadAll( - input: string, - iteratorOrOption?: T, - options?: LoaderStateOptions, -): T extends CbFunction ? void : unknown[] { - if (!isCbFunction(iteratorOrOption)) { - return loadDocuments(input, iteratorOrOption as LoaderStateOptions) as Any; - } - - const documents = loadDocuments(input, options); - const iterator = iteratorOrOption; - for (let index = 0, length = documents.length; index < length; index++) { - iterator(documents[index]); - } - - return void 0 as Any; -} - -export function load(input: string, options?: LoaderStateOptions): unknown { - const documents = loadDocuments(input, options); - - if (documents.length === 0) { - return; - } - if (documents.length === 1) { - return documents[0]; - } - throw new YAMLError( - "expected a single document in the stream, but found more", - ); -} diff --git a/std/encoding/_yaml/loader/loader_state.ts b/std/encoding/_yaml/loader/loader_state.ts deleted file mode 100644 index b5ec776802..0000000000 --- a/std/encoding/_yaml/loader/loader_state.ts +++ /dev/null @@ -1,75 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import type { YAMLError } from "../error.ts"; -import type { Schema, SchemaDefinition, TypeMap } from "../schema.ts"; -import { State } from "../state.ts"; -import type { Type } from "../type.ts"; -import type { Any, ArrayObject } from "../utils.ts"; - -export interface LoaderStateOptions { - legacy?: boolean; - listener?: ((...args: Any[]) => void) | null; - /** string to be used as a file path in error/warning messages. */ - filename?: string; - /** specifies a schema to use. */ - schema?: SchemaDefinition; - /** compatibility with JSON.parse behaviour. */ - json?: boolean; - /** function to call on warning messages. */ - onWarning?(this: null, e?: YAMLError): void; -} - -// deno-lint-ignore no-explicit-any -export type ResultType = any[] | Record | string; - -export class LoaderState extends State { - public documents: Any[] = []; - public length: number; - public lineIndent = 0; - public lineStart = 0; - public position = 0; - public line = 0; - public filename?: string; - public onWarning?: (...args: Any[]) => void; - public legacy: boolean; - public json: boolean; - public listener?: ((...args: Any[]) => void) | null; - public implicitTypes: Type[]; - public typeMap: TypeMap; - - public version?: string | null; - public checkLineBreaks?: boolean; - public tagMap?: ArrayObject; - public anchorMap?: ArrayObject; - public tag?: string | null; - public anchor?: string | null; - public kind?: string | null; - public result: ResultType | null = ""; - - constructor( - public input: string, - { - filename, - schema, - onWarning, - legacy = false, - json = false, - listener = null, - }: LoaderStateOptions, - ) { - super(schema); - this.filename = filename; - this.onWarning = onWarning; - this.legacy = legacy; - this.json = json; - this.listener = listener; - - this.implicitTypes = (this.schema as Schema).compiledImplicit; - this.typeMap = (this.schema as Schema).compiledTypeMap; - - this.length = input.length; - } -} diff --git a/std/encoding/_yaml/mark.ts b/std/encoding/_yaml/mark.ts deleted file mode 100644 index 399d6a7f31..0000000000 --- a/std/encoding/_yaml/mark.ts +++ /dev/null @@ -1,79 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { repeat } from "./utils.ts"; - -export class Mark { - constructor( - public name: string, - public buffer: string, - public position: number, - public line: number, - public column: number, - ) {} - - public getSnippet(indent = 4, maxLength = 75): string | null { - if (!this.buffer) return null; - - let head = ""; - let start = this.position; - - while ( - start > 0 && - "\x00\r\n\x85\u2028\u2029".indexOf(this.buffer.charAt(start - 1)) === -1 - ) { - start -= 1; - if (this.position - start > maxLength / 2 - 1) { - head = " ... "; - start += 5; - break; - } - } - - let tail = ""; - let end = this.position; - - while ( - end < this.buffer.length && - "\x00\r\n\x85\u2028\u2029".indexOf(this.buffer.charAt(end)) === -1 - ) { - end += 1; - if (end - this.position > maxLength / 2 - 1) { - tail = " ... "; - end -= 5; - break; - } - } - - const snippet = this.buffer.slice(start, end); - return `${repeat(" ", indent)}${head}${snippet}${tail}\n${ - repeat( - " ", - indent + this.position - start + head.length, - ) - }^`; - } - - public toString(compact?: boolean): string { - let snippet, - where = ""; - - if (this.name) { - where += `in "${this.name}" `; - } - - where += `at line ${this.line + 1}, column ${this.column + 1}`; - - if (!compact) { - snippet = this.getSnippet(); - - if (snippet) { - where += `:\n${snippet}`; - } - } - - return where; - } -} diff --git a/std/encoding/_yaml/parse.ts b/std/encoding/_yaml/parse.ts deleted file mode 100644 index a6469f7996..0000000000 --- a/std/encoding/_yaml/parse.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { CbFunction, load, loadAll } from "./loader/loader.ts"; -import type { LoaderStateOptions } from "./loader/loader_state.ts"; - -export type ParseOptions = LoaderStateOptions; - -/** - * Parses `content` as single YAML document. - * - * Returns a JavaScript object or throws `YAMLException` on error. - * By default, does not support regexps, functions and undefined. This method is safe for untrusted data. - * - */ -export function parse(content: string, options?: ParseOptions): unknown { - return load(content, options); -} - -/** - * Same as `parse()`, but understands multi-document sources. - * Applies iterator to each document if specified, or returns array of documents. - */ -export function parseAll( - content: string, - iterator?: CbFunction, - options?: ParseOptions, -): unknown { - return loadAll(content, iterator, options); -} diff --git a/std/encoding/_yaml/parse_test.ts b/std/encoding/_yaml/parse_test.ts deleted file mode 100644 index e810637cb0..0000000000 --- a/std/encoding/_yaml/parse_test.ts +++ /dev/null @@ -1,56 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { parse, parseAll } from "./parse.ts"; -import { assertEquals } from "../../testing/asserts.ts"; - -Deno.test({ - name: "`parse` parses single document yaml string", - fn(): void { - const yaml = ` - test: toto - foo: - bar: True - baz: 1 - qux: ~ - `; - - const expected = { test: "toto", foo: { bar: true, baz: 1, qux: null } }; - - assertEquals(parse(yaml), expected); - }, -}); - -Deno.test({ - name: "`parseAll` parses the yaml string with multiple documents", - fn(): void { - const yaml = ` ---- -id: 1 -name: Alice ---- -id: 2 -name: Bob ---- -id: 3 -name: Eve - `; - const expected = [ - { - id: 1, - name: "Alice", - }, - { - id: 2, - name: "Bob", - }, - { - id: 3, - name: "Eve", - }, - ]; - assertEquals(parseAll(yaml), expected); - }, -}); diff --git a/std/encoding/_yaml/schema.ts b/std/encoding/_yaml/schema.ts deleted file mode 100644 index 512f9f643b..0000000000 --- a/std/encoding/_yaml/schema.ts +++ /dev/null @@ -1,101 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { YAMLError } from "./error.ts"; -import type { KindType, Type } from "./type.ts"; -import type { Any, ArrayObject } from "./utils.ts"; - -function compileList( - schema: Schema, - name: "implicit" | "explicit", - result: Type[], -): Type[] { - const exclude: number[] = []; - - for (const includedSchema of schema.include) { - result = compileList(includedSchema, name, result); - } - - for (const currentType of schema[name]) { - for ( - let previousIndex = 0; - previousIndex < result.length; - previousIndex++ - ) { - const previousType = result[previousIndex]; - if ( - previousType.tag === currentType.tag && - previousType.kind === currentType.kind - ) { - exclude.push(previousIndex); - } - } - - result.push(currentType); - } - - return result.filter((type, index): unknown => !exclude.includes(index)); -} - -export type TypeMap = { [k in KindType | "fallback"]: ArrayObject }; -function compileMap(...typesList: Type[][]): TypeMap { - const result: TypeMap = { - fallback: {}, - mapping: {}, - scalar: {}, - sequence: {}, - }; - - for (const types of typesList) { - for (const type of types) { - if (type.kind !== null) { - result[type.kind][type.tag] = result["fallback"][type.tag] = type; - } - } - } - return result; -} - -export class Schema implements SchemaDefinition { - public static SCHEMA_DEFAULT?: Schema; - - public implicit: Type[]; - public explicit: Type[]; - public include: Schema[]; - - public compiledImplicit: Type[]; - public compiledExplicit: Type[]; - public compiledTypeMap: TypeMap; - - constructor(definition: SchemaDefinition) { - this.explicit = definition.explicit || []; - this.implicit = definition.implicit || []; - this.include = definition.include || []; - - for (const type of this.implicit) { - if (type.loadKind && type.loadKind !== "scalar") { - throw new YAMLError( - // eslint-disable-next-line max-len - "There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.", - ); - } - } - - this.compiledImplicit = compileList(this, "implicit", []); - this.compiledExplicit = compileList(this, "explicit", []); - this.compiledTypeMap = compileMap( - this.compiledImplicit, - this.compiledExplicit, - ); - } - - public static create(): void {} -} - -export interface SchemaDefinition { - implicit?: Any[]; - explicit?: Type[]; - include?: Schema[]; -} diff --git a/std/encoding/_yaml/schema/core.ts b/std/encoding/_yaml/schema/core.ts deleted file mode 100644 index ad95f73304..0000000000 --- a/std/encoding/_yaml/schema/core.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Schema } from "../schema.ts"; -import { json } from "./json.ts"; - -// Standard YAML's Core schema. -// http://www.yaml.org/spec/1.2/spec.html#id2804923 -export const core = new Schema({ - include: [json], -}); diff --git a/std/encoding/_yaml/schema/default.ts b/std/encoding/_yaml/schema/default.ts deleted file mode 100644 index 60b6d18ad2..0000000000 --- a/std/encoding/_yaml/schema/default.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Schema } from "../schema.ts"; -import { binary, merge, omap, pairs, set, timestamp } from "../type/mod.ts"; -import { core } from "./core.ts"; - -// JS-YAML's default schema for `safeLoad` function. -// It is not described in the YAML specification. -export const def = new Schema({ - explicit: [binary, omap, pairs, set], - implicit: [timestamp, merge], - include: [core], -}); diff --git a/std/encoding/_yaml/schema/failsafe.ts b/std/encoding/_yaml/schema/failsafe.ts deleted file mode 100644 index c44e3c5670..0000000000 --- a/std/encoding/_yaml/schema/failsafe.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Schema } from "../schema.ts"; -import { map, seq, str } from "../type/mod.ts"; - -// Standard YAML's Failsafe schema. -// http://www.yaml.org/spec/1.2/spec.html#id2802346 -export const failsafe = new Schema({ - explicit: [str, seq, map], -}); diff --git a/std/encoding/_yaml/schema/json.ts b/std/encoding/_yaml/schema/json.ts deleted file mode 100644 index 0d7c2567fd..0000000000 --- a/std/encoding/_yaml/schema/json.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Schema } from "../schema.ts"; -import { bool, float, int, nil } from "../type/mod.ts"; -import { failsafe } from "./failsafe.ts"; - -// Standard YAML's JSON schema. -// http://www.yaml.org/spec/1.2/spec.html#id2803231 -export const json = new Schema({ - implicit: [nil, bool, int, float], - include: [failsafe], -}); diff --git a/std/encoding/_yaml/schema/mod.ts b/std/encoding/_yaml/schema/mod.ts deleted file mode 100644 index a12075feb2..0000000000 --- a/std/encoding/_yaml/schema/mod.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -export { core as CORE_SCHEMA } from "./core.ts"; -export { def as DEFAULT_SCHEMA } from "./default.ts"; -export { failsafe as FAILSAFE_SCHEMA } from "./failsafe.ts"; -export { json as JSON_SCHEMA } from "./json.ts"; diff --git a/std/encoding/_yaml/state.ts b/std/encoding/_yaml/state.ts deleted file mode 100644 index 85cd91118d..0000000000 --- a/std/encoding/_yaml/state.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import type { SchemaDefinition } from "./schema.ts"; -import { DEFAULT_SCHEMA } from "./schema/mod.ts"; - -export abstract class State { - constructor(public schema: SchemaDefinition = DEFAULT_SCHEMA) {} -} diff --git a/std/encoding/_yaml/stringify.ts b/std/encoding/_yaml/stringify.ts deleted file mode 100644 index ea43cdfeff..0000000000 --- a/std/encoding/_yaml/stringify.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { dump } from "./dumper/dumper.ts"; -import type { DumperStateOptions } from "./dumper/dumper_state.ts"; - -export type DumpOptions = DumperStateOptions; - -/** - * Serializes `object` as a YAML document. - * - * You can disable exceptions by setting the skipInvalid option to true. - */ -export function stringify( - obj: Record, - options?: DumpOptions, -): string { - return dump(obj, options); -} diff --git a/std/encoding/_yaml/stringify_test.ts b/std/encoding/_yaml/stringify_test.ts deleted file mode 100644 index 03a7d8f3df..0000000000 --- a/std/encoding/_yaml/stringify_test.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { assertEquals } from "../../testing/asserts.ts"; -import { stringify } from "./stringify.ts"; - -Deno.test({ - name: "stringified correctly", - fn(): void { - const FIXTURE = { - foo: { - bar: true, - test: [ - "a", - "b", - { - a: false, - }, - { - a: false, - }, - ], - }, - test: "foobar", - }; - - const ASSERTS = `foo: - bar: true - test: - - a - - b - - a: false - - a: false -test: foobar -`; - - assertEquals(stringify(FIXTURE), ASSERTS); - }, -}); diff --git a/std/encoding/_yaml/type.ts b/std/encoding/_yaml/type.ts deleted file mode 100644 index e5659bfabf..0000000000 --- a/std/encoding/_yaml/type.ts +++ /dev/null @@ -1,55 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import type { Any, ArrayObject } from "./utils.ts"; - -export type KindType = "sequence" | "scalar" | "mapping"; -export type StyleVariant = "lowercase" | "uppercase" | "camelcase" | "decimal"; -export type RepresentFn = (data: Any, style?: StyleVariant) => Any; - -const DEFAULT_RESOLVE = (): boolean => true; -const DEFAULT_CONSTRUCT = (data: Any): Any => data; - -interface TypeOptions { - kind: KindType; - resolve?: (data: Any) => boolean; - construct?: (data: string) => Any; - instanceOf?: Any; - predicate?: (data: Record) => boolean; - represent?: RepresentFn | ArrayObject; - defaultStyle?: StyleVariant; - styleAliases?: ArrayObject; -} - -function checkTagFormat(tag: string): string { - return tag; -} - -export class Type { - public tag: string; - public kind: KindType | null = null; - public instanceOf: Any; - public predicate?: (data: Record) => boolean; - public represent?: RepresentFn | ArrayObject; - public defaultStyle?: StyleVariant; - public styleAliases?: ArrayObject; - public loadKind?: KindType; - - constructor(tag: string, options?: TypeOptions) { - this.tag = checkTagFormat(tag); - if (options) { - this.kind = options.kind; - this.resolve = options.resolve || DEFAULT_RESOLVE; - this.construct = options.construct || DEFAULT_CONSTRUCT; - this.instanceOf = options.instanceOf; - this.predicate = options.predicate; - this.represent = options.represent; - this.defaultStyle = options.defaultStyle; - this.styleAliases = options.styleAliases; - } - } - public resolve: (data?: Any) => boolean = (): boolean => true; - public construct: (data?: Any) => Any = (data): Any => data; -} diff --git a/std/encoding/_yaml/type/binary.ts b/std/encoding/_yaml/type/binary.ts deleted file mode 100644 index 3a7982aa40..0000000000 --- a/std/encoding/_yaml/type/binary.ts +++ /dev/null @@ -1,136 +0,0 @@ -// Ported from js-yaml v3.13.1: -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { Type } from "../type.ts"; -import type { Any } from "../utils.ts"; - -// [ 64, 65, 66 ] -> [ padding, CR, LF ] -const BASE64_MAP = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r"; - -function resolveYamlBinary(data: Any): boolean { - if (data === null) return false; - - let code: number; - let bitlen = 0; - const max = data.length; - const map = BASE64_MAP; - - // Convert one by one. - for (let idx = 0; idx < max; idx++) { - code = map.indexOf(data.charAt(idx)); - - // Skip CR/LF - if (code > 64) continue; - - // Fail on illegal characters - if (code < 0) return false; - - bitlen += 6; - } - - // If there are any bits left, source was corrupted - return bitlen % 8 === 0; -} - -function constructYamlBinary(data: string): Deno.Buffer { - // remove CR/LF & padding to simplify scan - const input = data.replace(/[\r\n=]/g, ""); - const max = input.length; - const map = BASE64_MAP; - - // Collect by 6*4 bits (3 bytes) - - const result = []; - let bits = 0; - for (let idx = 0; idx < max; idx++) { - if (idx % 4 === 0 && idx) { - result.push((bits >> 16) & 0xff); - result.push((bits >> 8) & 0xff); - result.push(bits & 0xff); - } - - bits = (bits << 6) | map.indexOf(input.charAt(idx)); - } - - // Dump tail - - const tailbits = (max % 4) * 6; - - if (tailbits === 0) { - result.push((bits >> 16) & 0xff); - result.push((bits >> 8) & 0xff); - result.push(bits & 0xff); - } else if (tailbits === 18) { - result.push((bits >> 10) & 0xff); - result.push((bits >> 2) & 0xff); - } else if (tailbits === 12) { - result.push((bits >> 4) & 0xff); - } - - return new Deno.Buffer(new Uint8Array(result)); -} - -function representYamlBinary(object: Uint8Array): string { - const max = object.length; - const map = BASE64_MAP; - - // Convert every three bytes to 4 ASCII characters. - - let result = ""; - let bits = 0; - for (let idx = 0; idx < max; idx++) { - if (idx % 3 === 0 && idx) { - result += map[(bits >> 18) & 0x3f]; - result += map[(bits >> 12) & 0x3f]; - result += map[(bits >> 6) & 0x3f]; - result += map[bits & 0x3f]; - } - - bits = (bits << 8) + object[idx]; - } - - // Dump tail - - const tail = max % 3; - - if (tail === 0) { - result += map[(bits >> 18) & 0x3f]; - result += map[(bits >> 12) & 0x3f]; - result += map[(bits >> 6) & 0x3f]; - result += map[bits & 0x3f]; - } else if (tail === 2) { - result += map[(bits >> 10) & 0x3f]; - result += map[(bits >> 4) & 0x3f]; - result += map[(bits << 2) & 0x3f]; - result += map[64]; - } else if (tail === 1) { - result += map[(bits >> 2) & 0x3f]; - result += map[(bits << 4) & 0x3f]; - result += map[64]; - result += map[64]; - } - - return result; -} - -function isBinary(obj: Any): obj is Deno.Buffer { - const buf = new Deno.Buffer(); - try { - if (0 > buf.readFromSync(obj as Deno.Buffer)) return true; - return false; - } catch { - return false; - } finally { - buf.reset(); - } -} - -export const binary = new Type("tag:yaml.org,2002:binary", { - construct: constructYamlBinary, - kind: "scalar", - predicate: isBinary, - represent: representYamlBinary, - resolve: resolveYamlBinary, -}); diff --git a/std/encoding/_yaml/type/bool.ts b/std/encoding/_yaml/type/bool.ts deleted file mode 100644 index 3dc7149877..0000000000 --- a/std/encoding/_yaml/type/bool.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Type } from "../type.ts"; -import { isBoolean } from "../utils.ts"; - -function resolveYamlBoolean(data: string): boolean { - const max = data.length; - - return ( - (max === 4 && (data === "true" || data === "True" || data === "TRUE")) || - (max === 5 && (data === "false" || data === "False" || data === "FALSE")) - ); -} - -function constructYamlBoolean(data: string): boolean { - return data === "true" || data === "True" || data === "TRUE"; -} - -export const bool = new Type("tag:yaml.org,2002:bool", { - construct: constructYamlBoolean, - defaultStyle: "lowercase", - kind: "scalar", - predicate: isBoolean, - represent: { - lowercase(object: boolean): string { - return object ? "true" : "false"; - }, - uppercase(object: boolean): string { - return object ? "TRUE" : "FALSE"; - }, - camelcase(object: boolean): string { - return object ? "True" : "False"; - }, - }, - resolve: resolveYamlBoolean, -}); diff --git a/std/encoding/_yaml/type/float.ts b/std/encoding/_yaml/type/float.ts deleted file mode 100644 index b4d6a3f3f1..0000000000 --- a/std/encoding/_yaml/type/float.ts +++ /dev/null @@ -1,125 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { StyleVariant, Type } from "../type.ts"; -import { Any, isNegativeZero } from "../utils.ts"; - -const YAML_FLOAT_PATTERN = new RegExp( - // 2.5e4, 2.5 and integers - "^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?" + - // .2e4, .2 - // special case, seems not from spec - "|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?" + - // 20:59 - "|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*" + - // .inf - "|[-+]?\\.(?:inf|Inf|INF)" + - // .nan - "|\\.(?:nan|NaN|NAN))$", -); - -function resolveYamlFloat(data: string): boolean { - if ( - !YAML_FLOAT_PATTERN.test(data) || - // Quick hack to not allow integers end with `_` - // Probably should update regexp & check speed - data[data.length - 1] === "_" - ) { - return false; - } - - return true; -} - -function constructYamlFloat(data: string): number { - let value = data.replace(/_/g, "").toLowerCase(); - const sign = value[0] === "-" ? -1 : 1; - const digits: number[] = []; - - if ("+-".indexOf(value[0]) >= 0) { - value = value.slice(1); - } - - if (value === ".inf") { - return sign === 1 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY; - } - if (value === ".nan") { - return NaN; - } - if (value.indexOf(":") >= 0) { - value.split(":").forEach((v): void => { - digits.unshift(parseFloat(v)); - }); - - let valueNb = 0.0; - let base = 1; - - digits.forEach((d): void => { - valueNb += d * base; - base *= 60; - }); - - return sign * valueNb; - } - return sign * parseFloat(value); -} - -const SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/; - -function representYamlFloat(object: Any, style?: StyleVariant): Any { - if (isNaN(object)) { - switch (style) { - case "lowercase": - return ".nan"; - case "uppercase": - return ".NAN"; - case "camelcase": - return ".NaN"; - } - } else if (Number.POSITIVE_INFINITY === object) { - switch (style) { - case "lowercase": - return ".inf"; - case "uppercase": - return ".INF"; - case "camelcase": - return ".Inf"; - } - } else if (Number.NEGATIVE_INFINITY === object) { - switch (style) { - case "lowercase": - return "-.inf"; - case "uppercase": - return "-.INF"; - case "camelcase": - return "-.Inf"; - } - } else if (isNegativeZero(object)) { - return "-0.0"; - } - - const res = object.toString(10); - - // JS stringifier can build scientific format without dots: 5e-100, - // while YAML requires dot: 5.e-100. Fix it with simple hack - - return SCIENTIFIC_WITHOUT_DOT.test(res) ? res.replace("e", ".e") : res; -} - -function isFloat(object: Any): boolean { - return ( - Object.prototype.toString.call(object) === "[object Number]" && - (object % 1 !== 0 || isNegativeZero(object)) - ); -} - -export const float = new Type("tag:yaml.org,2002:float", { - construct: constructYamlFloat, - defaultStyle: "lowercase", - kind: "scalar", - predicate: isFloat, - represent: representYamlFloat, - resolve: resolveYamlFloat, -}); diff --git a/std/encoding/_yaml/type/int.ts b/std/encoding/_yaml/type/int.ts deleted file mode 100644 index f166778c51..0000000000 --- a/std/encoding/_yaml/type/int.ts +++ /dev/null @@ -1,188 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Type } from "../type.ts"; -import { Any, isNegativeZero } from "../utils.ts"; - -function isHexCode(c: number): boolean { - return ( - (0x30 <= /* 0 */ c && c <= 0x39) /* 9 */ || - (0x41 <= /* A */ c && c <= 0x46) /* F */ || - (0x61 <= /* a */ c && c <= 0x66) /* f */ - ); -} - -function isOctCode(c: number): boolean { - return 0x30 <= /* 0 */ c && c <= 0x37 /* 7 */; -} - -function isDecCode(c: number): boolean { - return 0x30 <= /* 0 */ c && c <= 0x39 /* 9 */; -} - -function resolveYamlInteger(data: string): boolean { - const max = data.length; - let index = 0; - let hasDigits = false; - - if (!max) return false; - - let ch = data[index]; - - // sign - if (ch === "-" || ch === "+") { - ch = data[++index]; - } - - if (ch === "0") { - // 0 - if (index + 1 === max) return true; - ch = data[++index]; - - // base 2, base 8, base 16 - - if (ch === "b") { - // base 2 - index++; - - for (; index < max; index++) { - ch = data[index]; - if (ch === "_") continue; - if (ch !== "0" && ch !== "1") return false; - hasDigits = true; - } - return hasDigits && ch !== "_"; - } - - if (ch === "x") { - // base 16 - index++; - - for (; index < max; index++) { - ch = data[index]; - if (ch === "_") continue; - if (!isHexCode(data.charCodeAt(index))) return false; - hasDigits = true; - } - return hasDigits && ch !== "_"; - } - - // base 8 - for (; index < max; index++) { - ch = data[index]; - if (ch === "_") continue; - if (!isOctCode(data.charCodeAt(index))) return false; - hasDigits = true; - } - return hasDigits && ch !== "_"; - } - - // base 10 (except 0) or base 60 - - // value should not start with `_`; - if (ch === "_") return false; - - for (; index < max; index++) { - ch = data[index]; - if (ch === "_") continue; - if (ch === ":") break; - if (!isDecCode(data.charCodeAt(index))) { - return false; - } - hasDigits = true; - } - - // Should have digits and should not end with `_` - if (!hasDigits || ch === "_") return false; - - // if !base60 - done; - if (ch !== ":") return true; - - // base60 almost not used, no needs to optimize - return /^(:[0-5]?[0-9])+$/.test(data.slice(index)); -} - -function constructYamlInteger(data: string): number { - let value = data; - const digits: number[] = []; - - if (value.indexOf("_") !== -1) { - value = value.replace(/_/g, ""); - } - - let sign = 1; - let ch = value[0]; - if (ch === "-" || ch === "+") { - if (ch === "-") sign = -1; - value = value.slice(1); - ch = value[0]; - } - - if (value === "0") return 0; - - if (ch === "0") { - if (value[1] === "b") return sign * parseInt(value.slice(2), 2); - if (value[1] === "x") return sign * parseInt(value, 16); - return sign * parseInt(value, 8); - } - - if (value.indexOf(":") !== -1) { - value.split(":").forEach((v): void => { - digits.unshift(parseInt(v, 10)); - }); - - let valueInt = 0; - let base = 1; - - digits.forEach((d): void => { - valueInt += d * base; - base *= 60; - }); - - return sign * valueInt; - } - - return sign * parseInt(value, 10); -} - -function isInteger(object: Any): boolean { - return ( - Object.prototype.toString.call(object) === "[object Number]" && - object % 1 === 0 && - !isNegativeZero(object) - ); -} - -export const int = new Type("tag:yaml.org,2002:int", { - construct: constructYamlInteger, - defaultStyle: "decimal", - kind: "scalar", - predicate: isInteger, - represent: { - binary(obj: number): string { - return obj >= 0 - ? `0b${obj.toString(2)}` - : `-0b${obj.toString(2).slice(1)}`; - }, - octal(obj: number): string { - return obj >= 0 ? `0${obj.toString(8)}` : `-0${obj.toString(8).slice(1)}`; - }, - decimal(obj: number): string { - return obj.toString(10); - }, - hexadecimal(obj: number): string { - return obj >= 0 - ? `0x${obj.toString(16).toUpperCase()}` - : `-0x${obj.toString(16).toUpperCase().slice(1)}`; - }, - }, - resolve: resolveYamlInteger, - styleAliases: { - binary: [2, "bin"], - decimal: [10, "dec"], - hexadecimal: [16, "hex"], - octal: [8, "oct"], - }, -}); diff --git a/std/encoding/_yaml/type/map.ts b/std/encoding/_yaml/type/map.ts deleted file mode 100644 index 6457597bed..0000000000 --- a/std/encoding/_yaml/type/map.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Type } from "../type.ts"; -import type { Any } from "../utils.ts"; - -export const map = new Type("tag:yaml.org,2002:map", { - construct(data): Any { - return data !== null ? data : {}; - }, - kind: "mapping", -}); diff --git a/std/encoding/_yaml/type/merge.ts b/std/encoding/_yaml/type/merge.ts deleted file mode 100644 index 598b68ffa0..0000000000 --- a/std/encoding/_yaml/type/merge.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Type } from "../type.ts"; - -function resolveYamlMerge(data: string): boolean { - return data === "<<" || data === null; -} - -export const merge = new Type("tag:yaml.org,2002:merge", { - kind: "scalar", - resolve: resolveYamlMerge, -}); diff --git a/std/encoding/_yaml/type/mod.ts b/std/encoding/_yaml/type/mod.ts deleted file mode 100644 index 480b1fe615..0000000000 --- a/std/encoding/_yaml/type/mod.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -export { binary } from "./binary.ts"; -export { bool } from "./bool.ts"; -export { float } from "./float.ts"; -export { int } from "./int.ts"; -export { map } from "./map.ts"; -export { merge } from "./merge.ts"; -export { nil } from "./nil.ts"; -export { omap } from "./omap.ts"; -export { pairs } from "./pairs.ts"; -export { seq } from "./seq.ts"; -export { set } from "./set.ts"; -export { str } from "./str.ts"; -export { timestamp } from "./timestamp.ts"; diff --git a/std/encoding/_yaml/type/nil.ts b/std/encoding/_yaml/type/nil.ts deleted file mode 100644 index 03a2f6bd62..0000000000 --- a/std/encoding/_yaml/type/nil.ts +++ /dev/null @@ -1,45 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Type } from "../type.ts"; - -function resolveYamlNull(data: string): boolean { - const max = data.length; - - return ( - (max === 1 && data === "~") || - (max === 4 && (data === "null" || data === "Null" || data === "NULL")) - ); -} - -function constructYamlNull(): null { - return null; -} - -function isNull(object: unknown): object is null { - return object === null; -} - -export const nil = new Type("tag:yaml.org,2002:null", { - construct: constructYamlNull, - defaultStyle: "lowercase", - kind: "scalar", - predicate: isNull, - represent: { - canonical(): string { - return "~"; - }, - lowercase(): string { - return "null"; - }, - uppercase(): string { - return "NULL"; - }, - camelcase(): string { - return "Null"; - }, - }, - resolve: resolveYamlNull, -}); diff --git a/std/encoding/_yaml/type/omap.ts b/std/encoding/_yaml/type/omap.ts deleted file mode 100644 index 300debeb8a..0000000000 --- a/std/encoding/_yaml/type/omap.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Type } from "../type.ts"; -import type { Any } from "../utils.ts"; - -const _hasOwnProperty = Object.prototype.hasOwnProperty; -const _toString = Object.prototype.toString; - -function resolveYamlOmap(data: Any): boolean { - const objectKeys: string[] = []; - let pairKey = ""; - let pairHasKey = false; - - for (const pair of data) { - pairHasKey = false; - - if (_toString.call(pair) !== "[object Object]") return false; - - for (pairKey in pair) { - if (_hasOwnProperty.call(pair, pairKey)) { - if (!pairHasKey) pairHasKey = true; - else return false; - } - } - - if (!pairHasKey) return false; - - if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey); - else return false; - } - - return true; -} - -function constructYamlOmap(data: Any): Any { - return data !== null ? data : []; -} - -export const omap = new Type("tag:yaml.org,2002:omap", { - construct: constructYamlOmap, - kind: "sequence", - resolve: resolveYamlOmap, -}); diff --git a/std/encoding/_yaml/type/pairs.ts b/std/encoding/_yaml/type/pairs.ts deleted file mode 100644 index 81664655a0..0000000000 --- a/std/encoding/_yaml/type/pairs.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Type } from "../type.ts"; -import type { Any } from "../utils.ts"; - -const _toString = Object.prototype.toString; - -function resolveYamlPairs(data: Any[][]): boolean { - const result = new Array(data.length); - - for (let index = 0; index < data.length; index++) { - const pair = data[index]; - - if (_toString.call(pair) !== "[object Object]") return false; - - const keys = Object.keys(pair); - - if (keys.length !== 1) return false; - - result[index] = [keys[0], pair[keys[0] as Any]]; - } - - return true; -} - -function constructYamlPairs(data: string): Any[] { - if (data === null) return []; - - const result = new Array(data.length); - - for (let index = 0; index < data.length; index += 1) { - const pair = data[index]; - - const keys = Object.keys(pair); - - result[index] = [keys[0], pair[keys[0] as Any]]; - } - - return result; -} - -export const pairs = new Type("tag:yaml.org,2002:pairs", { - construct: constructYamlPairs, - kind: "sequence", - resolve: resolveYamlPairs, -}); diff --git a/std/encoding/_yaml/type/seq.ts b/std/encoding/_yaml/type/seq.ts deleted file mode 100644 index 9679cd5169..0000000000 --- a/std/encoding/_yaml/type/seq.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Type } from "../type.ts"; -import type { Any } from "../utils.ts"; - -export const seq = new Type("tag:yaml.org,2002:seq", { - construct(data): Any { - return data !== null ? data : []; - }, - kind: "sequence", -}); diff --git a/std/encoding/_yaml/type/set.ts b/std/encoding/_yaml/type/set.ts deleted file mode 100644 index 234951f4de..0000000000 --- a/std/encoding/_yaml/type/set.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Type } from "../type.ts"; -import type { Any } from "../utils.ts"; - -const _hasOwnProperty = Object.prototype.hasOwnProperty; - -function resolveYamlSet(data: Any): boolean { - if (data === null) return true; - - for (const key in data) { - if (_hasOwnProperty.call(data, key)) { - if (data[key] !== null) return false; - } - } - - return true; -} - -function constructYamlSet(data: string): Any { - return data !== null ? data : {}; -} - -export const set = new Type("tag:yaml.org,2002:set", { - construct: constructYamlSet, - kind: "mapping", - resolve: resolveYamlSet, -}); diff --git a/std/encoding/_yaml/type/str.ts b/std/encoding/_yaml/type/str.ts deleted file mode 100644 index 053f8b9f79..0000000000 --- a/std/encoding/_yaml/type/str.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Type } from "../type.ts"; - -export const str = new Type("tag:yaml.org,2002:str", { - construct(data): string { - return data !== null ? data : ""; - }, - kind: "scalar", -}); diff --git a/std/encoding/_yaml/type/timestamp.ts b/std/encoding/_yaml/type/timestamp.ts deleted file mode 100644 index 47a797488e..0000000000 --- a/std/encoding/_yaml/type/timestamp.ts +++ /dev/null @@ -1,96 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Type } from "../type.ts"; - -const YAML_DATE_REGEXP = new RegExp( - "^([0-9][0-9][0-9][0-9])" + // [1] year - "-([0-9][0-9])" + // [2] month - "-([0-9][0-9])$", // [3] day -); - -const YAML_TIMESTAMP_REGEXP = new RegExp( - "^([0-9][0-9][0-9][0-9])" + // [1] year - "-([0-9][0-9]?)" + // [2] month - "-([0-9][0-9]?)" + // [3] day - "(?:[Tt]|[ \\t]+)" + // ... - "([0-9][0-9]?)" + // [4] hour - ":([0-9][0-9])" + // [5] minute - ":([0-9][0-9])" + // [6] second - "(?:\\.([0-9]*))?" + // [7] fraction - "(?:[ \\t]*(Z|([-+])([0-9][0-9]?)" + // [8] tz [9] tz_sign [10] tz_hour - "(?::([0-9][0-9]))?))?$", // [11] tz_minute -); - -function resolveYamlTimestamp(data: string): boolean { - if (data === null) return false; - if (YAML_DATE_REGEXP.exec(data) !== null) return true; - if (YAML_TIMESTAMP_REGEXP.exec(data) !== null) return true; - return false; -} - -function constructYamlTimestamp(data: string): Date { - let match = YAML_DATE_REGEXP.exec(data); - if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(data); - - if (match === null) throw new Error("Date resolve error"); - - // match: [1] year [2] month [3] day - - const year = +match[1]; - const month = +match[2] - 1; // JS month starts with 0 - const day = +match[3]; - - if (!match[4]) { - // no hour - return new Date(Date.UTC(year, month, day)); - } - - // match: [4] hour [5] minute [6] second [7] fraction - - const hour = +match[4]; - const minute = +match[5]; - const second = +match[6]; - - let fraction = 0; - if (match[7]) { - let partFraction = match[7].slice(0, 3); - while (partFraction.length < 3) { - // milli-seconds - partFraction += "0"; - } - fraction = +partFraction; - } - - // match: [8] tz [9] tz_sign [10] tz_hour [11] tz_minute - - let delta = null; - if (match[9]) { - const tzHour = +match[10]; - const tzMinute = +(match[11] || 0); - delta = (tzHour * 60 + tzMinute) * 60000; // delta in milli-seconds - if (match[9] === "-") delta = -delta; - } - - const date = new Date( - Date.UTC(year, month, day, hour, minute, second, fraction), - ); - - if (delta) date.setTime(date.getTime() - delta); - - return date; -} - -function representYamlTimestamp(date: Date): string { - return date.toISOString(); -} - -export const timestamp = new Type("tag:yaml.org,2002:timestamp", { - construct: constructYamlTimestamp, - instanceOf: Date, - kind: "scalar", - represent: representYamlTimestamp, - resolve: resolveYamlTimestamp, -}); diff --git a/std/encoding/_yaml/utils.ts b/std/encoding/_yaml/utils.ts deleted file mode 100644 index cf18760de6..0000000000 --- a/std/encoding/_yaml/utils.ts +++ /dev/null @@ -1,80 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -// deno-lint-ignore no-explicit-any -export type Any = any; - -export function isNothing(subject: unknown): subject is never { - return typeof subject === "undefined" || subject === null; -} - -export function isArray(value: unknown): value is Any[] { - return Array.isArray(value); -} - -export function isBoolean(value: unknown): value is boolean { - return typeof value === "boolean" || value instanceof Boolean; -} - -export function isNull(value: unknown): value is null { - return value === null; -} - -export function isNumber(value: unknown): value is number { - return typeof value === "number" || value instanceof Number; -} - -export function isString(value: unknown): value is string { - return typeof value === "string" || value instanceof String; -} - -export function isSymbol(value: unknown): value is symbol { - return typeof value === "symbol"; -} - -export function isUndefined(value: unknown): value is undefined { - return value === undefined; -} - -export function isObject(value: unknown): value is Record { - return value !== null && typeof value === "object"; -} - -export function isError(e: unknown): boolean { - return e instanceof Error; -} - -export function isFunction(value: unknown): value is () => void { - return typeof value === "function"; -} - -export function isRegExp(value: unknown): value is RegExp { - return value instanceof RegExp; -} - -export function toArray(sequence: T): T | [] | [T] { - if (isArray(sequence)) return sequence; - if (isNothing(sequence)) return []; - - return [sequence]; -} - -export function repeat(str: string, count: number): string { - let result = ""; - - for (let cycle = 0; cycle < count; cycle++) { - result += str; - } - - return result; -} - -export function isNegativeZero(i: number): boolean { - return i === 0 && Number.NEGATIVE_INFINITY === 1 / i; -} - -export interface ArrayObject { - [P: string]: T; -} diff --git a/std/encoding/ascii85.ts b/std/encoding/ascii85.ts deleted file mode 100644 index 877174bfd4..0000000000 --- a/std/encoding/ascii85.ts +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. - -export type Ascii85Standard = "Adobe" | "btoa" | "RFC 1924" | "Z85"; -/** - * encoding/decoding options - * @property standard - characterset and delimiter (if supported and used). Defaults to Adobe - * @property delimiter - whether to use a delimiter (if supported) - "<~" and "~>" by default - */ -export interface Ascii85Options { - standard?: Ascii85Standard; - delimiter?: boolean; -} -const rfc1924 = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"; -const Z85 = - "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"; -/** - * Encodes a given Uint8Array into ascii85, supports multiple standards - * @param uint8 input to encode - * @param [options] encoding options - * @param [options.standard=Adobe] encoding standard (Adobe, btoa, RFC 1924 or Z85) - * @param [options.delimiter] whether to use a delimiter, if supported by encoding standard - */ -export function encode(uint8: Uint8Array, options?: Ascii85Options): string { - const standard = options?.standard ?? "Adobe"; - let output: string[] = [], - v: number, - n = 0, - difference = 0; - if (uint8.length % 4 !== 0) { - const tmp = uint8; - difference = 4 - (tmp.length % 4); - uint8 = new Uint8Array(tmp.length + difference); - uint8.set(tmp); - } - const view = new DataView(uint8.buffer); - for (let i = 0, len = uint8.length; i < len; i += 4) { - v = view.getUint32(i); - // Adobe and btoa standards compress 4 zeroes to single "z" character - if ( - (standard === "Adobe" || standard === "btoa") && - v === 0 && - i < len - difference - 3 - ) { - output[n++] = "z"; - continue; - } - // btoa compresses 4 spaces - that is, bytes equal to 32 - into single "y" character - if (standard === "btoa" && v === 538976288) { - output[n++] = "y"; - continue; - } - for (let j = 4; j >= 0; j--) { - output[n + j] = String.fromCharCode((v % 85) + 33); - v = Math.trunc(v / 85); - } - n += 5; - } - switch (standard) { - case "Adobe": - if (options?.delimiter) { - return `<~${output.slice(0, output.length - difference).join("")}~>`; - } - break; - case "btoa": - if (options?.delimiter) { - return `xbtoa Begin\n${ - output - .slice(0, output.length - difference) - .join("") - }\nxbtoa End`; - } - break; - case "RFC 1924": - output = output.map((val) => rfc1924[val.charCodeAt(0) - 33]); - break; - case "Z85": - output = output.map((val) => Z85[val.charCodeAt(0) - 33]); - break; - } - return output.slice(0, output.length - difference).join(""); -} -/** - * Decodes a given ascii85 encoded string. - * @param ascii85 input to decode - * @param [options] decoding options - * @param [options.standard=Adobe] encoding standard used in the input string (Adobe, btoa, RFC 1924 or Z85) - */ -export function decode(ascii85: string, options?: Ascii85Options): Uint8Array { - const encoding = options?.standard ?? "Adobe"; - // translate all encodings to most basic adobe/btoa one and decompress some special characters ("z" and "y") - switch (encoding) { - case "Adobe": - ascii85 = ascii85.replaceAll(/(<~|~>)/g, "").replaceAll("z", "!!!!!"); - break; - case "btoa": - ascii85 = ascii85 - .replaceAll(/(xbtoa Begin|xbtoa End|\n)/g, "") - .replaceAll("z", "!!!!!") - .replaceAll("y", "+ String.fromCharCode(rfc1924.indexOf(match) + 33), - ); - break; - case "Z85": - ascii85 = ascii85.replaceAll( - /./g, - (match) => String.fromCharCode(Z85.indexOf(match) + 33), - ); - break; - } - //remove all invalid characters - ascii85 = ascii85.replaceAll(/[^!-u]/g, ""); - const len = ascii85.length, - output = new Uint8Array(len + 4 - (len % 4)); - const view = new DataView(output.buffer); - let v = 0, - n = 0, - max = 0; - for (let i = 0; i < len;) { - for (max += 5; i < max; i++) { - v = v * 85 + (i < len ? ascii85.charCodeAt(i) : 117) - 33; - } - view.setUint32(n, v); - v = 0; - n += 4; - } - return output.slice(0, Math.trunc(len * 0.8)); -} diff --git a/std/encoding/ascii85_test.ts b/std/encoding/ascii85_test.ts deleted file mode 100644 index 8bf486b8ac..0000000000 --- a/std/encoding/ascii85_test.ts +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { Ascii85Standard, decode, encode } from "./ascii85.ts"; -type TestCases = Partial<{ [index in Ascii85Standard]: string[][] }>; -const utf8encoder = new TextEncoder(); -const testCasesNoDelimiter: TestCases = { - Adobe: [ - ["test", "FCfN8"], - ["ascii85", "@<5pmBfIs"], - ["Hello world!", "87cURD]j7BEbo80"], - //wikipedia example - [ - "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.", - "9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKFCj@.4Gp$d7F!,L7@<6@)/0JDEF@3BB/F*&OCAfu2/AKYi(DIb:@FD,*)+C]U=@3BN#EcYf8ATD3s@q?d$AftVqCh[NqF-FD5W8ARlolDIal(DIduD.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c", - ], - ["", ""], - ["\0", "!!"], - ["\0\0", "!!!"], - ["\0\0\0", "!!!!"], - //special Adobe and btoa test cases - 4 bytes equal to 0 should become a "z" - ["\0\0\0\0", "z"], - ["\0\0\0\0\0", "z!!"], - [" ", "+Cj@.4Gp$d7F!,L7@<6@)/0JDEF@3BB/F*&OCAfu2/AKYi(DIb:@FD,*)+C]U=@3BN#EcYf8ATD3s@q?d$AftVqCh[NqF-FD5W8ARlolDIal(DIduD.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c", - ], - ["", ""], - ["\0", "!!"], - ["\0\0", "!!!"], - ["\0\0\0", "!!!!"], - //special Adobe and btoa test cases - 4 bytes equal to 0 should become a "z" - ["\0\0\0\0", "z"], - ["\0\0\0\0\0", "z!!"], - //special btoa test case - 4 spaces should become "y" - [" ", "y"], - ], - "RFC 1924": [ - ["test", "bY*jN"], - ["ascii85", "VRK_?X*e|"], - ["Hello world!", "NM&qnZy%ZCX>)XGZfA9Ab7*B`EFf-gbRchTYDO_b1WctXlY|;AZc?TVIXXEb95kYW*~HEWgu;7Ze%PVbZB98AYyqSVIXj2a&u*NWpZI|V`U(3W*}r`Y-wj`bRcPNAarPDAY*TCbZKsNWn>^>Ze$>7Ze(RV>IZ)PBCZf|#NWn^b%EFfigV`XJzb0BnRWgv5CZ*p`Xc4cT~ZDnp_Wgu^6AYpEKAY);2ZeeU7aBO8^b9HiME&", - ], - ["", ""], - ["\0", "00"], - ["\0\0", "000"], - ["\0\0\0", "0000"], - ["\0\0\0\0", "00000"], - ["\0\0\0\0\0", "0000000"], - [" ", "ARr(h"], - ], - Z85: [ - ["test", "By/Jn"], - ["ascii85", "vrk{)x/E%"], - ["Hello world!", "nm=QNzY"], - ["ascii85", "<~@<5pmBfIs~>"], - ["Hello world!", "<~87cURD]j7BEbo80~>"], - //wikipedia example - [ - "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.", - "<~9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKFCj@.4Gp$d7F!,L7@<6@)/0JDEF@3BB/F*&OCAfu2/AKYi(DIb:@FD,*)+C]U=@3BN#EcYf8ATD3s@q?d$AftVqCh[NqF-FD5W8ARlolDIal(DIduD.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c~>", - ], - ["", "<~~>"], - ["\0", "<~!!~>"], - ["\0\0", "<~!!!~>"], - ["\0\0\0", "<~!!!!~>"], - //special Adobe and btoa test cases - 4 bytes equal to 0 should become a "z" - ["\0\0\0\0", "<~z~>"], - ["\0\0\0\0\0", "<~z!!~>"], - [" ", "<~+"], - ], - btoa: [ - ["test", "xbtoa Begin\nFCfN8\nxbtoa End"], - ["ascii85", "xbtoa Begin\n@<5pmBfIs\nxbtoa End"], - ["Hello world!", "xbtoa Begin\n87cURD]j7BEbo80\nxbtoa End"], - //wikipedia example - [ - "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.", - "xbtoa Begin\n9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKFCj@.4Gp$d7F!,L7@<6@)/0JDEF@3BB/F*&OCAfu2/AKYi(DIb:@FD,*)+C]U=@3BN#EcYf8ATD3s@q?d$AftVqCh[NqF-FD5W8ARlolDIal(DIduD.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c\nxbtoa End", - ], - ["", "xbtoa Begin\n\nxbtoa End"], - ["\0", "xbtoa Begin\n!!\nxbtoa End"], - ["\0\0", "xbtoa Begin\n!!!\nxbtoa End"], - ["\0\0\0", "xbtoa Begin\n!!!!\nxbtoa End"], - //special Adobe and btoa test cases - 4 bytes equal to 0 should become a "z" - ["\0\0\0\0", "xbtoa Begin\nz\nxbtoa End"], - ["\0\0\0\0\0", "xbtoa Begin\nz!!\nxbtoa End"], - //special btoa test case - 4 spaces should become "y" - [" ", "xbtoa Begin\ny\nxbtoa End"], - ], -}; - -for (const [standard, tests] of Object.entries(testCasesNoDelimiter)) { - if (tests === undefined) continue; - Deno.test({ - name: `[encoding/ascii85] encode ${standard}`, - fn(): void { - for (const [bin, b85] of tests) { - assertEquals( - encode(utf8encoder.encode(bin), { - standard: standard as Ascii85Standard, - }), - b85, - ); - } - }, - }); - - Deno.test({ - name: `[encoding/ascii85] decode ${standard}`, - fn(): void { - for (const [bin, b85] of tests) { - assertEquals( - decode(b85, { standard: standard as Ascii85Standard }), - utf8encoder.encode(bin), - ); - } - }, - }); -} -for (const [standard, tests] of Object.entries(testCasesDelimiter)) { - if (tests === undefined) continue; - Deno.test({ - name: `[encoding/ascii85] encode ${standard} with delimiter`, - fn(): void { - for (const [bin, b85] of tests) { - assertEquals( - encode(utf8encoder.encode(bin), { - standard: standard as Ascii85Standard, - delimiter: true, - }), - b85, - ); - } - }, - }); - - Deno.test({ - name: `[encoding/ascii85] decode ${standard} with delimiter`, - fn(): void { - for (const [bin, b85] of tests) { - assertEquals( - decode(b85, { - standard: standard as Ascii85Standard, - delimiter: true, - }), - utf8encoder.encode(bin), - ); - } - }, - }); -} diff --git a/std/encoding/base32.ts b/std/encoding/base32.ts deleted file mode 100644 index 7d0ec81f36..0000000000 --- a/std/encoding/base32.ts +++ /dev/null @@ -1,207 +0,0 @@ -// Modified from https://github.com/beatgammit/base64-js -// Copyright (c) 2014 Jameson Little. MIT License. - -const lookup: string[] = []; -const revLookup: number[] = []; - -// RFC4648 base32 -const code = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; -for (let i = 0, len = code.length; i < len; ++i) { - lookup[i] = code[i]; - revLookup[code.charCodeAt(i)] = i; -} - -const placeHolderPadLookup = [0, 1, , 2, 3, , 4]; -function _getPadLen(placeHoldersLen: number): number { - const maybeLen = placeHolderPadLookup[placeHoldersLen]; - if (typeof maybeLen !== "number") { - throw new Error("Invalid pad length"); - } - return maybeLen; -} - -function getLens(b32: string): [number, number] { - const len = b32.length; - - if (len % 8 > 0) { - throw new Error("Invalid string. Length must be a multiple of 8"); - } - - let validLen = b32.indexOf("="); - if (validLen === -1) validLen = len; - - const placeHoldersLen = validLen === len ? 0 : 8 - (validLen % 8); - - return [validLen, placeHoldersLen]; -} - -/** - * Returns number of bytes encoded in the given RFC4648 base32 string input. - * @param b32 - */ -export function byteLength(b32: string): number { - const [validLen, placeHoldersLen] = getLens(b32); - return _byteLength(validLen, placeHoldersLen); -} - -function _byteLength(validLen: number, placeHoldersLen: number): number { - return ((validLen + placeHoldersLen) * 5) / 8 - _getPadLen(placeHoldersLen); -} - -/** - * Decodes a given RFC4648 base32 encoded string. - * @param b32 - */ -export function decode(b32: string): Uint8Array { - let tmp: number; - const [validLen, placeHoldersLen] = getLens(b32); - - const arr = new Uint8Array(_byteLength(validLen, placeHoldersLen)); - - let curByte = 0; - - // if there are placeholders, only get up to the last complete 8 chars - const len = placeHoldersLen > 0 ? validLen - 8 : validLen; - - let i: number; - for (i = 0; i < len; i += 8) { - tmp = (revLookup[b32.charCodeAt(i)] << 20) | - (revLookup[b32.charCodeAt(i + 1)] << 15) | - (revLookup[b32.charCodeAt(i + 2)] << 10) | - (revLookup[b32.charCodeAt(i + 3)] << 5) | - revLookup[b32.charCodeAt(i + 4)]; - arr[curByte++] = (tmp >> 17) & 0xff; - arr[curByte++] = (tmp >> 9) & 0xff; - arr[curByte++] = (tmp >> 1) & 0xff; - - tmp = ((tmp & 1) << 15) | - (revLookup[b32.charCodeAt(i + 5)] << 10) | - (revLookup[b32.charCodeAt(i + 6)] << 5) | - revLookup[b32.charCodeAt(i + 7)]; - arr[curByte++] = (tmp >> 8) & 0xff; - arr[curByte++] = tmp & 0xff; - } - - if (placeHoldersLen === 1) { - tmp = (revLookup[b32.charCodeAt(i)] << 20) | - (revLookup[b32.charCodeAt(i + 1)] << 15) | - (revLookup[b32.charCodeAt(i + 2)] << 10) | - (revLookup[b32.charCodeAt(i + 3)] << 5) | - revLookup[b32.charCodeAt(i + 4)]; - arr[curByte++] = (tmp >> 17) & 0xff; - arr[curByte++] = (tmp >> 9) & 0xff; - arr[curByte++] = (tmp >> 1) & 0xff; - tmp = ((tmp & 1) << 7) | - (revLookup[b32.charCodeAt(i + 5)] << 2) | - (revLookup[b32.charCodeAt(i + 6)] >> 3); - arr[curByte++] = tmp & 0xff; - } else if (placeHoldersLen === 3) { - tmp = (revLookup[b32.charCodeAt(i)] << 19) | - (revLookup[b32.charCodeAt(i + 1)] << 14) | - (revLookup[b32.charCodeAt(i + 2)] << 9) | - (revLookup[b32.charCodeAt(i + 3)] << 4) | - (revLookup[b32.charCodeAt(i + 4)] >> 1); - arr[curByte++] = (tmp >> 16) & 0xff; - arr[curByte++] = (tmp >> 8) & 0xff; - arr[curByte++] = tmp & 0xff; - } else if (placeHoldersLen === 4) { - tmp = (revLookup[b32.charCodeAt(i)] << 11) | - (revLookup[b32.charCodeAt(i + 1)] << 6) | - (revLookup[b32.charCodeAt(i + 2)] << 1) | - (revLookup[b32.charCodeAt(i + 3)] >> 4); - arr[curByte++] = (tmp >> 8) & 0xff; - arr[curByte++] = tmp & 0xff; - } else if (placeHoldersLen === 6) { - tmp = (revLookup[b32.charCodeAt(i)] << 3) | - (revLookup[b32.charCodeAt(i + 1)] >> 2); - arr[curByte++] = tmp & 0xff; - } - - return arr; -} - -function encodeChunk(uint8: Uint8Array, start: number, end: number): string { - let tmp: number; - const output = []; - for (let i = start; i < end; i += 5) { - tmp = ((uint8[i] << 16) & 0xff0000) | - ((uint8[i + 1] << 8) & 0xff00) | - (uint8[i + 2] & 0xff); - output.push(lookup[(tmp >> 19) & 0x1f]); - output.push(lookup[(tmp >> 14) & 0x1f]); - output.push(lookup[(tmp >> 9) & 0x1f]); - output.push(lookup[(tmp >> 4) & 0x1f]); - tmp = ((tmp & 0xf) << 16) | - ((uint8[i + 3] << 8) & 0xff00) | - (uint8[i + 4] & 0xff); - output.push(lookup[(tmp >> 15) & 0x1f]); - output.push(lookup[(tmp >> 10) & 0x1f]); - output.push(lookup[(tmp >> 5) & 0x1f]); - output.push(lookup[tmp & 0x1f]); - } - return output.join(""); -} - -/** - * Encodes a given Uint8Array into RFC4648 base32 representation - * @param uint8 - */ -export function encode(uint8: Uint8Array): string { - let tmp: number; - const len = uint8.length; - const extraBytes = len % 5; - const parts = []; - const maxChunkLength = 16385; // must be multiple of 5 - const len2 = len - extraBytes; - - // go through the array every 5 bytes, we'll deal with trailing stuff later - for (let i = 0; i < len2; i += maxChunkLength) { - parts.push( - encodeChunk( - uint8, - i, - i + maxChunkLength > len2 ? len2 : i + maxChunkLength, - ), - ); - } - - // pad the end with zeros, but make sure to not forget the extra bytes - if (extraBytes === 4) { - tmp = ((uint8[len2] & 0xff) << 16) | - ((uint8[len2 + 1] & 0xff) << 8) | - (uint8[len2 + 2] & 0xff); - parts.push(lookup[(tmp >> 19) & 0x1f]); - parts.push(lookup[(tmp >> 14) & 0x1f]); - parts.push(lookup[(tmp >> 9) & 0x1f]); - parts.push(lookup[(tmp >> 4) & 0x1f]); - tmp = ((tmp & 0xf) << 11) | (uint8[len2 + 3] << 3); - parts.push(lookup[(tmp >> 10) & 0x1f]); - parts.push(lookup[(tmp >> 5) & 0x1f]); - parts.push(lookup[tmp & 0x1f]); - parts.push("="); - } else if (extraBytes === 3) { - tmp = ((uint8[len2] & 0xff) << 17) | - ((uint8[len2 + 1] & 0xff) << 9) | - ((uint8[len2 + 2] & 0xff) << 1); - parts.push(lookup[(tmp >> 20) & 0x1f]); - parts.push(lookup[(tmp >> 15) & 0x1f]); - parts.push(lookup[(tmp >> 10) & 0x1f]); - parts.push(lookup[(tmp >> 5) & 0x1f]); - parts.push(lookup[tmp & 0x1f]); - parts.push("==="); - } else if (extraBytes === 2) { - tmp = ((uint8[len2] & 0xff) << 12) | ((uint8[len2 + 1] & 0xff) << 4); - parts.push(lookup[(tmp >> 15) & 0x1f]); - parts.push(lookup[(tmp >> 10) & 0x1f]); - parts.push(lookup[(tmp >> 5) & 0x1f]); - parts.push(lookup[tmp & 0x1f]); - parts.push("===="); - } else if (extraBytes === 1) { - tmp = (uint8[len2] & 0xff) << 2; - parts.push(lookup[(tmp >> 5) & 0x1f]); - parts.push(lookup[tmp & 0x1f]); - parts.push("======"); - } - - return parts.join(""); -} diff --git a/std/encoding/base32_test.ts b/std/encoding/base32_test.ts deleted file mode 100644 index 1c497182fb..0000000000 --- a/std/encoding/base32_test.ts +++ /dev/null @@ -1,135 +0,0 @@ -// Test cases copied from https://github.com/LinusU/base32-encode/blob/master/test.js -// Copyright (c) 2016-2017 Linus Unnebäck. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "../testing/asserts.ts"; -import { decode, encode } from "./base32.ts"; - -// Lifted from https://stackoverflow.com/questions/38987784 -const fromHexString = (hexString: string): Uint8Array => - new Uint8Array(hexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))); -const toHexString = (bytes: Uint8Array): string => - bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), ""); - -const testCases = [ - ["73", "OM======"], - ["f80c", "7AGA===="], - ["6450", "MRIA===="], - ["cc91d0", "ZSI5A==="], - ["6c60c0", "NRQMA==="], - ["4f6a23", "J5VCG==="], - ["88b44f18", "RC2E6GA="], - ["90bad04714", "SC5NARYU"], - ["e9ef1def8086", "5HXR334AQY======"], - ["83fe3f9c1e9302", "QP7D7HA6SMBA===="], - ["15aa1f7cafc17cb8", "CWVB67FPYF6LQ==="], - ["da51d4fed48b4c32dc", "3JI5J7WURNGDFXA="], - ["c4be14228512d7299831", "YS7BIIUFCLLSTGBR"], - ["2f273c5b5ef04724fab944", "F4TTYW266BDSJ6VZIQ======"], - ["969da1b80ec2442d2bdd4bdb", "S2O2DOAOYJCC2K65JPNQ===="], - ["31f5adb50792f549d3714f3f99", "GH223NIHSL2UTU3RJ47ZS==="], - ["6a654f7a072c29951930700c0a61", "NJSU66QHFQUZKGJQOAGAUYI="], - ["0fe29d6825ad999e87d9b7cac3589d", "B7RJ22BFVWMZ5B6ZW7FMGWE5"], - ["0f960ab44e165973a5172ccd294b3412", "B6LAVNCOCZMXHJIXFTGSSSZUCI======"], - ["325b9fd847a41fb0d485c207a1a5b02dcf", "GJNZ7WCHUQP3BVEFYID2DJNQFXHQ===="], - ["ddf80ebe21bf1b1e12a64c5cc6a74b5d92dd", "3X4A5PRBX4NR4EVGJROMNJ2LLWJN2==="], - [ - "c0cae52c6f641ce04a7ee5b9a8fa8ded121bca", - "YDFOKLDPMQOOAST64W42R6UN5UJBXSQ=", - ], - [ - "872840a355c8c70586f462c9e669ee760cb3537e", - "Q4UEBI2VZDDQLBXUMLE6M2POOYGLGU36", - ], - [ - "5773fe22662818a120c5688824c935fe018208a496", - "K5Z74ITGFAMKCIGFNCECJSJV7YAYECFESY======", - ], - [ - "416e23abc524d1b85736e2bea6cfecd5192789034a28", - "IFXCHK6FETI3QVZW4K7KNT7M2UMSPCIDJIUA====", - ], - [ - "83d2386ebdd7e8e818ec00e3ccd882aa933b905b7e2e44", - "QPJDQ3V527UOQGHMADR4ZWECVKJTXEC3PYXEI===", - ], - [ - "a2fa8b881f3b8024f52745763c4ae08ea12bdf8bef1a72f8", - "UL5IXCA7HOACJ5JHIV3DYSXAR2QSXX4L54NHF6A=", - ], - [ - "b074ae8b9efde0f17f37bccadde006d039997b59c8efb05add", - "WB2K5C467XQPC7ZXXTFN3YAG2A4ZS62ZZDX3AWW5", - ], - [ - "764fef941aee7e416dc204ae5ab9c5b9ce644567798e6849aea9", - "OZH67FA25Z7EC3OCASXFVOOFXHHGIRLHPGHGQSNOVE======", - ], - [ - "4995d9811f37f59797d7c3b9b9e5325aa78277415f70f4accf588c", - "JGK5TAI7G72ZPF6XYO43TZJSLKTYE52BL5YPJLGPLCGA====", - ], - [ - "24f0812ca8eed58374c11a7008f0b262698b72fd2792709208eaacb2", - "ETYICLFI53KYG5GBDJYAR4FSMJUYW4X5E6JHBEQI5KWLE===", - ], - [ - "d70692543810d4bf50d81cf44a55801a557a388a341367c7ea077ca306", - "24DJEVBYCDKL6UGYDT2EUVMADJKXUOEKGQJWPR7KA56KGBQ=", - ], - [ - "6e08a89ca36b677ff8fe99e68a1241c8d8cef2570a5f60b6417d2538b30c", - "NYEKRHFDNNTX76H6THTIUESBZDMM54SXBJPWBNSBPUSTRMYM", - ], - [ - "f2fc2319bd29457ccd01e8e194ee9bd7e97298b6610df4ab0f3d5baa0b2d7ccf69829edb74edef", - "6L6CGGN5FFCXZTIB5DQZJ3U327UXFGFWMEG7JKYPHVN2UCZNPTHWTAU63N2O33Y=", - ], -]; - -Deno.test({ - name: "[encoding.base32] encode", - fn(): void { - for (const [bin, b32] of testCases) { - assertEquals(encode(fromHexString(bin)), b32); - } - }, -}); - -Deno.test({ - name: "[encoding.base32] decode", - fn(): void { - for (const [bin, b32] of testCases) { - assertEquals(toHexString(decode(b32)), bin); - } - }, -}); - -Deno.test({ - name: "[encoding.base32] decode bad length", - fn(): void { - let errorCaught = false; - try { - decode("OOOO=="); - } catch (e) { - assert( - e.message.includes("Invalid string. Length must be a multiple of 8"), - ); - errorCaught = true; - } - assert(errorCaught); - }, -}); - -Deno.test({ - name: "[encoding.base32] decode bad padding", - fn(): void { - let errorCaught = false; - try { - decode("OOOOOO=="); - } catch (e) { - assert(e.message.includes("Invalid pad length")); - errorCaught = true; - } - assert(errorCaught); - }, -}); diff --git a/std/encoding/base64.ts b/std/encoding/base64.ts deleted file mode 100644 index c98030b7f7..0000000000 --- a/std/encoding/base64.ts +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -// deno-fmt-ignore -const base64abc = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", - "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", - "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", - "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", - "5", "6", "7", "8", "9", "+", "/"]; - -/** - * CREDIT: https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727 - * Encodes a given Uint8Array, ArrayBuffer or string into RFC4648 base64 representation - * @param data - */ -export function encode(data: ArrayBuffer | string): string { - const uint8 = - typeof data === "string" - ? new TextEncoder().encode(data) - : data instanceof Uint8Array - ? data - : new Uint8Array(data); - let result = "", - i; - const l = uint8.length; - for (i = 2; i < l; i += 3) { - result += base64abc[uint8[i - 2] >> 2]; - result += base64abc[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)]; - result += base64abc[((uint8[i - 1] & 0x0f) << 2) | (uint8[i] >> 6)]; - result += base64abc[uint8[i] & 0x3f]; - } - if (i === l + 1) { - // 1 octet yet to write - result += base64abc[uint8[i - 2] >> 2]; - result += base64abc[(uint8[i - 2] & 0x03) << 4]; - result += "=="; - } - if (i === l) { - // 2 octets yet to write - result += base64abc[uint8[i - 2] >> 2]; - result += base64abc[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)]; - result += base64abc[(uint8[i - 1] & 0x0f) << 2]; - result += "="; - } - return result; -} - -/** - * Decodes a given RFC4648 base64 encoded string - * @param b64 - */ -export function decode(b64: string): Uint8Array { - const binString = atob(b64); - const size = binString.length; - const bytes = new Uint8Array(size); - for (let i = 0; i < size; i++) { - bytes[i] = binString.charCodeAt(i); - } - return bytes; -} diff --git a/std/encoding/base64_test.ts b/std/encoding/base64_test.ts deleted file mode 100644 index 829deb6880..0000000000 --- a/std/encoding/base64_test.ts +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { assertEquals } from "../testing/asserts.ts"; -import { decode, encode } from "./base64.ts"; - -const testsetString = [ - ["", ""], - ["ß", "w58="], - ["f", "Zg=="], - ["fo", "Zm8="], - ["foo", "Zm9v"], - ["foob", "Zm9vYg=="], - ["fooba", "Zm9vYmE="], - ["foobar", "Zm9vYmFy"], -]; - -const testsetBinary = testsetString.map(([str, b64]) => [ - new TextEncoder().encode(str), - b64, -]) as Array<[Uint8Array, string]>; - -Deno.test("[encoding/base64] testBase64EncodeString", () => { - for (const [input, output] of testsetString) { - assertEquals(encode(input), output); - } -}); - -Deno.test("[encoding/base64] testBase64EncodeBinary", () => { - for (const [input, output] of testsetBinary) { - assertEquals(encode(input), output); - } -}); - -Deno.test("[encoding/base64] testBase64EncodeBinaryBuffer", () => { - for (const [input, output] of testsetBinary) { - assertEquals(encode(input.buffer), output); - } -}); - -Deno.test("[encoding/base64] testBase64DecodeBinary", () => { - for (const [input, output] of testsetBinary) { - const outputBinary = decode(output); - assertEquals(outputBinary, input); - } -}); diff --git a/std/encoding/base64url.ts b/std/encoding/base64url.ts deleted file mode 100644 index 45de1bd6d6..0000000000 --- a/std/encoding/base64url.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import * as base64 from "./base64.ts"; - -/* - * Some variants allow or require omitting the padding '=' signs: - * https://en.wikipedia.org/wiki/Base64#URL_applications - * @param base64url - */ -export function addPaddingToBase64url(base64url: string): string { - if (base64url.length % 4 === 2) return base64url + "=="; - if (base64url.length % 4 === 3) return base64url + "="; - if (base64url.length % 4 === 1) { - throw new TypeError("Illegal base64url string!"); - } - return base64url; -} - -function convertBase64urlToBase64(b64url: string): string { - return addPaddingToBase64url(b64url).replace(/\-/g, "+").replace(/_/g, "/"); -} - -function convertBase64ToBase64url(b64: string): string { - return b64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); -} - -/** - * Encodes a given Uint8Array into a base64url representation - * @param uint8 - */ -export function encode(uint8: Uint8Array): string { - return convertBase64ToBase64url(base64.encode(uint8)); -} - -/** - * Converts given base64url encoded data back to original - * @param b64url - */ -export function decode(b64url: string): Uint8Array { - return base64.decode(convertBase64urlToBase64(b64url)); -} diff --git a/std/encoding/base64url_test.ts b/std/encoding/base64url_test.ts deleted file mode 100644 index 3a41c5f641..0000000000 --- a/std/encoding/base64url_test.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { assertEquals } from "../testing/asserts.ts"; -import { decode, encode } from "./base64url.ts"; - -const testsetString = [ - ["", ""], - ["ß", "w58"], - ["f", "Zg"], - ["fo", "Zm8"], - ["foo", "Zm9v"], - ["foob", "Zm9vYg"], - ["fooba", "Zm9vYmE"], - ["foobar", "Zm9vYmFy"], - [">?>d?ß", "Pj8-ZD_Dnw"], -]; - -const testsetBinary = testsetString.map(([str, b64]) => [ - new TextEncoder().encode(str), - b64, -]) as Array<[Uint8Array, string]>; - -Deno.test("[encoding/base64url] testBase64urlEncodeBinary", () => { - for (const [input, output] of testsetBinary) { - assertEquals(encode(input), output); - } -}); - -Deno.test("[decoding/base64url] testBase64urlDecodeBinary", () => { - for (const [input, output] of testsetBinary) { - assertEquals(decode(output), input); - } -}); diff --git a/std/encoding/binary.ts b/std/encoding/binary.ts deleted file mode 100644 index f62b3b7a46..0000000000 --- a/std/encoding/binary.ts +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -type RawBaseType = "int8" | "int16" | "int32" | "uint8" | "uint16" | "uint32"; -type RawNumberType = RawBaseType | "float32" | "float64"; -type RawBigType = RawBaseType | "int64" | "uint64"; -export type DataType = RawNumberType | RawBigType; - -/** How encoded binary data is ordered. */ -export type Endianness = "little" | "big"; - -/** Options for working with the `number` type. */ -export interface VarnumOptions { - /** The binary format used. */ - dataType?: RawNumberType; - /** The binary encoding order used. */ - endian?: Endianness; -} - -/** Options for working with the `bigint` type. */ -export interface VarbigOptions { - /** The binary format used. */ - dataType?: RawBigType; - /** The binary encoding order used. */ - endian?: Endianness; -} - -const rawTypeSizes: Record = { - int8: 1, - uint8: 1, - int16: 2, - uint16: 2, - int32: 4, - uint32: 4, - int64: 8, - uint64: 8, - float32: 4, - float64: 8, -} as const; - -/** Number of bytes required to store `dataType`. */ -export function sizeof(dataType: DataType): number { - return rawTypeSizes[dataType]; -} - -/** Reads `n` bytes from `r`. - * - * Resolves it in a `Uint8Array`, or throws `Deno.errors.UnexpectedEof` if `n` bytes cannot be read. */ -export async function getNBytes( - r: Deno.Reader, - n: number, -): Promise { - const scratch = new Uint8Array(n); - const nRead = await r.read(scratch); - if (nRead === null || nRead < n) throw new Deno.errors.UnexpectedEof(); - return scratch; -} - -/** Decodes a number from `b`. If `o.bytes` is shorter than `sizeof(o.dataType)`, returns `null`. - * - * `o.dataType` defaults to `"int32"`. */ -export function varnum(b: Uint8Array, o: VarnumOptions = {}): number | null { - o.dataType = o.dataType ?? "int32"; - const littleEndian = (o.endian ?? "big") === "little" ? true : false; - if (b.length < sizeof(o.dataType)) return null; - const view = new DataView(b.buffer); - switch (o.dataType) { - case "int8": - return view.getInt8(0); - case "uint8": - return view.getUint8(0); - case "int16": - return view.getInt16(0, littleEndian); - case "uint16": - return view.getUint16(0, littleEndian); - case "int32": - return view.getInt32(0, littleEndian); - case "uint32": - return view.getUint32(0, littleEndian); - case "float32": - return view.getFloat32(0, littleEndian); - case "float64": - return view.getFloat64(0, littleEndian); - } -} - -/** Decodes a bigint from `b`. If `o.bytes` is shorter than `sizeof(o.dataType)`, returns `null`. - * - * `o.dataType` defaults to `"int64"`. */ -export function varbig(b: Uint8Array, o: VarbigOptions = {}): bigint | null { - o.dataType = o.dataType ?? "int64"; - const littleEndian = (o.endian ?? "big") === "little" ? true : false; - if (b.length < sizeof(o.dataType)) return null; - const view = new DataView(b.buffer); - switch (o.dataType) { - case "int8": - return BigInt(view.getInt8(0)); - case "uint8": - return BigInt(view.getUint8(0)); - case "int16": - return BigInt(view.getInt16(0, littleEndian)); - case "uint16": - return BigInt(view.getUint16(0, littleEndian)); - case "int32": - return BigInt(view.getInt32(0, littleEndian)); - case "uint32": - return BigInt(view.getUint32(0, littleEndian)); - case "int64": - return view.getBigInt64(0, littleEndian); - case "uint64": - return view.getBigUint64(0, littleEndian); - } -} - -/** Encodes number `x` into `b`. Returns the number of bytes used, or `0` if `b` is shorter than `sizeof(o.dataType)`. - * - * `o.dataType` defaults to `"int32"`. */ -export function putVarnum( - b: Uint8Array, - x: number, - o: VarnumOptions = {}, -): number { - o.dataType = o.dataType ?? "int32"; - const littleEndian = (o.endian ?? "big") === "little" ? true : false; - if (b.length < sizeof(o.dataType)) return 0; - const view = new DataView(b.buffer); - switch (o.dataType) { - case "int8": - view.setInt8(0, x); - break; - case "uint8": - view.setUint8(0, x); - break; - case "int16": - view.setInt16(0, x, littleEndian); - break; - case "uint16": - view.setUint16(0, x, littleEndian); - break; - case "int32": - view.setInt32(0, x, littleEndian); - break; - case "uint32": - view.setUint32(0, x, littleEndian); - break; - case "float32": - view.setFloat32(0, x, littleEndian); - break; - case "float64": - view.setFloat64(0, x, littleEndian); - break; - } - return sizeof(o.dataType); -} - -/** Encodes bigint `x` into `b`. Returns the number of bytes used, or `0` if `b` is shorter than `sizeof(o.dataType)`. - * - * `o.dataType` defaults to `"int64"`. */ -export function putVarbig( - b: Uint8Array, - x: bigint, - o: VarbigOptions = {}, -): number { - o.dataType = o.dataType ?? "int64"; - const littleEndian = (o.endian ?? "big") === "little" ? true : false; - if (b.length < sizeof(o.dataType)) return 0; - const view = new DataView(b.buffer); - switch (o.dataType) { - case "int8": - view.setInt8(0, Number(x)); - break; - case "uint8": - view.setUint8(0, Number(x)); - break; - case "int16": - view.setInt16(0, Number(x), littleEndian); - break; - case "uint16": - view.setUint16(0, Number(x), littleEndian); - break; - case "int32": - view.setInt32(0, Number(x), littleEndian); - break; - case "uint32": - view.setUint32(0, Number(x), littleEndian); - break; - case "int64": - view.setBigInt64(0, x, littleEndian); - break; - case "uint64": - view.setBigUint64(0, x, littleEndian); - break; - } - return sizeof(o.dataType); -} - -/** Decodes a number from `r`, consuming `sizeof(o.dataType)` bytes. If less than `sizeof(o.dataType)` bytes were read, throws `Deno.errors.unexpectedEof`. - * - * `o.dataType` defaults to `"int32"`. */ -export async function readVarnum( - r: Deno.Reader, - o: VarnumOptions = {}, -): Promise { - o.dataType = o.dataType ?? "int32"; - const scratch = await getNBytes(r, sizeof(o.dataType)); - return varnum(scratch, o) as number; -} - -/** Decodes a bigint from `r`, consuming `sizeof(o.dataType)` bytes. If less than `sizeof(o.dataType)` bytes were read, throws `Deno.errors.unexpectedEof`. - * - * `o.dataType` defaults to `"int64"`. */ -export async function readVarbig( - r: Deno.Reader, - o: VarbigOptions = {}, -): Promise { - o.dataType = o.dataType ?? "int64"; - const scratch = await getNBytes(r, sizeof(o.dataType)); - return varbig(scratch, o) as bigint; -} - -/** Encodes and writes `x` to `w`. Resolves to the number of bytes written. - * - * `o.dataType` defaults to `"int32"`. */ -export function writeVarnum( - w: Deno.Writer, - x: number, - o: VarnumOptions = {}, -): Promise { - o.dataType = o.dataType ?? "int32"; - const scratch = new Uint8Array(sizeof(o.dataType)); - putVarnum(scratch, x, o); - return w.write(scratch); -} - -/** Encodes and writes `x` to `w`. Resolves to the number of bytes written. - * - * `o.dataType` defaults to `"int64"`. */ -export function writeVarbig( - w: Deno.Writer, - x: bigint, - o: VarbigOptions = {}, -): Promise { - o.dataType = o.dataType ?? "int64"; - const scratch = new Uint8Array(sizeof(o.dataType)); - putVarbig(scratch, x, o); - return w.write(scratch); -} - -/** Encodes `x` into a new `Uint8Array`. - * - * `o.dataType` defaults to `"int32"` */ -export function varnumBytes(x: number, o: VarnumOptions = {}): Uint8Array { - o.dataType = o.dataType ?? "int32"; - const b = new Uint8Array(sizeof(o.dataType)); - putVarnum(b, x, o); - return b; -} - -/** Encodes `x` into a new `Uint8Array`. - * - * `o.dataType` defaults to `"int64"` */ -export function varbigBytes(x: bigint, o: VarbigOptions = {}): Uint8Array { - o.dataType = o.dataType ?? "int64"; - const b = new Uint8Array(sizeof(o.dataType)); - putVarbig(b, x, o); - return b; -} diff --git a/std/encoding/binary_test.ts b/std/encoding/binary_test.ts deleted file mode 100644 index 5688b84a2d..0000000000 --- a/std/encoding/binary_test.ts +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { assertEquals, assertThrowsAsync } from "../testing/asserts.ts"; -import { - getNBytes, - putVarbig, - putVarnum, - readVarbig, - readVarnum, - sizeof, - varbig, - varbigBytes, - varnum, - varnumBytes, - writeVarbig, - writeVarnum, -} from "./binary.ts"; - -Deno.test("testGetNBytes", async function (): Promise { - const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); - const buff = new Deno.Buffer(data.buffer); - const rslt = await getNBytes(buff, 8); - assertEquals(rslt, data); -}); - -Deno.test("testGetNBytesThrows", async function (): Promise { - const data = new Uint8Array([1, 2, 3, 4]); - const buff = new Deno.Buffer(data.buffer); - await assertThrowsAsync(async () => { - await getNBytes(buff, 8); - }, Deno.errors.UnexpectedEof); -}); - -Deno.test("testPutVarbig", function (): void { - const buff = new Uint8Array(8); - putVarbig(buff, 0xffeeddccbbaa9988n); - assertEquals( - buff, - new Uint8Array([0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88]), - ); -}); - -Deno.test("testPutVarbigLittleEndian", function (): void { - const buff = new Uint8Array(8); - putVarbig(buff, 0x8899aabbccddeeffn, { endian: "little" }); - assertEquals( - buff, - new Uint8Array([0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88]), - ); -}); - -Deno.test("testPutVarnum", function (): void { - const buff = new Uint8Array(4); - putVarnum(buff, 0xffeeddcc); - assertEquals(buff, new Uint8Array([0xff, 0xee, 0xdd, 0xcc])); -}); - -Deno.test("testPutVarnumLittleEndian", function (): void { - const buff = new Uint8Array(4); - putVarnum(buff, 0xccddeeff, { endian: "little" }); - assertEquals(buff, new Uint8Array([0xff, 0xee, 0xdd, 0xcc])); -}); - -Deno.test("testReadVarbig", async function (): Promise { - const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); - const buff = new Deno.Buffer(data.buffer); - const rslt = await readVarbig(buff); - assertEquals(rslt, 0x0102030405060708n); -}); - -Deno.test("testReadVarbigLittleEndian", async function (): Promise { - const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); - const buff = new Deno.Buffer(data.buffer); - const rslt = await readVarbig(buff, { endian: "little" }); - assertEquals(rslt, 0x0807060504030201n); -}); - -Deno.test("testReadVarnum", async function (): Promise { - const data = new Uint8Array([1, 2, 3, 4]); - const buff = new Deno.Buffer(data.buffer); - const rslt = await readVarnum(buff); - assertEquals(rslt, 0x01020304); -}); - -Deno.test("testReadVarnumLittleEndian", async function (): Promise { - const data = new Uint8Array([1, 2, 3, 4]); - const buff = new Deno.Buffer(data.buffer); - const rslt = await readVarnum(buff, { endian: "little" }); - assertEquals(rslt, 0x04030201); -}); - -Deno.test("testSizeof", function (): void { - assertEquals(1, sizeof("int8")); - assertEquals(1, sizeof("uint8")); - assertEquals(2, sizeof("int16")); - assertEquals(2, sizeof("uint16")); - assertEquals(4, sizeof("int32")); - assertEquals(4, sizeof("uint32")); - assertEquals(8, sizeof("int64")); - assertEquals(8, sizeof("uint64")); - assertEquals(4, sizeof("float32")); - assertEquals(8, sizeof("float64")); -}); - -Deno.test("testVarbig", function (): void { - const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); - const rslt = varbig(data); - assertEquals(rslt, 0x0102030405060708n); -}); - -Deno.test("testVarbigLittleEndian", function (): void { - const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); - const rslt = varbig(data, { endian: "little" }); - assertEquals(rslt, 0x0807060504030201n); -}); - -Deno.test("testVarnum", function (): void { - const data = new Uint8Array([1, 2, 3, 4]); - const rslt = varnum(data); - assertEquals(rslt, 0x01020304); -}); -Deno.test("testVarnumLittleEndian", function (): void { - const data = new Uint8Array([1, 2, 3, 4]); - const rslt = varnum(data, { endian: "little" }); - assertEquals(rslt, 0x04030201); -}); - -Deno.test("testWriteVarbig", async function (): Promise { - const data = new Uint8Array(8); - const buff = new Deno.Buffer(); - await writeVarbig(buff, 0x0102030405060708n); - await buff.read(data); - assertEquals( - data, - new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]), - ); -}); - -Deno.test("testWriteVarbigLittleEndian", async function (): Promise { - const data = new Uint8Array(8); - const buff = new Deno.Buffer(); - await writeVarbig(buff, 0x0807060504030201n, { endian: "little" }); - await buff.read(data); - assertEquals( - data, - new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]), - ); -}); - -Deno.test("testWriteVarnum", async function (): Promise { - const data = new Uint8Array(4); - const buff = new Deno.Buffer(); - await writeVarnum(buff, 0x01020304); - await buff.read(data); - assertEquals(data, new Uint8Array([0x01, 0x02, 0x03, 0x04])); -}); - -Deno.test("testWriteVarnumLittleEndian", async function (): Promise { - const data = new Uint8Array(4); - const buff = new Deno.Buffer(); - await writeVarnum(buff, 0x04030201, { endian: "little" }); - await buff.read(data); - assertEquals(data, new Uint8Array([0x01, 0x02, 0x03, 0x04])); -}); - -Deno.test("testVarbigBytes", function (): void { - const rslt = varbigBytes(0x0102030405060708n); - assertEquals( - rslt, - new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]), - ); -}); - -Deno.test("testVarbigBytesLittleEndian", function (): void { - const rslt = varbigBytes(0x0807060504030201n, { endian: "little" }); - assertEquals( - rslt, - new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]), - ); -}); - -Deno.test("testVarnumBytes", function (): void { - const rslt = varnumBytes(0x01020304); - assertEquals(rslt, new Uint8Array([0x01, 0x02, 0x03, 0x04])); -}); - -Deno.test("testVarnumBytesLittleEndian", function (): void { - const rslt = varnumBytes(0x04030201, { endian: "little" }); - assertEquals(rslt, new Uint8Array([0x01, 0x02, 0x03, 0x04])); -}); diff --git a/std/encoding/csv.ts b/std/encoding/csv.ts deleted file mode 100644 index bda28c1d93..0000000000 --- a/std/encoding/csv.ts +++ /dev/null @@ -1,462 +0,0 @@ -// Ported from Go: -// https://github.com/golang/go/blob/go1.12.5/src/encoding/csv/ -// Copyright 2011 The Go Authors. All rights reserved. BSD license. -// https://github.com/golang/go/blob/master/LICENSE -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { BufReader } from "../io/bufio.ts"; -import { TextProtoReader } from "../textproto/mod.ts"; -import { StringReader } from "../io/readers.ts"; -import { assert } from "../_util/assert.ts"; - -export { NEWLINE, stringify, StringifyError } from "./csv_stringify.ts"; - -export type { - Column, - ColumnDetails, - DataItem, - StringifyOptions, -} from "./csv_stringify.ts"; - -const INVALID_RUNE = ["\r", "\n", '"']; - -export const ERR_BARE_QUOTE = 'bare " in non-quoted-field'; -export const ERR_QUOTE = 'extraneous or missing " in quoted-field'; -export const ERR_INVALID_DELIM = "Invalid Delimiter"; -export const ERR_FIELD_COUNT = "wrong number of fields"; - -/** - * A ParseError is returned for parsing errors. - * Line numbers are 1-indexed and columns are 0-indexed. - */ -export class ParseError extends Error { - /** Line where the record starts*/ - startLine: number; - /** Line where the error occurred */ - line: number; - /** Column (rune index) where the error occurred */ - column: number | null; - - constructor( - start: number, - line: number, - column: number | null, - message: string, - ) { - super(); - this.startLine = start; - this.column = column; - this.line = line; - - if (message === ERR_FIELD_COUNT) { - this.message = `record on line ${line}: ${message}`; - } else if (start !== line) { - this.message = - `record on line ${start}; parse error on line ${line}, column ${column}: ${message}`; - } else { - this.message = - `parse error on line ${line}, column ${column}: ${message}`; - } - } -} - -/** - * @property separator - Character which separates values. Default: ',' - * @property comment - Character to start a comment. Default: '#' - * @property trimLeadingSpace - Flag to trim the leading space of the value. - * Default: 'false' - * @property lazyQuotes - Allow unquoted quote in a quoted field or non double - * quoted quotes in quoted field. Default: 'false' - * @property fieldsPerRecord - Enabling the check of fields for each row. - * If == 0, first row is used as referral for the number of fields. - */ -export interface ReadOptions { - separator?: string; - comment?: string; - trimLeadingSpace?: boolean; - lazyQuotes?: boolean; - fieldsPerRecord?: number; -} - -function chkOptions(opt: ReadOptions): void { - if (!opt.separator) { - opt.separator = ","; - } - if (!opt.trimLeadingSpace) { - opt.trimLeadingSpace = false; - } - if ( - INVALID_RUNE.includes(opt.separator) || - (typeof opt.comment === "string" && INVALID_RUNE.includes(opt.comment)) || - opt.separator === opt.comment - ) { - throw new Error(ERR_INVALID_DELIM); - } -} - -async function readRecord( - startLine: number, - reader: BufReader, - opt: ReadOptions = { separator: ",", trimLeadingSpace: false }, -): Promise { - const tp = new TextProtoReader(reader); - let line = await readLine(tp); - let lineIndex = startLine + 1; - - if (line === null) return null; - if (line.length === 0) { - return []; - } - // line starting with comment character is ignored - if (opt.comment && line[0] === opt.comment) { - return []; - } - - assert(opt.separator != null); - - let fullLine = line; - let quoteError: ParseError | null = null; - const quote = '"'; - const quoteLen = quote.length; - const separatorLen = opt.separator.length; - let recordBuffer = ""; - const fieldIndexes = [] as number[]; - parseField: - for (;;) { - if (opt.trimLeadingSpace) { - line = line.trimLeft(); - } - - if (line.length === 0 || !line.startsWith(quote)) { - // Non-quoted string field - const i = line.indexOf(opt.separator); - let field = line; - if (i >= 0) { - field = field.substring(0, i); - } - // Check to make sure a quote does not appear in field. - if (!opt.lazyQuotes) { - const j = field.indexOf(quote); - if (j >= 0) { - const col = runeCount( - fullLine.slice(0, fullLine.length - line.slice(j).length), - ); - quoteError = new ParseError( - startLine + 1, - lineIndex, - col, - ERR_BARE_QUOTE, - ); - break parseField; - } - } - recordBuffer += field; - fieldIndexes.push(recordBuffer.length); - if (i >= 0) { - line = line.substring(i + separatorLen); - continue parseField; - } - break parseField; - } else { - // Quoted string field - line = line.substring(quoteLen); - for (;;) { - const i = line.indexOf(quote); - if (i >= 0) { - // Hit next quote. - recordBuffer += line.substring(0, i); - line = line.substring(i + quoteLen); - if (line.startsWith(quote)) { - // `""` sequence (append quote). - recordBuffer += quote; - line = line.substring(quoteLen); - } else if (line.startsWith(opt.separator)) { - // `","` sequence (end of field). - line = line.substring(separatorLen); - fieldIndexes.push(recordBuffer.length); - continue parseField; - } else if (0 === line.length) { - // `"\n` sequence (end of line). - fieldIndexes.push(recordBuffer.length); - break parseField; - } else if (opt.lazyQuotes) { - // `"` sequence (bare quote). - recordBuffer += quote; - } else { - // `"*` sequence (invalid non-escaped quote). - const col = runeCount( - fullLine.slice(0, fullLine.length - line.length - quoteLen), - ); - quoteError = new ParseError( - startLine + 1, - lineIndex, - col, - ERR_QUOTE, - ); - break parseField; - } - } else if (line.length > 0 || !(await isEOF(tp))) { - // Hit end of line (copy all data so far). - recordBuffer += line; - const r = await readLine(tp); - lineIndex++; - line = r ?? ""; // This is a workaround for making this module behave similarly to the encoding/csv/reader.go. - fullLine = line; - if (r === null) { - // Abrupt end of file (EOF or error). - if (!opt.lazyQuotes) { - const col = runeCount(fullLine); - quoteError = new ParseError( - startLine + 1, - lineIndex, - col, - ERR_QUOTE, - ); - break parseField; - } - fieldIndexes.push(recordBuffer.length); - break parseField; - } - recordBuffer += "\n"; // preserve line feed (This is because TextProtoReader removes it.) - } else { - // Abrupt end of file (EOF on error). - if (!opt.lazyQuotes) { - const col = runeCount(fullLine); - quoteError = new ParseError( - startLine + 1, - lineIndex, - col, - ERR_QUOTE, - ); - break parseField; - } - fieldIndexes.push(recordBuffer.length); - break parseField; - } - } - } - } - if (quoteError) { - throw quoteError; - } - const result = [] as string[]; - let preIdx = 0; - for (const i of fieldIndexes) { - result.push(recordBuffer.slice(preIdx, i)); - preIdx = i; - } - return result; -} - -async function isEOF(tp: TextProtoReader): Promise { - return (await tp.r.peek(0)) === null; -} - -function runeCount(s: string): number { - // Array.from considers the surrogate pair. - return Array.from(s).length; -} - -async function readLine(tp: TextProtoReader): Promise { - let line: string; - const r = await tp.readLine(); - if (r === null) return null; - line = r; - - // For backwards compatibility, drop trailing \r before EOF. - if ((await isEOF(tp)) && line.length > 0 && line[line.length - 1] === "\r") { - line = line.substring(0, line.length - 1); - } - - // Normalize \r\n to \n on all input lines. - if ( - line.length >= 2 && - line[line.length - 2] === "\r" && - line[line.length - 1] === "\n" - ) { - line = line.substring(0, line.length - 2); - line = line + "\n"; - } - - return line; -} - -/** - * Parse the CSV from the `reader` with the options provided and return `string[][]`. - * - * @param reader provides the CSV data to parse - * @param opt controls the parsing behavior - */ -export async function readMatrix( - reader: BufReader, - opt: ReadOptions = { - separator: ",", - trimLeadingSpace: false, - lazyQuotes: false, - }, -): Promise { - const result: string[][] = []; - let _nbFields: number | undefined; - let lineResult: string[]; - let first = true; - let lineIndex = 0; - chkOptions(opt); - - for (;;) { - const r = await readRecord(lineIndex, reader, opt); - if (r === null) break; - lineResult = r; - lineIndex++; - // If fieldsPerRecord is 0, Read sets it to - // the number of fields in the first record - if (first) { - first = false; - if (opt.fieldsPerRecord !== undefined) { - if (opt.fieldsPerRecord === 0) { - _nbFields = lineResult.length; - } else { - _nbFields = opt.fieldsPerRecord; - } - } - } - - if (lineResult.length > 0) { - if (_nbFields && _nbFields !== lineResult.length) { - throw new ParseError(lineIndex, lineIndex, null, ERR_FIELD_COUNT); - } - result.push(lineResult); - } - } - return result; -} - -/** - * Parse the CSV string/buffer with the options provided. - * - * ColumnOptions provides the column definition - * and the parse function for each entry of the - * column. - */ -export interface ColumnOptions { - /** - * Name of the column to be used as property - */ - name: string; - /** - * Parse function for the column. - * This is executed on each entry of the header. - * This can be combined with the Parse function of the rows. - */ - parse?: (input: string) => unknown; -} - -export interface ParseOptions extends ReadOptions { - /** - * If you provide `skipFirstRow: true` and `columns`, the first line will be skipped. - * If you provide `skipFirstRow: true` but not `columns`, the first line will be skipped and used as header definitions. - */ - skipFirstRow?: boolean; - - /** - * If you provide `string[]` or `ColumnOptions[]`, those names will be used for header definition. - */ - columns?: string[] | ColumnOptions[]; - - /** Parse function for rows. - * Example: - * const r = await parseFile('a,b,c\ne,f,g\n', { - * columns: ["this", "is", "sparta"], - * parse: (e: Record) => { - * return { super: e.this, street: e.is, fighter: e.sparta }; - * } - * }); - * // output - * [ - * { super: "a", street: "b", fighter: "c" }, - * { super: "e", street: "f", fighter: "g" } - * ] - */ - parse?: (input: unknown) => unknown; -} - -/** - * Csv parse helper to manipulate data. - * Provides an auto/custom mapper for columns and parse function - * for columns and rows. - * @param input Input to parse. Can be a string or BufReader. - * @param opt options of the parser. - * @returns If you don't provide `opt.skipFirstRow`, `opt.parse`, and `opt.columns`, it returns `string[][]`. - * If you provide `opt.skipFirstRow` or `opt.columns` but not `opt.parse`, it returns `object[]`. - * If you provide `opt.parse`, it returns an array where each element is the value returned from `opt.parse`. - */ -export async function parse( - input: string | BufReader, - opt: ParseOptions = { - skipFirstRow: false, - }, -): Promise { - let r: string[][]; - if (input instanceof BufReader) { - r = await readMatrix(input, opt); - } else { - r = await readMatrix(new BufReader(new StringReader(input)), opt); - } - if (opt.skipFirstRow || opt.columns) { - let headers: ColumnOptions[] = []; - let i = 0; - - if (opt.skipFirstRow) { - const head = r.shift(); - assert(head != null); - headers = head.map( - (e): ColumnOptions => { - return { - name: e, - }; - }, - ); - i++; - } - - if (opt.columns) { - if (typeof opt.columns[0] !== "string") { - headers = opt.columns as ColumnOptions[]; - } else { - const h = opt.columns as string[]; - headers = h.map( - (e): ColumnOptions => { - return { - name: e, - }; - }, - ); - } - } - return r.map((e): unknown => { - if (e.length !== headers.length) { - throw `Error number of fields line:${i}`; - } - i++; - const out: Record = {}; - for (let j = 0; j < e.length; j++) { - const h = headers[j]; - if (h.parse) { - out[h.name] = h.parse(e[j]); - } else { - out[h.name] = e[j]; - } - } - if (opt.parse) { - return opt.parse(out); - } - return out; - }); - } - if (opt.parse) { - return r.map((e: string[]): unknown => { - assert(opt.parse, "opt.parse must be set"); - return opt.parse(e); - }); - } - return r; -} diff --git a/std/encoding/csv_stringify.ts b/std/encoding/csv_stringify.ts deleted file mode 100644 index 921631d376..0000000000 --- a/std/encoding/csv_stringify.ts +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// Implements the CSV spec at https://tools.ietf.org/html/rfc4180 - -// This module is browser compatible. - -const QUOTE = '"'; -export const NEWLINE = "\r\n"; - -export class StringifyError extends Error { - readonly name = "StringifyError"; -} - -function getEscapedString(value: unknown, sep: string): string { - if (value === undefined || value === null) return ""; - let str = ""; - - if (typeof value === "object") str = JSON.stringify(value); - else str = String(value); - - // Is regex.test more performant here? If so, how to dynamically create? - // https://stackoverflow.com/questions/3561493/ - if (str.includes(sep) || str.includes(NEWLINE) || str.includes(QUOTE)) { - return `${QUOTE}${str.replaceAll(QUOTE, `${QUOTE}${QUOTE}`)}${QUOTE}`; - } - - return str; -} - -type PropertyAccessor = number | string; - -/** - * @param fn Optional callback for transforming the value - * - * @param header Explicit column header name. If omitted, - * the (final) property accessor is used for this value. - * - * @param prop Property accessor(s) used to access the value on the object - */ -export type ColumnDetails = { - // "unknown" is more type-safe, but inconvenient for user. How to resolve? - // deno-lint-ignore no-explicit-any - fn?: (value: any) => string | Promise; - header?: string; - prop: PropertyAccessor | PropertyAccessor[]; -}; - -export type Column = ColumnDetails | PropertyAccessor | PropertyAccessor[]; - -type NormalizedColumn = Omit & { - header: string; - prop: PropertyAccessor[]; -}; - -function normalizeColumn(column: Column): NormalizedColumn { - let fn: NormalizedColumn["fn"], - header: NormalizedColumn["header"], - prop: NormalizedColumn["prop"]; - - if (typeof column === "object") { - if (Array.isArray(column)) { - header = String(column[column.length - 1]); - prop = column; - } else { - ({ fn } = column); - prop = Array.isArray(column.prop) ? column.prop : [column.prop]; - header = typeof column.header === "string" - ? column.header - : String(prop[prop.length - 1]); - } - } else { - header = String(column); - prop = [column]; - } - - return { fn, header, prop }; -} - -type ObjectWithStringPropertyKeys = Record; - -/** An object (plain or array) */ -export type DataItem = ObjectWithStringPropertyKeys | unknown[]; - -/** - * Returns an array of values from an object using the property accessors - * (and optional transform function) in each column - */ -async function getValuesFromItem( - item: DataItem, - normalizedColumns: NormalizedColumn[], -): Promise { - const values: unknown[] = []; - - for (const column of normalizedColumns) { - let value: unknown = item; - - for (const prop of column.prop) { - if (typeof value !== "object" || value === null) continue; - if (Array.isArray(value)) { - if (typeof prop === "number") value = value[prop]; - else { - throw new StringifyError('Property accessor is not of type "number"'); - } - } // I think this assertion is safe. Confirm? - else value = (value as ObjectWithStringPropertyKeys)[prop]; - } - - if (typeof column.fn === "function") value = await column.fn(value); - values.push(value); - } - - return values; -} - -/** - * @param headers Whether or not to include the row of headers. - * Default: `true` - * - * @param separator Delimiter used to separate values. Examples: - * - `","` _comma_ (Default) - * - `"\t"` _tab_ - * - `"|"` _pipe_ - * - etc. - */ -export type StringifyOptions = { - headers?: boolean; - separator?: string; -}; - -/** - * @param data The array of objects to encode - * @param columns Array of values specifying which data to include in the output - * @param options Output formatting options - */ -export async function stringify( - data: DataItem[], - columns: Column[], - options: StringifyOptions = {}, -): Promise { - const { headers, separator: sep } = { - headers: true, - separator: ",", - ...options, - }; - if (sep.includes(QUOTE) || sep.includes(NEWLINE)) { - const message = [ - "Separator cannot include the following strings:", - ' - U+0022: Quotation mark (")', - " - U+000D U+000A: Carriage Return + Line Feed (\\r\\n)", - ].join("\n"); - throw new StringifyError(message); - } - - const normalizedColumns = columns.map(normalizeColumn); - let output = ""; - - if (headers) { - output += normalizedColumns - .map((column) => getEscapedString(column.header, sep)) - .join(sep); - output += NEWLINE; - } - - for (const item of data) { - const values = await getValuesFromItem(item, normalizedColumns); - output += values - .map((value) => getEscapedString(value, sep)) - .join(sep); - output += NEWLINE; - } - - return output; -} diff --git a/std/encoding/csv_stringify_test.ts b/std/encoding/csv_stringify_test.ts deleted file mode 100644 index f8b7d81cde..0000000000 --- a/std/encoding/csv_stringify_test.ts +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { assertEquals, assertThrowsAsync } from "../testing/asserts.ts"; - -import { - Column, - DataItem, - NEWLINE, - stringify, - StringifyError, - StringifyOptions, -} from "./csv_stringify.ts"; - -type StringifyTestCaseBase = { - columns: Column[]; - data: DataItem[]; - name: string; - options?: StringifyOptions; -}; - -type StringifyTestCaseError = StringifyTestCaseBase & { - errorMessage?: string; - // deno-lint-ignore no-explicit-any - throwsError: new (...args: any[]) => Error; -}; - -type StringifyTestCase = StringifyTestCaseBase & { expected: string }; - -const stringifyTestCases: (StringifyTestCase | StringifyTestCaseError)[] = [ - { - columns: ["a"], - data: [["foo"], ["bar"]], - errorMessage: 'Property accessor is not of type "number"', - name: "[CSV_stringify] Access array index using string", - throwsError: StringifyError, - }, - { - columns: [0], - data: [["foo"], ["bar"]], - errorMessage: [ - "Separator cannot include the following strings:", - ' - U+0022: Quotation mark (")', - " - U+000D U+000A: Carriage Return + Line Feed (\\r\\n)", - ].join("\n"), - name: "[CSV_stringify] Double quote in separator", - options: { separator: '"' }, - throwsError: StringifyError, - }, - { - columns: [0], - data: [["foo"], ["bar"]], - errorMessage: [ - "Separator cannot include the following strings:", - ' - U+0022: Quotation mark (")', - " - U+000D U+000A: Carriage Return + Line Feed (\\r\\n)", - ].join("\n"), - name: "[CSV_stringify] CRLF in separator", - options: { separator: "\r\n" }, - throwsError: StringifyError, - }, - { - columns: [ - { - fn: (obj) => obj.toUpperCase(), - prop: "msg", - }, - ], - data: [{ msg: { value: "foo" } }, { msg: { value: "bar" } }], - name: "[CSV_stringify] Transform function", - throwsError: TypeError, - }, - { - columns: [], - data: [], - expected: NEWLINE, - name: "[CSV_stringify] No data, no columns", - }, - { - columns: [], - data: [], - expected: ``, - name: "[CSV_stringify] No data, no columns, no headers", - options: { headers: false }, - }, - { - columns: ["a"], - data: [], - expected: `a${NEWLINE}`, - name: "[CSV_stringify] No data, columns", - }, - { - columns: ["a"], - data: [], - expected: ``, - name: "[CSV_stringify] No data, columns, no headers", - options: { headers: false }, - }, - { - columns: [], - data: [{ a: 1 }, { a: 2 }], - expected: `${NEWLINE}${NEWLINE}${NEWLINE}`, - name: "[CSV_stringify] Data, no columns", - }, - { - columns: [0, 1], - data: [["foo", "bar"], ["baz", "qux"]], - expected: `0\r1${NEWLINE}foo\rbar${NEWLINE}baz\rqux${NEWLINE}`, - name: "[CSV_stringify] Separator: CR", - options: { separator: "\r" }, - }, - { - columns: [0, 1], - data: [["foo", "bar"], ["baz", "qux"]], - expected: `0\n1${NEWLINE}foo\nbar${NEWLINE}baz\nqux${NEWLINE}`, - name: "[CSV_stringify] Separator: LF", - options: { separator: "\n" }, - }, - { - columns: [1], - data: [{ 1: 1 }, { 1: 2 }], - expected: `1${NEWLINE}1${NEWLINE}2${NEWLINE}`, - name: "[CSV_stringify] Column: number accessor, Data: object", - }, - { - columns: [{ header: "Value", prop: "value" }], - data: [{ value: "foo" }, { value: "bar" }], - expected: `foo${NEWLINE}bar${NEWLINE}`, - name: "[CSV_stringify] Explicit header value, no headers", - options: { headers: false }, - }, - { - columns: [1], - data: [["key", "foo"], ["key", "bar"]], - expected: `1${NEWLINE}foo${NEWLINE}bar${NEWLINE}`, - name: "[CSV_stringify] Column: number accessor, Data: array", - }, - { - columns: [[1]], - data: [{ 1: 1 }, { 1: 2 }], - expected: `1${NEWLINE}1${NEWLINE}2${NEWLINE}`, - name: "[CSV_stringify] Column: array number accessor, Data: object", - }, - { - columns: [[1]], - data: [["key", "foo"], ["key", "bar"]], - expected: `1${NEWLINE}foo${NEWLINE}bar${NEWLINE}`, - name: "[CSV_stringify] Column: array number accessor, Data: array", - }, - { - columns: [[1, 1]], - data: [["key", ["key", "foo"]], ["key", ["key", "bar"]]], - expected: `1${NEWLINE}foo${NEWLINE}bar${NEWLINE}`, - name: "[CSV_stringify] Column: array number accessor, Data: array", - }, - { - columns: ["value"], - data: [{ value: "foo" }, { value: "bar" }], - expected: `value${NEWLINE}foo${NEWLINE}bar${NEWLINE}`, - name: "[CSV_stringify] Column: string accessor, Data: object", - }, - { - columns: [["value"]], - data: [{ value: "foo" }, { value: "bar" }], - expected: `value${NEWLINE}foo${NEWLINE}bar${NEWLINE}`, - name: "[CSV_stringify] Column: array string accessor, Data: object", - }, - { - columns: [["msg", "value"]], - data: [{ msg: { value: "foo" } }, { msg: { value: "bar" } }], - expected: `value${NEWLINE}foo${NEWLINE}bar${NEWLINE}`, - name: "[CSV_stringify] Column: array string accessor, Data: object", - }, - { - columns: [ - { - header: "Value", - prop: ["msg", "value"], - }, - ], - data: [{ msg: { value: "foo" } }, { msg: { value: "bar" } }], - expected: `Value${NEWLINE}foo${NEWLINE}bar${NEWLINE}`, - name: "[CSV_stringify] Explicit header", - }, - { - columns: [ - { - fn: (str: string) => str.toUpperCase(), - prop: ["msg", "value"], - }, - ], - data: [{ msg: { value: "foo" } }, { msg: { value: "bar" } }], - expected: `value${NEWLINE}FOO${NEWLINE}BAR${NEWLINE}`, - name: "[CSV_stringify] Transform function 1", - }, - { - columns: [ - { - fn: (str: string) => Promise.resolve(str.toUpperCase()), - prop: ["msg", "value"], - }, - ], - data: [{ msg: { value: "foo" } }, { msg: { value: "bar" } }], - expected: `value${NEWLINE}FOO${NEWLINE}BAR${NEWLINE}`, - name: "[CSV_stringify] Transform function 1 async", - }, - { - columns: [ - { - fn: (obj: { value: string }) => obj.value, - prop: "msg", - }, - ], - data: [{ msg: { value: "foo" } }, { msg: { value: "bar" } }], - expected: `msg${NEWLINE}foo${NEWLINE}bar${NEWLINE}`, - name: "[CSV_stringify] Transform function 2", - }, - { - columns: [ - { - fn: (obj: { value: string }) => obj.value, - header: "Value", - prop: "msg", - }, - ], - data: [{ msg: { value: "foo" } }, { msg: { value: "bar" } }], - expected: `Value${NEWLINE}foo${NEWLINE}bar${NEWLINE}`, - name: "[CSV_stringify] Transform function 2, explicit header", - }, - { - columns: [0], - data: [[{ value: "foo" }], [{ value: "bar" }]], - expected: - `0${NEWLINE}"{""value"":""foo""}"${NEWLINE}"{""value"":""bar""}"${NEWLINE}`, - name: "[CSV_stringify] Targeted value: object", - }, - { - columns: [0], - data: [ - [[{ value: "foo" }, { value: "bar" }]], - [[{ value: "baz" }, { value: "qux" }]], - ], - expected: - `0${NEWLINE}"[{""value"":""foo""},{""value"":""bar""}]"${NEWLINE}"[{""value"":""baz""},{""value"":""qux""}]"${NEWLINE}`, - name: "[CSV_stringify] Targeted value: arary of objects", - }, - { - columns: [0], - data: [[["foo", "bar"]], [["baz", "qux"]]], - expected: - `0${NEWLINE}"[""foo"",""bar""]"${NEWLINE}"[""baz"",""qux""]"${NEWLINE}`, - name: "[CSV_stringify] Targeted value: array", - }, - { - columns: [0], - data: [[["foo", "bar"]], [["baz", "qux"]]], - expected: - `0${NEWLINE}"[""foo"",""bar""]"${NEWLINE}"[""baz"",""qux""]"${NEWLINE}`, - name: "[CSV_stringify] Targeted value: array, separator: tab", - options: { separator: "\t" }, - }, - { - columns: [0], - data: [[], []], - expected: `0${NEWLINE}${NEWLINE}${NEWLINE}`, - name: "[CSV_stringify] Targeted value: undefined", - }, - { - columns: [0], - data: [[null], [null]], - expected: `0${NEWLINE}${NEWLINE}${NEWLINE}`, - name: "[CSV_stringify] Targeted value: null", - }, - { - columns: [0], - data: [[0xa], [0xb]], - expected: `0${NEWLINE}10${NEWLINE}11${NEWLINE}`, - name: "[CSV_stringify] Targeted value: hex number", - }, - { - columns: [0], - data: [[BigInt("1")], [BigInt("2")]], - expected: `0${NEWLINE}1${NEWLINE}2${NEWLINE}`, - name: "[CSV_stringify] Targeted value: BigInt", - }, - { - columns: [0], - data: [[true], [false]], - expected: `0${NEWLINE}true${NEWLINE}false${NEWLINE}`, - name: "[CSV_stringify] Targeted value: boolean", - }, - { - columns: [0], - data: [["foo"], ["bar"]], - expected: `0${NEWLINE}foo${NEWLINE}bar${NEWLINE}`, - name: "[CSV_stringify] Targeted value: string", - }, - { - columns: [0], - data: [[Symbol("foo")], [Symbol("bar")]], - expected: `0${NEWLINE}Symbol(foo)${NEWLINE}Symbol(bar)${NEWLINE}`, - name: "[CSV_stringify] Targeted value: symbol", - }, - { - columns: [0], - data: [[(n: number) => n]], - expected: `0${NEWLINE}(n) => n${NEWLINE}`, - name: "[CSV_stringify] Targeted value: function", - }, - { - columns: [0], - data: [['foo"']], - expected: `0${NEWLINE}"foo"""${NEWLINE}`, - name: "[CSV_stringify] Value with double quote", - }, - { - columns: [0], - data: [["foo\r\n"]], - expected: `0${NEWLINE}"foo\r\n"${NEWLINE}`, - name: "[CSV_stringify] Value with CRLF", - }, - { - columns: [0], - data: [["foo\r"]], - expected: `0${NEWLINE}foo\r${NEWLINE}`, - name: "[CSV_stringify] Value with CR", - }, - { - columns: [0], - data: [["foo\n"]], - expected: `0${NEWLINE}foo\n${NEWLINE}`, - name: "[CSV_stringify] Value with LF", - }, - { - columns: [0], - data: [["foo,"]], - expected: `0${NEWLINE}"foo,"${NEWLINE}`, - name: "[CSV_stringify] Value with comma", - }, - { - columns: [0], - data: [["foo,"]], - expected: `0${NEWLINE}foo,${NEWLINE}`, - name: "[CSV_stringify] Value with comma, tab separator", - options: { separator: "\t" }, - }, -]; - -for (const tc of stringifyTestCases) { - if ((tc as StringifyTestCaseError).throwsError) { - const t = tc as StringifyTestCaseError; - Deno.test({ - async fn() { - await assertThrowsAsync( - async () => { - await stringify(t.data, t.columns, t.options); - }, - t.throwsError, - t.errorMessage, - ); - }, - name: t.name, - }); - } else { - const t = tc as StringifyTestCase; - Deno.test({ - async fn() { - const actual = await stringify(t.data, t.columns, t.options); - assertEquals(actual, t.expected); - }, - name: t.name, - }); - } -} diff --git a/std/encoding/csv_test.ts b/std/encoding/csv_test.ts deleted file mode 100644 index c3257808f5..0000000000 --- a/std/encoding/csv_test.ts +++ /dev/null @@ -1,653 +0,0 @@ -// Test ported from Golang -// https://github.com/golang/go/blob/2cc15b1/src/encoding/csv/reader_test.go -// Copyright 2011 The Go Authors. All rights reserved. BSD license. -// https://github.com/golang/go/blob/master/LICENSE -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { assertEquals, assertThrowsAsync } from "../testing/asserts.ts"; -import { - ERR_BARE_QUOTE, - ERR_FIELD_COUNT, - ERR_INVALID_DELIM, - ERR_QUOTE, - parse, - ParseError, - readMatrix, -} from "./csv.ts"; -import { StringReader } from "../io/readers.ts"; -import { BufReader } from "../io/bufio.ts"; - -// Test cases for `readMatrix()` -const testCases = [ - { - Name: "Simple", - Input: "a,b,c\n", - Output: [["a", "b", "c"]], - }, - { - Name: "CRLF", - Input: "a,b\r\nc,d\r\n", - Output: [ - ["a", "b"], - ["c", "d"], - ], - }, - { - Name: "BareCR", - Input: "a,b\rc,d\r\n", - Output: [["a", "b\rc", "d"]], - }, - { - Name: "RFC4180test", - Input: `#field1,field2,field3 -"aaa","bbb","ccc" -"a,a","bbb","ccc" -zzz,yyy,xxx`, - UseFieldsPerRecord: true, - FieldsPerRecord: 0, - Output: [ - ["#field1", "field2", "field3"], - ["aaa", "bbb", "ccc"], - ["a,a", `bbb`, "ccc"], - ["zzz", "yyy", "xxx"], - ], - }, - { - Name: "NoEOLTest", - Input: "a,b,c", - Output: [["a", "b", "c"]], - }, - { - Name: "Semicolon", - Input: "a;b;c\n", - Output: [["a", "b", "c"]], - Separator: ";", - }, - { - Name: "MultiLine", - Input: `"two -line","one line","three -line -field"`, - Output: [["two\nline", "one line", "three\nline\nfield"]], - }, - { - Name: "BlankLine", - Input: "a,b,c\n\nd,e,f\n\n", - Output: [ - ["a", "b", "c"], - ["d", "e", "f"], - ], - }, - { - Name: "BlankLineFieldCount", - Input: "a,b,c\n\nd,e,f\n\n", - Output: [ - ["a", "b", "c"], - ["d", "e", "f"], - ], - UseFieldsPerRecord: true, - FieldsPerRecord: 0, - }, - { - Name: "TrimSpace", - Input: " a, b, c\n", - Output: [["a", "b", "c"]], - TrimLeadingSpace: true, - }, - { - Name: "LeadingSpace", - Input: " a, b, c\n", - Output: [[" a", " b", " c"]], - }, - { - Name: "Comment", - Input: "#1,2,3\na,b,c\n#comment", - Output: [["a", "b", "c"]], - Comment: "#", - }, - { - Name: "NoComment", - Input: "#1,2,3\na,b,c", - Output: [ - ["#1", "2", "3"], - ["a", "b", "c"], - ], - }, - { - Name: "LazyQuotes", - Input: `a "word","1"2",a","b`, - Output: [[`a "word"`, `1"2`, `a"`, `b`]], - LazyQuotes: true, - }, - { - Name: "BareQuotes", - Input: `a "word","1"2",a"`, - Output: [[`a "word"`, `1"2`, `a"`]], - LazyQuotes: true, - }, - { - Name: "BareDoubleQuotes", - Input: `a""b,c`, - Output: [[`a""b`, `c`]], - LazyQuotes: true, - }, - { - Name: "BadDoubleQuotes", - Input: `a""b,c`, - Error: new ParseError(1, 1, 1, ERR_BARE_QUOTE), - }, - { - Name: "TrimQuote", - Input: ` "a"," b",c`, - Output: [["a", " b", "c"]], - TrimLeadingSpace: true, - }, - { - Name: "BadBareQuote", - Input: `a "word","b"`, - Error: new ParseError(1, 1, 2, ERR_BARE_QUOTE), - }, - { - Name: "BadTrailingQuote", - Input: `"a word",b"`, - Error: new ParseError(1, 1, 10, ERR_BARE_QUOTE), - }, - { - Name: "ExtraneousQuote", - Input: `"a "word","b"`, - Error: new ParseError(1, 1, 3, ERR_QUOTE), - }, - { - Name: "BadFieldCount", - Input: "a,b,c\nd,e", - Error: new ParseError(2, 2, null, ERR_FIELD_COUNT), - UseFieldsPerRecord: true, - FieldsPerRecord: 0, - }, - { - Name: "BadFieldCount1", - Input: `a,b,c`, - UseFieldsPerRecord: true, - FieldsPerRecord: 2, - Error: new ParseError(1, 1, null, ERR_FIELD_COUNT), - }, - { - Name: "FieldCount", - Input: "a,b,c\nd,e", - Output: [ - ["a", "b", "c"], - ["d", "e"], - ], - }, - { - Name: "TrailingCommaEOF", - Input: "a,b,c,", - Output: [["a", "b", "c", ""]], - }, - { - Name: "TrailingCommaEOL", - Input: "a,b,c,\n", - Output: [["a", "b", "c", ""]], - }, - { - Name: "TrailingCommaSpaceEOF", - Input: "a,b,c, ", - Output: [["a", "b", "c", ""]], - TrimLeadingSpace: true, - }, - { - Name: "TrailingCommaSpaceEOL", - Input: "a,b,c, \n", - Output: [["a", "b", "c", ""]], - TrimLeadingSpace: true, - }, - { - Name: "TrailingCommaLine3", - Input: "a,b,c\nd,e,f\ng,hi,", - Output: [ - ["a", "b", "c"], - ["d", "e", "f"], - ["g", "hi", ""], - ], - TrimLeadingSpace: true, - }, - { - Name: "NotTrailingComma3", - Input: "a,b,c, \n", - Output: [["a", "b", "c", " "]], - }, - { - Name: "CommaFieldTest", - Input: `x,y,z,w -x,y,z, -x,y,, -x,,, -,,, -"x","y","z","w" -"x","y","z","" -"x","y","","" -"x","","","" -"","","","" -`, - Output: [ - ["x", "y", "z", "w"], - ["x", "y", "z", ""], - ["x", "y", "", ""], - ["x", "", "", ""], - ["", "", "", ""], - ["x", "y", "z", "w"], - ["x", "y", "z", ""], - ["x", "y", "", ""], - ["x", "", "", ""], - ["", "", "", ""], - ], - }, - { - Name: "TrailingCommaIneffective1", - Input: "a,b,\nc,d,e", - Output: [ - ["a", "b", ""], - ["c", "d", "e"], - ], - TrimLeadingSpace: true, - }, - { - Name: "ReadAllReuseRecord", - Input: "a,b\nc,d", - Output: [ - ["a", "b"], - ["c", "d"], - ], - ReuseRecord: true, - }, - { - Name: "StartLine1", // Issue 19019 - Input: 'a,"b\nc"d,e', - Error: new ParseError(1, 2, 1, ERR_QUOTE), - }, - { - Name: "StartLine2", - Input: 'a,b\n\"d\n\n,e', - Error: new ParseError(2, 5, 0, ERR_QUOTE), - }, - { - Name: "CRLFInQuotedField", // Issue 21201 - Input: 'A,"Hello\r\nHi",B\r\n', - Output: [["A", "Hello\nHi", "B"]], - }, - { - Name: "BinaryBlobField", // Issue 19410 - Input: "x09\x41\xb4\x1c,aktau", - Output: [["x09A\xb4\x1c", "aktau"]], - }, - { - Name: "TrailingCR", - Input: "field1,field2\r", - Output: [["field1", "field2"]], - }, - { - Name: "QuotedTrailingCR", - Input: '"field"\r', - Output: [["field"]], - }, - { - Name: "QuotedTrailingCRCR", - Input: '"field"\r\r', - Error: new ParseError(1, 1, 6, ERR_QUOTE), - }, - { - Name: "FieldCR", - Input: "field\rfield\r", - Output: [["field\rfield"]], - }, - { - Name: "FieldCRCR", - Input: "field\r\rfield\r\r", - Output: [["field\r\rfield\r"]], - }, - { - Name: "FieldCRCRLF", - Input: "field\r\r\nfield\r\r\n", - Output: [["field\r"], ["field\r"]], - }, - { - Name: "FieldCRCRLFCR", - Input: "field\r\r\n\rfield\r\r\n\r", - Output: [["field\r"], ["\rfield\r"]], - }, - { - Name: "FieldCRCRLFCRCR", - Input: "field\r\r\n\r\rfield\r\r\n\r\r", - Output: [["field\r"], ["\r\rfield\r"], ["\r"]], - }, - { - Name: "MultiFieldCRCRLFCRCR", - Input: "field1,field2\r\r\n\r\rfield1,field2\r\r\n\r\r,", - Output: [ - ["field1", "field2\r"], - ["\r\rfield1", "field2\r"], - ["\r\r", ""], - ], - }, - { - Name: "NonASCIICommaAndComment", - Input: "a£b,c£ \td,e\n€ comment\n", - Output: [["a", "b,c", "d,e"]], - TrimLeadingSpace: true, - Separator: "£", - Comment: "€", - }, - { - Name: "NonASCIICommaAndCommentWithQuotes", - Input: 'a€" b,"€ c\nλ comment\n', - Output: [["a", " b,", " c"]], - Separator: "€", - Comment: "λ", - }, - { - // λ and θ start with the same byte. - // This tests that the parser doesn't confuse such characters. - Name: "NonASCIICommaConfusion", - Input: '"abθcd"λefθgh', - Output: [["abθcd", "efθgh"]], - Separator: "λ", - Comment: "€", - }, - { - Name: "NonASCIICommentConfusion", - Input: "λ\nλ\nθ\nλ\n", - Output: [["λ"], ["λ"], ["λ"]], - Comment: "θ", - }, - { - Name: "QuotedFieldMultipleLF", - Input: '"\n\n\n\n"', - Output: [["\n\n\n\n"]], - }, - { - Name: "MultipleCRLF", - Input: "\r\n\r\n\r\n\r\n", - Output: [], - }, - /** - * The implementation may read each line in several chunks if - * it doesn't fit entirely. - * in the read buffer, so we should test the code to handle that condition. - */ - { - Name: "HugeLines", - Input: "#ignore\n".repeat(10000) + "@".repeat(5000) + "," + - "*".repeat(5000), - Output: [["@".repeat(5000), "*".repeat(5000)]], - Comment: "#", - }, - { - Name: "QuoteWithTrailingCRLF", - Input: '"foo"bar"\r\n', - Error: new ParseError(1, 1, 4, ERR_QUOTE), - }, - { - Name: "LazyQuoteWithTrailingCRLF", - Input: '"foo"bar"\r\n', - Output: [[`foo"bar`]], - LazyQuotes: true, - }, - { - Name: "DoubleQuoteWithTrailingCRLF", - Input: '"foo""bar"\r\n', - Output: [[`foo"bar`]], - }, - { - Name: "EvenQuotes", - Input: `""""""""`, - Output: [[`"""`]], - }, - { - Name: "OddQuotes", - Input: `"""""""`, - Error: new ParseError(1, 1, 7, ERR_QUOTE), - }, - { - Name: "LazyOddQuotes", - Input: `"""""""`, - Output: [[`"""`]], - LazyQuotes: true, - }, - { - Name: "BadComma1", - Separator: "\n", - Error: new Error(ERR_INVALID_DELIM), - }, - { - Name: "BadComma2", - Separator: "\r", - Error: new Error(ERR_INVALID_DELIM), - }, - { - Name: "BadComma3", - Separator: '"', - Error: new Error(ERR_INVALID_DELIM), - }, - { - Name: "BadComment1", - Comment: "\n", - Error: new Error(ERR_INVALID_DELIM), - }, - { - Name: "BadComment2", - Comment: "\r", - Error: new Error(ERR_INVALID_DELIM), - }, - { - Name: "BadCommaComment", - Separator: "X", - Comment: "X", - Error: new Error(ERR_INVALID_DELIM), - }, -]; -for (const t of testCases) { - Deno.test({ - name: `[CSV] ${t.Name}`, - async fn(): Promise { - let separator = ","; - let comment: string | undefined; - let fieldsPerRec: number | undefined; - let trim = false; - let lazyquote = false; - if (t.Separator) { - separator = t.Separator; - } - if (t.Comment) { - comment = t.Comment; - } - if (t.TrimLeadingSpace) { - trim = true; - } - if (t.UseFieldsPerRecord) { - fieldsPerRec = t.FieldsPerRecord; - } - if (t.LazyQuotes) { - lazyquote = t.LazyQuotes; - } - let actual; - if (t.Error) { - const err = await assertThrowsAsync(async () => { - await readMatrix( - new BufReader(new StringReader(t.Input ?? "")), - { - separator, - comment: comment, - trimLeadingSpace: trim, - fieldsPerRecord: fieldsPerRec, - lazyQuotes: lazyquote, - }, - ); - }); - - assertEquals(err, t.Error); - } else { - actual = await readMatrix( - new BufReader(new StringReader(t.Input ?? "")), - { - separator, - comment: comment, - trimLeadingSpace: trim, - fieldsPerRecord: fieldsPerRec, - lazyQuotes: lazyquote, - }, - ); - const expected = t.Output; - assertEquals(actual, expected); - } - }, - }); -} - -const parseTestCases = [ - { - name: "simple", - in: "a,b,c", - skipFirstRow: false, - result: [["a", "b", "c"]], - }, - { - name: "simple Bufreader", - in: new BufReader(new StringReader("a,b,c")), - skipFirstRow: false, - result: [["a", "b", "c"]], - }, - { - name: "multiline", - in: "a,b,c\ne,f,g\n", - skipFirstRow: false, - result: [ - ["a", "b", "c"], - ["e", "f", "g"], - ], - }, - { - name: "header mapping boolean", - in: "a,b,c\ne,f,g\n", - skipFirstRow: true, - result: [{ a: "e", b: "f", c: "g" }], - }, - { - name: "header mapping array", - in: "a,b,c\ne,f,g\n", - columns: ["this", "is", "sparta"], - result: [ - { this: "a", is: "b", sparta: "c" }, - { this: "e", is: "f", sparta: "g" }, - ], - }, - { - name: "header mapping object", - in: "a,b,c\ne,f,g\n", - columns: [{ name: "this" }, { name: "is" }, { name: "sparta" }], - result: [ - { this: "a", is: "b", sparta: "c" }, - { this: "e", is: "f", sparta: "g" }, - ], - }, - { - name: "header mapping parse entry", - in: "a,b,c\ne,f,g\n", - columns: [ - { - name: "this", - parse: (e: string): string => { - return `b${e}$$`; - }, - }, - { - name: "is", - parse: (e: string): number => { - return e.length; - }, - }, - { - name: "sparta", - parse: (e: string): unknown => { - return { bim: `boom-${e}` }; - }, - }, - ], - result: [ - { this: "ba$$", is: 1, sparta: { bim: `boom-c` } }, - { this: "be$$", is: 1, sparta: { bim: `boom-g` } }, - ], - }, - { - name: "multiline parse", - in: "a,b,c\ne,f,g\n", - parse: (e: string[]): unknown => { - return { super: e[0], street: e[1], fighter: e[2] }; - }, - skipFirstRow: false, - result: [ - { super: "a", street: "b", fighter: "c" }, - { super: "e", street: "f", fighter: "g" }, - ], - }, - { - name: "header mapping object parseline", - in: "a,b,c\ne,f,g\n", - columns: [{ name: "this" }, { name: "is" }, { name: "sparta" }], - parse: (e: Record): unknown => { - return { super: e.this, street: e.is, fighter: e.sparta }; - }, - result: [ - { super: "a", street: "b", fighter: "c" }, - { super: "e", street: "f", fighter: "g" }, - ], - }, - { - name: "provides both opts.skipFirstRow and opts.columns", - in: "a,b,1\nc,d,2\ne,f,3", - skipFirstRow: true, - columns: [ - { name: "foo" }, - { name: "bar" }, - { name: "baz", parse: (e: string) => Number(e) }, - ], - result: [ - { foo: "c", bar: "d", baz: 2 }, - { foo: "e", bar: "f", baz: 3 }, - ], - }, -]; - -for (const testCase of parseTestCases) { - Deno.test({ - name: `[CSV] Parse ${testCase.name}`, - async fn(): Promise { - const r = await parse(testCase.in, { - skipFirstRow: testCase.skipFirstRow, - columns: testCase.columns, - parse: testCase.parse as (input: unknown) => unknown, - }); - assertEquals(r, testCase.result); - }, - }); -} - -Deno.test({ - name: "[CSV] ParseError.message", - fn(): void { - assertEquals( - new ParseError(2, 2, null, ERR_FIELD_COUNT).message, - `record on line 2: ${ERR_FIELD_COUNT}`, - ); - - assertEquals( - new ParseError(1, 2, 1, ERR_QUOTE).message, - `record on line 1; parse error on line 2, column 1: ${ERR_QUOTE}`, - ); - - assertEquals( - new ParseError(1, 1, 7, ERR_QUOTE).message, - `parse error on line 1, column 7: ${ERR_QUOTE}`, - ); - }, -}); diff --git a/std/encoding/hex.ts b/std/encoding/hex.ts deleted file mode 100644 index c5cb519730..0000000000 --- a/std/encoding/hex.ts +++ /dev/null @@ -1,111 +0,0 @@ -// Ported from Go -// https://github.com/golang/go/blob/go1.12.5/src/encoding/hex/hex.go -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -const hexTable = new TextEncoder().encode("0123456789abcdef"); - -/** - * ErrInvalidByte takes an invalid byte and returns an Error. - * @param byte - */ -export function errInvalidByte(byte: number): Error { - return new Error( - "encoding/hex: invalid byte: " + - new TextDecoder().decode(new Uint8Array([byte])), - ); -} - -/** ErrLength returns an error about odd string length. */ -export function errLength(): Error { - return new Error("encoding/hex: odd length hex string"); -} - -// fromHexChar converts a hex character into its value. -function fromHexChar(byte: number): number { - // '0' <= byte && byte <= '9' - if (48 <= byte && byte <= 57) return byte - 48; - // 'a' <= byte && byte <= 'f' - if (97 <= byte && byte <= 102) return byte - 97 + 10; - // 'A' <= byte && byte <= 'F' - if (65 <= byte && byte <= 70) return byte - 65 + 10; - - throw errInvalidByte(byte); -} - -/** - * EncodedLen returns the length of an encoding of n source bytes. Specifically, - * it returns n * 2. - * @param n - */ -export function encodedLen(n: number): number { - return n * 2; -} - -/** - * Encode encodes `src` into `encodedLen(src.length)` bytes. - * @param src - */ -export function encode(src: Uint8Array): Uint8Array { - const dst = new Uint8Array(encodedLen(src.length)); - for (let i = 0; i < dst.length; i++) { - const v = src[i]; - dst[i * 2] = hexTable[v >> 4]; - dst[i * 2 + 1] = hexTable[v & 0x0f]; - } - return dst; -} - -/** - * EncodeToString returns the hexadecimal encoding of `src`. - * @param src - */ -export function encodeToString(src: Uint8Array): string { - return new TextDecoder().decode(encode(src)); -} - -/** - * Decode decodes `src` into `decodedLen(src.length)` bytes - * If the input is malformed an error will be thrown - * the error. - * @param src - */ -export function decode(src: Uint8Array): Uint8Array { - const dst = new Uint8Array(decodedLen(src.length)); - for (let i = 0; i < dst.length; i++) { - const a = fromHexChar(src[i * 2]); - const b = fromHexChar(src[i * 2 + 1]); - dst[i] = (a << 4) | b; - } - - if (src.length % 2 == 1) { - // Check for invalid char before reporting bad length, - // since the invalid char (if present) is an earlier problem. - fromHexChar(src[dst.length * 2]); - throw errLength(); - } - - return dst; -} - -/** - * DecodedLen returns the length of decoding `x` source bytes. - * Specifically, it returns `x / 2`. - * @param x - */ -export function decodedLen(x: number): number { - return x >>> 1; -} - -/** - * DecodeString returns the bytes represented by the hexadecimal string `s`. - * DecodeString expects that src contains only hexadecimal characters and that - * src has even length. - * If the input is malformed, DecodeString will throw an error. - * @param s the `string` to decode to `Uint8Array` - */ -export function decodeString(s: string): Uint8Array { - return decode(new TextEncoder().encode(s)); -} diff --git a/std/encoding/hex_test.ts b/std/encoding/hex_test.ts deleted file mode 100644 index 53161a3ba5..0000000000 --- a/std/encoding/hex_test.ts +++ /dev/null @@ -1,154 +0,0 @@ -// Ported from Go -// https://github.com/golang/go/blob/go1.12.5/src/encoding/hex/hex.go -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals, assertThrows } from "../testing/asserts.ts"; - -import { - decode, - decodedLen, - decodeString, - encode, - encodedLen, - encodeToString, - errInvalidByte, - errLength, -} from "./hex.ts"; - -function toByte(s: string): number { - return new TextEncoder().encode(s)[0]; -} - -const testCases = [ - // encoded(hex) / decoded(Uint8Array) - ["", []], - ["0001020304050607", [0, 1, 2, 3, 4, 5, 6, 7]], - ["08090a0b0c0d0e0f", [8, 9, 10, 11, 12, 13, 14, 15]], - ["f0f1f2f3f4f5f6f7", [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7]], - ["f8f9fafbfcfdfeff", [0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff]], - ["67", Array.from(new TextEncoder().encode("g"))], - ["e3a1", [0xe3, 0xa1]], -]; - -const errCases = [ - // encoded(hex) / error - ["0", errLength()], - ["zd4aa", errInvalidByte(toByte("z"))], - ["d4aaz", errInvalidByte(toByte("z"))], - ["30313", errLength()], - ["0g", errInvalidByte(new TextEncoder().encode("g")[0])], - ["00gg", errInvalidByte(new TextEncoder().encode("g")[0])], - ["0\x01", errInvalidByte(new TextEncoder().encode("\x01")[0])], - ["ffeed", errLength()], -]; - -Deno.test({ - name: "[encoding.hex] encodedLen", - fn(): void { - assertEquals(encodedLen(0), 0); - assertEquals(encodedLen(1), 2); - assertEquals(encodedLen(2), 4); - assertEquals(encodedLen(3), 6); - assertEquals(encodedLen(4), 8); - }, -}); - -Deno.test({ - name: "[encoding.hex] encode", - fn(): void { - { - const srcStr = "abc"; - const src = new TextEncoder().encode(srcStr); - const dest = encode(src); - assertEquals(src, new Uint8Array([97, 98, 99])); - assertEquals(dest.length, 6); - } - - for (const [enc, dec] of testCases) { - const src = new Uint8Array(dec as number[]); - const dest = encode(src); - assertEquals(dest.length, src.length * 2); - assertEquals(new TextDecoder().decode(dest), enc); - } - }, -}); - -Deno.test({ - name: "[encoding.hex] encodeToString", - fn(): void { - for (const [enc, dec] of testCases) { - assertEquals(encodeToString(new Uint8Array(dec as number[])), enc); - } - }, -}); - -Deno.test({ - name: "[encoding.hex] decodedLen", - fn(): void { - assertEquals(decodedLen(0), 0); - assertEquals(decodedLen(2), 1); - assertEquals(decodedLen(4), 2); - assertEquals(decodedLen(6), 3); - assertEquals(decodedLen(8), 4); - }, -}); - -Deno.test({ - name: "[encoding.hex] decode", - fn(): void { - // Case for decoding uppercase hex characters, since - // Encode always uses lowercase. - const extraTestcase = [ - ["F8F9FAFBFCFDFEFF", [0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff]], - ]; - - const cases = testCases.concat(extraTestcase); - - for (const [enc, dec] of cases) { - const src = new TextEncoder().encode(enc as string); - const dest = decode(src); - assertEquals(Array.from(dest), Array.from(dec as number[])); - } - }, -}); - -Deno.test({ - name: "[encoding.hex] decodeString", - fn(): void { - for (const [enc, dec] of testCases) { - const dst = decodeString(enc as string); - - assertEquals(dec, Array.from(dst)); - } - }, -}); - -Deno.test({ - name: "[encoding.hex] decode error", - fn(): void { - for (const [input, expectedErr] of errCases) { - assertThrows( - () => decode(new TextEncoder().encode(input as string)), - Error, - (expectedErr as Error).message, - ); - } - }, -}); - -Deno.test({ - name: "[encoding.hex] decodeString error", - fn(): void { - for (const [input, expectedErr] of errCases) { - assertThrows( - (): void => { - decodeString(input as string); - }, - Error, - (expectedErr as Error).message, - ); - } - }, -}); diff --git a/std/encoding/testdata/CRLF.toml b/std/encoding/testdata/CRLF.toml deleted file mode 100644 index 92264888a2..0000000000 --- a/std/encoding/testdata/CRLF.toml +++ /dev/null @@ -1,3 +0,0 @@ -[boolean] -bool1 = true -bool2 = false \ No newline at end of file diff --git a/std/encoding/testdata/arrayTable.toml b/std/encoding/testdata/arrayTable.toml deleted file mode 100644 index 3788b7e7c4..0000000000 --- a/std/encoding/testdata/arrayTable.toml +++ /dev/null @@ -1,12 +0,0 @@ - -[[bin]] -name = "deno" -path = "cli/main.rs" - -[[bin]] -name = "deno_core" -path = "src/foo.rs" - -[[nib]] -name = "node" -path = "not_found" \ No newline at end of file diff --git a/std/encoding/testdata/arrays.toml b/std/encoding/testdata/arrays.toml deleted file mode 100644 index f52509bf25..0000000000 --- a/std/encoding/testdata/arrays.toml +++ /dev/null @@ -1,8 +0,0 @@ -[arrays] -data = [ ["gamma", "delta"], [1, 2] ] # comment after an array caused issue #7072 - -# Line breaks are OK when inside arrays -hosts = [ - "alpha", - "omega" -] # comment \ No newline at end of file diff --git a/std/encoding/testdata/boolean.toml b/std/encoding/testdata/boolean.toml deleted file mode 100644 index e3e287981b..0000000000 --- a/std/encoding/testdata/boolean.toml +++ /dev/null @@ -1,4 +0,0 @@ -[boolean] # i hate comments -bool1 = true -bool2 = false -bool3 = true # I love comments \ No newline at end of file diff --git a/std/encoding/testdata/cargo.toml b/std/encoding/testdata/cargo.toml deleted file mode 100644 index cb0c85fe67..0000000000 --- a/std/encoding/testdata/cargo.toml +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -# Dummy package info required by `cargo fetch`. - -[workspace] -members = [ - "./", - "core", -] - -[[bin]] -name = "deno" -path = "cli/main.rs" - -[package] -name = "deno" -version = "0.3.4" -edition = "2018" - -[dependencies] -deno_core = { path = "./core" } - -atty = "0.2.11" -dirs = "1.0.5" -flatbuffers = "0.5.0" -futures = "0.1.25" -getopts = "0.2.18" -http = "0.1.16" -hyper = "0.12.24" -hyper-rustls = "0.16.0" -integer-atomics = "1.0.2" -lazy_static = "1.3.0" -libc = "0.2.49" -log = "0.4.6" -rand = "0.6.5" -regex = "1.1.0" -remove_dir_all = "0.5.2" -ring = "0.14.6" -rustyline = "3.0.0" -serde_json = "1.0.38" -source-map-mappings = "0.5.0" -tempfile = "3.0.7" -tokio = "0.1.15" -tokio-executor = "0.1.6" -tokio-fs = "0.1.5" -tokio-io = "0.1.11" -tokio-process = "0.2.3" -tokio-threadpool = "0.1.11" -url = "1.7.2" - -[target.'cfg(windows)'.dependencies] -winapi = "0.3.6" diff --git a/std/encoding/testdata/cargoTest.toml b/std/encoding/testdata/cargoTest.toml deleted file mode 100644 index 47e7f6e4d3..0000000000 --- a/std/encoding/testdata/cargoTest.toml +++ /dev/null @@ -1,147 +0,0 @@ -# This is a TOML document. - -title = "TOML Example" - -[deeply.nested.object.in.the.toml] -name = "Tom Preston-Werner" -dob = 2009-05-27T07:32:00 - -[database] -server = "192.168.1.1" -ports = [ 8001, 8001, 8002 ] -connection_max = 5000 -enabled = true - -[servers] - - # Indentation (tabs and/or spaces) is allowed but not required - [servers.alpha] - ip = "10.0.0.1" - dc = "eqdc10" - - [servers.beta] - ip = "10.0.0.2" - dc = "eqdc10" - -[clients] -data = [ ["gamma", "delta"], [1, 2] ] - -# Line breaks are OK when inside arrays -hosts = [ - "alpha", - "omega" -] - -[strings] -str0 = "deno" -str1 = """ -Roses are red - Violets are blue""" -# On a Unix system, the above multi-line string will most likely be the same as: -str2 = "Roses are red\nViolets are blue" - -# On a Windows system, it will most likely be equivalent to: -str3 = "Roses are red\r\nViolets are blue" -str4 = "The quick brown fox jumps over the lazy dog." -str5 = "this is a \"quote\"" - -str5 = """ -The quick brown \ - - - fox jumps over \ - the lazy dog.""" - -str6 = """\ - The quick brown \ - fox jumps over \ - the lazy dog.\ - """ -lines = ''' -The first newline is -trimmed in raw strings. - All other whitespace - is preserved. -''' - -[Integer] -int1 = +99 -int2 = 42 -int3 = 0 -int4 = -17 -int5 = 1_000 -int6 = 5_349_221 -int7 = 1_2_3_4_5 # VALID but discouraged - -# hexadecimal with prefix `0x` -hex1 = 0xDEADBEEF -hex2 = 0xdeadbeef -hex3 = 0xdead_beef - -# octal with prefix `0o` -oct1 = 0o01234567 -oct2 = 0o755 # useful for Unix file permissions - -# binary with prefix `0b` -bin1 = 0b11010110 - -[Date-Time] -odt1 = 1979-05-27T07:32:00Z -odt2 = 1979-05-27T00:32:00-07:00 -odt3 = 1979-05-27T00:32:00.999999-07:00 -odt4 = 1979-05-27 07:32:00Z -ld1 = 1979-05-27 -lt1 = 07:32:00 #buggy -lt2 = 00:32:00.999999 #buggy - -[boolean] -bool1 = true -bool2 = false - -[float] -# fractional -flt1 = +1.0 -flt2 = 3.1415 -flt3 = -0.01 - -# exponent -flt4 = 5e+22 -flt5 = 1e6 -flt6 = -2E-2 - -# both -flt7 = 6.626e-34 -flt8 = 224_617.445_991_228 -# infinity -sf1 = inf # positive infinity -sf2 = +inf # positive infinity -sf3 = -inf # negative infinity - -# not a number -sf4 = nan # actual sNaN/qNaN encoding is implementation specific -sf5 = +nan # same as `nan` -sf6 = -nan # valid, actual encoding is implementation specific - -[Table] -name = { first = "Tom", last = "Preston-Werner" } -point = { x = 1, y = 2 } -animal = { type.name = "pug" } - -[[fruit]] - name = "apple" - - [fruit.physical] - color = "red" - shape = "round" - - [[fruit.variety]] - name = "red delicious" - - [[fruit.variety]] - name = "granny smith" - -[[fruit]] - name = "banana" - - [[fruit.variety]] - name = "plantain" diff --git a/std/encoding/testdata/comment.toml b/std/encoding/testdata/comment.toml deleted file mode 100644 index 6bc9be0458..0000000000 --- a/std/encoding/testdata/comment.toml +++ /dev/null @@ -1,29 +0,0 @@ -# This is a full-line comment -str0 = 'value' # This is a comment at the end of a line -str1 = "# This is not a comment" # but this is -str2 = """ # this is not a comment! -A multiline string with a # -# this is also not a comment -""" # this is definitely a comment - -str3 = ''' -"# not a comment" - # this is a real tab on purpose -# not a comment -''' # comment - -point0 = { x = 1, y = 2, str0 = "#not a comment", z = 3 } # comment -point1 = { x = 7, y = 8, z = 9, str0 = "#not a comment"} # comment - -[deno] # this comment is fine -features = ["#secure by default", "supports typescript # not a comment"] # Comment caused Issue #7072 -url = "https://deno.land/" # comment -is_not_node = true # comment - -[toml] # Comment caused Issue #7072 (case 2) -name = "Tom's Obvious, Minimal Language" -objectives = [ # Comment - "easy to read", # Comment - "minimal config file", - "#not a comment" # comment -] # comment diff --git a/std/encoding/testdata/datetime.toml b/std/encoding/testdata/datetime.toml deleted file mode 100644 index 62377a4ba2..0000000000 --- a/std/encoding/testdata/datetime.toml +++ /dev/null @@ -1,8 +0,0 @@ -[datetime] -odt1 = 1979-05-27T07:32:00Z # Comment -odt2 = 1979-05-27T00:32:00-07:00 # Comment -odt3 = 1979-05-27T00:32:00.999999-07:00 # Comment -odt4 = 1979-05-27 07:32:00Z # Comment -ld1 = 1979-05-27 # Comment -lt1 = 07:32:00 # Comment -lt2 = 00:32:00.999999 # Comment diff --git a/std/encoding/testdata/float.toml b/std/encoding/testdata/float.toml deleted file mode 100644 index 6a384179cf..0000000000 --- a/std/encoding/testdata/float.toml +++ /dev/null @@ -1,23 +0,0 @@ -[float] -# fractional -flt1 = +1.0 # Comment -flt2 = 3.1415 # Comment -flt3 = -0.01 # Comment - -# exponent -flt4 = 5e+22 # Comment -flt5 = 1e6 # Comment -flt6 = -2E-2 # Comment - -# both -flt7 = 6.626e-34 # Comment -flt8 = 224_617.445_991_228 # Comment -# infinity -sf1 = inf # positive infinity -sf2 = +inf # positive infinity -sf3 = -inf # negative infinity - -# not a number -sf4 = nan # actual sNaN/qNaN encoding is implementation specific -sf5 = +nan # same as `nan` -sf6 = -nan # valid, actual encoding is implementation specific \ No newline at end of file diff --git a/std/encoding/testdata/inlineArrayOfInlineTable.toml b/std/encoding/testdata/inlineArrayOfInlineTable.toml deleted file mode 100644 index a440ff927a..0000000000 --- a/std/encoding/testdata/inlineArrayOfInlineTable.toml +++ /dev/null @@ -1,8 +0,0 @@ -[inlineArray] -string = [ {var = "a string"} ] - -my_points = [ { x = 1, y = 2, z = 3 }, { x = 7, y = 8, z = 9 }, { x = 2, y = 4, z = 8 } ] - -points = [ { x = 1, y = 2, z = 3 }, - { x = 7, y = 8, z = 9 }, - { x = 2, y = 4, z = 8 } ] \ No newline at end of file diff --git a/std/encoding/testdata/inlineTable.toml b/std/encoding/testdata/inlineTable.toml deleted file mode 100644 index 203cb16db7..0000000000 --- a/std/encoding/testdata/inlineTable.toml +++ /dev/null @@ -1,7 +0,0 @@ -[inlinetable] -name = { first = "Tom", last = "Preston-Werner" } -point = { x = 1, y = 2 } -dog = { type = { name = "pug" } } -animal.as.leaders = "tosin" -"tosin.abasi" = "guitarist" -nile = { derek.roddy = "drummer", also = { malevolant.creation = { drum.kit = "Tama" } } } \ No newline at end of file diff --git a/std/encoding/testdata/integer.toml b/std/encoding/testdata/integer.toml deleted file mode 100644 index 3bd781e8f5..0000000000 --- a/std/encoding/testdata/integer.toml +++ /dev/null @@ -1,20 +0,0 @@ -[integer] -int1 = +99 -int2 = 42 -int3 = 0 -int4 = -17 -int5 = 1_000 -int6 = 5_349_221 -int7 = 1_2_3_4_5 # VALID but discouraged - -# hexadecimal with prefix `0x` -hex1 = 0xDEADBEEF -hex2 = 0xdeadbeef -hex3 = 0xdead_beef - -# octal with prefix `0o` -oct1 = 0o01234567 -oct2 = 0o755 # useful for Unix file permissions - -# binary with prefix `0b` -bin1 = 0b11010110 \ No newline at end of file diff --git a/std/encoding/testdata/simple.toml b/std/encoding/testdata/simple.toml deleted file mode 100644 index f3f6c10365..0000000000 --- a/std/encoding/testdata/simple.toml +++ /dev/null @@ -1,5 +0,0 @@ -deno = "is" -not = "[node]" -regex = '<\i\c*\s*>' -NANI = '何?!' -comment = "Comment inside # the comment" # Comment diff --git a/std/encoding/testdata/string.toml b/std/encoding/testdata/string.toml deleted file mode 100644 index 640717d0eb..0000000000 --- a/std/encoding/testdata/string.toml +++ /dev/null @@ -1,36 +0,0 @@ -[strings] -str0 = "deno" -str1 = """ -Roses are not Deno - Violets are not Deno either""" -# On a Unix system, the above multi-line string will most likely be the same as: -str2 = "Roses are not Deno\nViolets are not Deno either" - -# On a Windows system, it will most likely be equivalent to: -str3 = "Roses are not Deno\r\nViolets are not Deno either" -str4 = "this is a \"quote\"" - -str5 = """ -The quick brown \ - - - fox jumps over \ - the lazy dog.""" - -str6 = """\ - The quick brown \ - fox jumps over \ - the lazy dog.\ - """ -lines = ''' -The first newline is -trimmed in raw strings. - All other whitespace - is preserved. -''' - -withApostrophe = "What if it's not?" -withSemicolon = "const message = 'hello world';" -withHexNumberLiteral = "Prevent bug from stripping string here ->0xabcdef" -withUnicodeChar1 = "\u3042" -withUnicodeChar2 = "Deno\U01F995" diff --git a/std/encoding/testdata/table.toml b/std/encoding/testdata/table.toml deleted file mode 100644 index 7008e6fb01..0000000000 --- a/std/encoding/testdata/table.toml +++ /dev/null @@ -1,13 +0,0 @@ -[deeply.nested.object.in.the.toml] -name = "Tom Preston-Werner" - -[servers] - - # Indentation (tabs and/or spaces) is allowed but not required - [servers.alpha] - ip = "10.0.0.1" - dc = "eqdc10" - - [servers.beta] - ip = "10.0.0.2" - dc = "eqdc20" \ No newline at end of file diff --git a/std/encoding/toml.ts b/std/encoding/toml.ts deleted file mode 100644 index 6cd8faa71a..0000000000 --- a/std/encoding/toml.ts +++ /dev/null @@ -1,736 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { deepAssign } from "../_util/deep_assign.ts"; -import { assert } from "../_util/assert.ts"; - -class TOMLError extends Error {} - -class KeyValuePair { - constructor(public key: string, public value: unknown) {} -} - -class ParserGroup { - arrValues: unknown[] = []; - objValues: Record = {}; - - constructor(public type: string, public name: string) {} -} - -class ParserContext { - currentGroup?: ParserGroup; - output: Record = {}; -} - -class Parser { - tomlLines: string[]; - context: ParserContext; - constructor(tomlString: string) { - this.tomlLines = this._split(tomlString); - this.context = new ParserContext(); - } - _sanitize(): void { - const out: string[] = []; - for (let i = 0; i < this.tomlLines.length; i++) { - const s = this.tomlLines[i]; - const trimmed = s.trim(); - if (trimmed !== "") { - out.push(s); - } - } - this.tomlLines = out; - this._removeComments(); - this._mergeMultilines(); - } - - _removeComments(): void { - function isFullLineComment(line: string) { - return line.match(/^#/) ? true : false; - } - - function stringStart(line: string) { - const m = line.match(/(?:=\s*\[?\s*)("""|'''|"|')/); - if (!m) { - return false; - } - - // We want to know which syntax was used to open the string - openStringSyntax = m[1]; - return true; - } - - function stringEnd(line: string) { - // match the syntax used to open the string when searching for string close - // e.g. if we open with ''' we must close with a ''' - const reg = RegExp(`(? | unknown[] = {}, - cObj: Record | unknown[] = {}, - ): Record { - const out: Record = {}; - if (keys.length === 0) { - return cObj as Record; - } else { - if (Object.keys(cObj).length === 0) { - cObj = values; - } - const key: string | undefined = keys.pop(); - if (key) { - out[key] = cObj; - } - return this._unflat(keys, values, out); - } - } - _groupToOutput(): void { - assert(this.context.currentGroup != null, "currentGroup must be set"); - const arrProperty = this.context.currentGroup.name - .replace(/"/g, "") - .replace(/'/g, "") - .split("."); - let u = {}; - if (this.context.currentGroup.type === "array") { - u = this._unflat(arrProperty, this.context.currentGroup.arrValues); - } else { - u = this._unflat(arrProperty, this.context.currentGroup.objValues); - } - deepAssign(this.context.output, u); - delete this.context.currentGroup; - } - _split(str: string): string[] { - const out = []; - out.push(...str.split("\n")); - return out; - } - _isGroup(line: string): boolean { - const t = line.trim(); - return t[0] === "[" && /\[(.*)\]/.exec(t) ? true : false; - } - _isDeclaration(line: string): boolean { - return line.split("=").length > 1; - } - _createGroup(line: string): void { - const captureReg = /\[(.*)\]/; - if (this.context.currentGroup) { - this._groupToOutput(); - } - - let type; - let m = line.match(captureReg); - assert(m != null, "line mut be matched"); - let name = m[1]; - if (name.match(/\[.*\]/)) { - type = "array"; - m = name.match(captureReg); - assert(m != null, "name must be matched"); - name = m[1]; - } else { - type = "object"; - } - this.context.currentGroup = new ParserGroup(type, name); - } - _processDeclaration(line: string): KeyValuePair { - const idx = line.indexOf("="); - const key = line.substring(0, idx).trim(); - const value = this._parseData(line.slice(idx + 1)); - return new KeyValuePair(key, value); - } - _parseData(dataString: string): unknown { - dataString = dataString.trim(); - switch (dataString[0]) { - case '"': - case "'": - return this._parseString(dataString); - case "[": - case "{": - return this._parseInlineTableOrArray(dataString); - default: { - // Strip a comment. - const match = /#.*$/.exec(dataString); - if (match) { - dataString = dataString.slice(0, match.index).trim(); - } - - switch (dataString) { - case "true": - return true; - case "false": - return false; - case "inf": - case "+inf": - return Infinity; - case "-inf": - return -Infinity; - case "nan": - case "+nan": - case "-nan": - return NaN; - default: - return this._parseNumberOrDate(dataString); - } - } - } - } - _parseInlineTableOrArray(dataString: string): unknown { - const invalidArr = /,\]/g.exec(dataString); - if (invalidArr) { - dataString = dataString.replace(/,]/g, "]"); - } - - if ( - (dataString[0] === "{" && dataString[dataString.length - 1] === "}") || - (dataString[0] === "[" && dataString[dataString.length - 1] === "]") - ) { - const reg = /([a-zA-Z0-9-_\.]*) (=)/gi; - let result; - while ((result = reg.exec(dataString))) { - const ogVal = result[0]; - const newVal = ogVal - .replace(result[1], `"${result[1]}"`) - .replace(result[2], ":"); - dataString = dataString.replace(ogVal, newVal); - } - return JSON.parse(dataString); - } - throw new TOMLError("Malformed inline table or array literal"); - } - _parseString(dataString: string): string { - const quote = dataString[0]; - // Handle First and last EOL for multiline strings - if (dataString.startsWith(`"\\n`)) { - dataString = dataString.replace(`"\\n`, `"`); - } else if (dataString.startsWith(`'\\n`)) { - dataString = dataString.replace(`'\\n`, `'`); - } - if (dataString.endsWith(`\\n"`)) { - dataString = dataString.replace(`\\n"`, `"`); - } else if (dataString.endsWith(`\\n'`)) { - dataString = dataString.replace(`\\n'`, `'`); - } - let value = ""; - for (let i = 1; i < dataString.length; i++) { - switch (dataString[i]) { - case "\\": - i++; - // See https://toml.io/en/v1.0.0-rc.3#string - switch (dataString[i]) { - case "b": - value += "\b"; - break; - case "t": - value += "\t"; - break; - case "n": - value += "\n"; - break; - case "f": - value += "\f"; - break; - case "r": - value += "\r"; - break; - case "u": - case "U": { - // Unicode character - const codePointLen = dataString[i] === "u" ? 4 : 6; - const codePoint = parseInt( - "0x" + dataString.slice(i + 1, i + 1 + codePointLen), - 16, - ); - value += String.fromCodePoint(codePoint); - i += codePointLen; - break; - } - case "\\": - value += "\\"; - break; - default: - value += dataString[i]; - break; - } - break; - case quote: - if (dataString[i - 1] !== "\\") { - return value; - } - break; - default: - value += dataString[i]; - break; - } - } - throw new TOMLError("Incomplete string literal"); - } - _parseNumberOrDate(dataString: string): unknown { - if (this._isDate(dataString)) { - return new Date(dataString); - } - - if (this._isLocalTime(dataString)) { - return dataString; - } - - // If binary / octal / hex - const hex = /^(0(?:x|o|b)[0-9a-f_]*)/gi.exec(dataString); - if (hex && hex[0]) { - return hex[0].trim(); - } - - const testNumber = this._isParsableNumber(dataString); - if (testNumber !== false && !isNaN(testNumber as number)) { - return testNumber; - } - - return String(dataString); - } - _isLocalTime(str: string): boolean { - const reg = /(\d{2}):(\d{2}):(\d{2})/; - return reg.test(str); - } - _isParsableNumber(dataString: string): number | boolean { - const m = /((?:\+|-|)[0-9_\.e+\-]*)[^#]/i.exec(dataString); - if (!m) { - return false; - } else { - return parseFloat(m[0].replace(/_/g, "")); - } - } - _isDate(dateStr: string): boolean { - const reg = /\d{4}-\d{2}-\d{2}/; - return reg.test(dateStr); - } - _parseDeclarationName(declaration: string): string[] { - const out = []; - let acc = []; - let inLiteral = false; - for (let i = 0; i < declaration.length; i++) { - const c = declaration[i]; - switch (c) { - case ".": - if (!inLiteral) { - out.push(acc.join("")); - acc = []; - } else { - acc.push(c); - } - break; - case `"`: - if (inLiteral) { - inLiteral = false; - } else { - inLiteral = true; - } - break; - default: - acc.push(c); - break; - } - } - if (acc.length !== 0) { - out.push(acc.join("")); - } - return out; - } - _parseLines(): void { - for (let i = 0; i < this.tomlLines.length; i++) { - const line = this.tomlLines[i]; - - // TODO(zekth): Handle unflat of array of tables - if (this._isGroup(line)) { - // if the current group is an array we push the - // parsed objects in it. - if ( - this.context.currentGroup && - this.context.currentGroup.type === "array" - ) { - this.context.currentGroup.arrValues.push( - this.context.currentGroup.objValues, - ); - this.context.currentGroup.objValues = {}; - } - // If we need to create a group or to change group - if ( - !this.context.currentGroup || - (this.context.currentGroup && - this.context.currentGroup.name !== - line.replace(/\[/g, "").replace(/\]/g, "")) - ) { - this._createGroup(line); - continue; - } - } - if (this._isDeclaration(line)) { - const kv = this._processDeclaration(line); - const key = kv.key; - const value = kv.value; - if (!this.context.currentGroup) { - this.context.output[key] = value; - } else { - this.context.currentGroup.objValues[key] = value; - } - } - } - if (this.context.currentGroup) { - if (this.context.currentGroup.type === "array") { - this.context.currentGroup.arrValues.push( - this.context.currentGroup.objValues, - ); - } - this._groupToOutput(); - } - } - _cleanOutput(): void { - this._propertyClean(this.context.output); - } - _propertyClean(obj: Record): void { - const keys = Object.keys(obj); - for (let i = 0; i < keys.length; i++) { - let k = keys[i]; - if (k) { - let v = obj[k]; - const pathDeclaration = this._parseDeclarationName(k); - delete obj[k]; - if (pathDeclaration.length > 1) { - const shift = pathDeclaration.shift(); - if (shift) { - k = shift.replace(/"/g, ""); - v = this._unflat(pathDeclaration, v as Record); - } - } else { - k = k.replace(/"/g, ""); - } - obj[k] = v; - if (v instanceof Object) { - // deno-lint-ignore no-explicit-any - this._propertyClean(v as any); - } - } - } - } - parse(): Record { - this._sanitize(); - this._parseLines(); - this._cleanOutput(); - return this.context.output; - } -} - -// Bare keys may only contain ASCII letters, -// ASCII digits, underscores, and dashes (A-Za-z0-9_-). -function joinKeys(keys: string[]): string { - // Dotted keys are a sequence of bare or quoted keys joined with a dot. - // This allows for grouping similar properties together: - return keys - .map((str: string): string => { - return str.match(/[^A-Za-z0-9_-]/) ? `"${str}"` : str; - }) - .join("."); -} - -class Dumper { - maxPad = 0; - srcObject: Record; - output: string[] = []; - constructor(srcObjc: Record) { - this.srcObject = srcObjc; - } - dump(): string[] { - // deno-lint-ignore no-explicit-any - this.output = this._parse(this.srcObject as any); - this.output = this._format(); - return this.output; - } - _parse(obj: Record, keys: string[] = []): string[] { - const out = []; - const props = Object.keys(obj); - const propObj = props.filter((e: string): boolean => { - if (obj[e] instanceof Array) { - const d: unknown[] = obj[e] as unknown[]; - return !this._isSimplySerializable(d[0]); - } - return !this._isSimplySerializable(obj[e]); - }); - const propPrim = props.filter((e: string): boolean => { - if (obj[e] instanceof Array) { - const d: unknown[] = obj[e] as unknown[]; - return this._isSimplySerializable(d[0]); - } - return this._isSimplySerializable(obj[e]); - }); - const k = propPrim.concat(propObj); - for (let i = 0; i < k.length; i++) { - const prop = k[i]; - const value = obj[prop]; - if (value instanceof Date) { - out.push(this._dateDeclaration([prop], value)); - } else if (typeof value === "string" || value instanceof RegExp) { - out.push(this._strDeclaration([prop], value.toString())); - } else if (typeof value === "number") { - out.push(this._numberDeclaration([prop], value)); - } else if (typeof value === "boolean") { - out.push(this._boolDeclaration([prop], value)); - } else if ( - value instanceof Array && - this._isSimplySerializable(value[0]) - ) { - // only if primitives types in the array - out.push(this._arrayDeclaration([prop], value)); - } else if ( - value instanceof Array && - !this._isSimplySerializable(value[0]) - ) { - // array of objects - for (let i = 0; i < value.length; i++) { - out.push(""); - out.push(this._headerGroup([...keys, prop])); - out.push(...this._parse(value[i], [...keys, prop])); - } - } else if (typeof value === "object") { - out.push(""); - out.push(this._header([...keys, prop])); - if (value) { - const toParse = value as Record; - out.push(...this._parse(toParse, [...keys, prop])); - } - // out.push(...this._parse(value, `${path}${prop}.`)); - } - } - out.push(""); - return out; - } - _isSimplySerializable(value: unknown): boolean { - return ( - typeof value === "string" || - typeof value === "number" || - typeof value === "boolean" || - value instanceof RegExp || - value instanceof Date || - value instanceof Array - ); - } - _header(keys: string[]): string { - return `[${joinKeys(keys)}]`; - } - _headerGroup(keys: string[]): string { - return `[[${joinKeys(keys)}]]`; - } - _declaration(keys: string[]): string { - const title = joinKeys(keys); - if (title.length > this.maxPad) { - this.maxPad = title.length; - } - return `${title} = `; - } - _arrayDeclaration(keys: string[], value: unknown[]): string { - return `${this._declaration(keys)}${JSON.stringify(value)}`; - } - _strDeclaration(keys: string[], value: string): string { - return `${this._declaration(keys)}"${value}"`; - } - _numberDeclaration(keys: string[], value: number): string { - switch (value) { - case Infinity: - return `${this._declaration(keys)}inf`; - case -Infinity: - return `${this._declaration(keys)}-inf`; - default: - return `${this._declaration(keys)}${value}`; - } - } - _boolDeclaration(keys: string[], value: boolean): string { - return `${this._declaration(keys)}${value}`; - } - _dateDeclaration(keys: string[], value: Date): string { - function dtPad(v: string, lPad = 2): string { - return v.padStart(lPad, "0"); - } - const m = dtPad((value.getUTCMonth() + 1).toString()); - const d = dtPad(value.getUTCDate().toString()); - const h = dtPad(value.getUTCHours().toString()); - const min = dtPad(value.getUTCMinutes().toString()); - const s = dtPad(value.getUTCSeconds().toString()); - const ms = dtPad(value.getUTCMilliseconds().toString(), 3); - // formatted date - const fData = `${value.getUTCFullYear()}-${m}-${d}T${h}:${min}:${s}.${ms}`; - return `${this._declaration(keys)}${fData}`; - } - _format(): string[] { - const rDeclaration = /(.*)\s=/; - const out = []; - for (let i = 0; i < this.output.length; i++) { - const l = this.output[i]; - // we keep empty entry for array of objects - if (l[0] === "[" && l[1] !== "[") { - // empty object - if (this.output[i + 1] === "") { - i += 1; - continue; - } - out.push(l); - } else { - const m = rDeclaration.exec(l); - if (m) { - out.push(l.replace(m[1], m[1].padEnd(this.maxPad))); - } else { - out.push(l); - } - } - } - // Cleaning multiple spaces - const cleanedOutput = []; - for (let i = 0; i < out.length; i++) { - const l = out[i]; - if (!(l === "" && out[i + 1] === "")) { - cleanedOutput.push(l); - } - } - return cleanedOutput; - } -} - -/** - * Stringify dumps source object into TOML string and returns it. - * @param srcObj - */ -export function stringify(srcObj: Record): string { - return new Dumper(srcObj).dump().join("\n"); -} - -/** - * Parse parses TOML string into an object. - * @param tomlString - */ -export function parse(tomlString: string): Record { - // File is potentially using EOL CRLF - tomlString = tomlString.replace(/\r\n/g, "\n").replace(/\\\n/g, "\n"); - return new Parser(tomlString).parse(); -} diff --git a/std/encoding/toml_test.ts b/std/encoding/toml_test.ts deleted file mode 100644 index ab28fa49f8..0000000000 --- a/std/encoding/toml_test.ts +++ /dev/null @@ -1,479 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { existsSync } from "../fs/exists.ts"; -import * as path from "../path/mod.ts"; -import { parse, stringify } from "./toml.ts"; - -const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); -const testdataDir = path.resolve(moduleDir, "testdata"); - -function parseFile(filePath: string): Record { - if (!existsSync(filePath)) { - throw new Error(`File not found: ${filePath}`); - } - return parse(Deno.readTextFileSync(filePath)); -} - -Deno.test({ - name: "[TOML] Strings", - fn(): void { - const expected = { - strings: { - str0: "deno", - str1: "Roses are not Deno\nViolets are not Deno either", - str2: "Roses are not Deno\nViolets are not Deno either", - str3: "Roses are not Deno\r\nViolets are not Deno either", - str4: 'this is a "quote"', - str5: "The quick brown\nfox jumps over\nthe lazy dog.", - str6: "The quick brown\nfox jumps over\nthe lazy dog.", - lines: "The first newline is\ntrimmed in raw strings.\n All other " + - "whitespace\n is preserved.", - withApostrophe: "What if it's not?", - withSemicolon: `const message = 'hello world';`, - withHexNumberLiteral: - "Prevent bug from stripping string here ->0xabcdef", - withUnicodeChar1: "あ", - withUnicodeChar2: "Deno🦕", - }, - }; - const actual = parseFile(path.join(testdataDir, "string.toml")); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] CRLF", - fn(): void { - const expected = { boolean: { bool1: true, bool2: false } }; - const actual = parseFile(path.join(testdataDir, "CRLF.toml")); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Boolean", - fn(): void { - const expected = { boolean: { bool1: true, bool2: false, bool3: true } }; - const actual = parseFile(path.join(testdataDir, "boolean.toml")); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Integer", - fn(): void { - const expected = { - integer: { - int1: 99, - int2: 42, - int3: 0, - int4: -17, - int5: 1000, - int6: 5349221, - int7: 12345, - hex1: "0xDEADBEEF", - hex2: "0xdeadbeef", - hex3: "0xdead_beef", - oct1: "0o01234567", - oct2: "0o755", - bin1: "0b11010110", - }, - }; - const actual = parseFile(path.join(testdataDir, "integer.toml")); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Float", - fn(): void { - const expected = { - float: { - flt1: 1.0, - flt2: 3.1415, - flt3: -0.01, - flt4: 5e22, - flt5: 1e6, - flt6: -2e-2, - flt7: 6.626e-34, - flt8: 224_617.445_991_228, - sf1: Infinity, - sf2: Infinity, - sf3: -Infinity, - sf4: NaN, - sf5: NaN, - sf6: NaN, - }, - }; - const actual = parseFile(path.join(testdataDir, "float.toml")); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Arrays", - fn(): void { - const expected = { - arrays: { - data: [ - ["gamma", "delta"], - [1, 2], - ], - hosts: ["alpha", "omega"], - }, - }; - const actual = parseFile(path.join(testdataDir, "arrays.toml")); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Table", - fn(): void { - const expected = { - deeply: { - nested: { - object: { - in: { - the: { - toml: { - name: "Tom Preston-Werner", - }, - }, - }, - }, - }, - }, - servers: { - alpha: { - ip: "10.0.0.1", - dc: "eqdc10", - }, - beta: { - ip: "10.0.0.2", - dc: "eqdc20", - }, - }, - }; - const actual = parseFile(path.join(testdataDir, "table.toml")); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Simple", - fn(): void { - const expected = { - deno: "is", - not: "[node]", - regex: "", - NANI: "何?!", - comment: "Comment inside # the comment", - }; - const actual = parseFile(path.join(testdataDir, "simple.toml")); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Datetime", - fn(): void { - const expected = { - datetime: { - odt1: new Date("1979-05-27T07:32:00Z"), - odt2: new Date("1979-05-27T00:32:00-07:00"), - odt3: new Date("1979-05-27T00:32:00.999999-07:00"), - odt4: new Date("1979-05-27 07:32:00Z"), - ld1: new Date("1979-05-27"), - lt1: "07:32:00", - lt2: "00:32:00.999999", - }, - }; - const actual = parseFile(path.join(testdataDir, "datetime.toml")); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Inline Table", - fn(): void { - const expected = { - inlinetable: { - nile: { - also: { - malevolant: { - creation: { - drum: { - kit: "Tama", - }, - }, - }, - }, - derek: { - roddy: "drummer", - }, - }, - name: { - first: "Tom", - last: "Preston-Werner", - }, - point: { - x: 1, - y: 2, - }, - dog: { - type: { - name: "pug", - }, - }, - "tosin.abasi": "guitarist", - animal: { - as: { - leaders: "tosin", - }, - }, - }, - }; - const actual = parseFile(path.join(testdataDir, "inlineTable.toml")); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Array of Tables", - fn(): void { - const expected = { - bin: [ - { name: "deno", path: "cli/main.rs" }, - { name: "deno_core", path: "src/foo.rs" }, - ], - nib: [{ name: "node", path: "not_found" }], - }; - const actual = parseFile(path.join(testdataDir, "arrayTable.toml")); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Cargo", - fn(): void { - const expected = { - workspace: { members: ["./", "core"] }, - bin: [{ name: "deno", path: "cli/main.rs" }], - package: { name: "deno", version: "0.3.4", edition: "2018" }, - dependencies: { - deno_core: { path: "./core" }, - atty: "0.2.11", - dirs: "1.0.5", - flatbuffers: "0.5.0", - futures: "0.1.25", - getopts: "0.2.18", - http: "0.1.16", - hyper: "0.12.24", - "hyper-rustls": "0.16.0", - "integer-atomics": "1.0.2", - lazy_static: "1.3.0", - libc: "0.2.49", - log: "0.4.6", - rand: "0.6.5", - regex: "1.1.0", - remove_dir_all: "0.5.2", - ring: "0.14.6", - rustyline: "3.0.0", - serde_json: "1.0.38", - "source-map-mappings": "0.5.0", - tempfile: "3.0.7", - tokio: "0.1.15", - "tokio-executor": "0.1.6", - "tokio-fs": "0.1.5", - "tokio-io": "0.1.11", - "tokio-process": "0.2.3", - "tokio-threadpool": "0.1.11", - url: "1.7.2", - }, - target: { "cfg(windows)": { dependencies: { winapi: "0.3.6" } } }, - }; - const actual = parseFile(path.join(testdataDir, "cargo.toml")); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Stringify", - fn(): void { - const src = { - foo: { bar: "deno" }, - this: { is: { nested: "denonono" } }, - "https://deno.land/std": { - $: "doller", - }, - "##": { - deno: { - "https://deno.land": { - proto: "https", - ":80": "port", - }, - }, - }, - arrayObjects: [{ stuff: "in" }, {}, { the: "array" }], - deno: "is", - not: "[node]", - regex: "", - NANI: "何?!", - comment: "Comment inside # the comment", - int1: 99, - int2: 42, - int3: 0, - int4: -17, - int5: 1000, - int6: 5349221, - int7: 12345, - flt1: 1.0, - flt2: 3.1415, - flt3: -0.01, - flt4: 5e22, - flt5: 1e6, - flt6: -2e-2, - flt7: 6.626e-34, - odt1: new Date("1979-05-01T07:32:00Z"), - odt2: new Date("1979-05-27T00:32:00-07:00"), - odt3: new Date("1979-05-27T00:32:00.999999-07:00"), - odt4: new Date("1979-05-27 07:32:00Z"), - ld1: new Date("1979-05-27"), - reg: /foo[bar]/, - sf1: Infinity, - sf2: Infinity, - sf3: -Infinity, - sf4: NaN, - sf5: NaN, - sf6: NaN, - data: [ - ["gamma", "delta"], - [1, 2], - ], - hosts: ["alpha", "omega"], - bool: true, - bool2: false, - }; - const expected = `deno = "is" -not = "[node]" -regex = "" -NANI = "何?!" -comment = "Comment inside # the comment" -int1 = 99 -int2 = 42 -int3 = 0 -int4 = -17 -int5 = 1000 -int6 = 5349221 -int7 = 12345 -flt1 = 1 -flt2 = 3.1415 -flt3 = -0.01 -flt4 = 5e+22 -flt5 = 1000000 -flt6 = -0.02 -flt7 = 6.626e-34 -odt1 = 1979-05-01T07:32:00.000 -odt2 = 1979-05-27T07:32:00.000 -odt3 = 1979-05-27T07:32:00.999 -odt4 = 1979-05-27T07:32:00.000 -ld1 = 1979-05-27T00:00:00.000 -reg = "/foo[bar]/" -sf1 = inf -sf2 = inf -sf3 = -inf -sf4 = NaN -sf5 = NaN -sf6 = NaN -data = [["gamma","delta"],[1,2]] -hosts = ["alpha","omega"] -bool = true -bool2 = false - -[foo] -bar = "deno" - -[this.is] -nested = "denonono" - -["https://deno.land/std"] -"$" = "doller" - -["##".deno."https://deno.land"] -proto = "https" -":80" = "port" - -[[arrayObjects]] -stuff = "in" - -[[arrayObjects]] - -[[arrayObjects]] -the = "array" -`; - const actual = stringify(src); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Comments", - fn: () => { - const expected = { - str0: "value", - str1: "# This is not a comment", - str2: - " # this is not a comment!\nA multiline string with a #\n# this is also not a comment", - str3: - '"# not a comment"\n\t# this is a real tab on purpose \n# not a comment', - point0: { x: 1, y: 2, str0: "#not a comment", z: 3 }, - point1: { x: 7, y: 8, z: 9, str0: "#not a comment" }, - deno: { - features: ["#secure by default", "supports typescript # not a comment"], - url: "https://deno.land/", - is_not_node: true, - }, - toml: { - name: "Tom's Obvious, Minimal Language", - objectives: ["easy to read", "minimal config file", "#not a comment"], - }, - }; - const actual = parseFile(path.join(testdataDir, "comment.toml")); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Inline Array of Inline Table", - fn(): void { - const expected = { - inlineArray: { - string: [{ var: "a string" }], - my_points: [ - { x: 1, y: 2, z: 3 }, - { x: 7, y: 8, z: 9 }, - { x: 2, y: 4, z: 8 }, - ], - points: [ - { x: 1, y: 2, z: 3 }, - { x: 7, y: 8, z: 9 }, - { x: 2, y: 4, z: 8 }, - ], - }, - }; - const actual = parseFile( - path.join(testdataDir, "inlineArrayOfInlineTable.toml"), - ); - assertEquals(actual, expected); - }, -}); - -Deno.test({ - name: "[TOML] Parse malformed local time as String (#8433)", - fn(): void { - const expected = { sign: "2020-01-01x" }; - const actual = parse(`sign='2020-01-01x'`); - assertEquals(actual, expected); - }, -}); diff --git a/std/encoding/utf8.ts b/std/encoding/utf8.ts deleted file mode 100644 index 6951e37f0b..0000000000 --- a/std/encoding/utf8.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -/** A default TextEncoder instance */ -export const encoder = new TextEncoder(); - -/** Shorthand for new TextEncoder().encode() */ -export function encode(input?: string): Uint8Array { - return encoder.encode(input); -} - -/** A default TextDecoder instance */ -export const decoder = new TextDecoder(); - -/** Shorthand for new TextDecoder().decode() */ -export function decode(input?: Uint8Array): string { - return decoder.decode(input); -} diff --git a/std/encoding/yaml.ts b/std/encoding/yaml.ts deleted file mode 100644 index 9566652440..0000000000 --- a/std/encoding/yaml.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -export type { ParseOptions } from "./_yaml/parse.ts"; -export { parse, parseAll } from "./_yaml/parse.ts"; -export type { DumpOptions as StringifyOptions } from "./_yaml/stringify.ts"; -export { stringify } from "./_yaml/stringify.ts"; -export type { SchemaDefinition } from "./_yaml/schema.ts"; -export type { StyleVariant } from "./_yaml/type.ts"; -export { - CORE_SCHEMA, - DEFAULT_SCHEMA, - FAILSAFE_SCHEMA, - JSON_SCHEMA, -} from "./_yaml/schema/mod.ts"; diff --git a/std/encoding/yaml_test.ts b/std/encoding/yaml_test.ts deleted file mode 100644 index 90ca9924fc..0000000000 --- a/std/encoding/yaml_test.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import "./_yaml/parse_test.ts"; -import "./_yaml/stringify_test.ts"; - -// Type check. -import "./yaml.ts"; diff --git a/std/examples/README.md b/std/examples/README.md deleted file mode 100644 index cdc477833c..0000000000 --- a/std/examples/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# Deno example programs - -This module contains small scripts that demonstrate use of Deno and its standard -module. - -You can run these examples using just their URL or install the example as an -executable script which references the URL. (Think of installing as creating a -bookmark to a program.) - -### A TCP echo server - -```shell -deno run --allow-net https://deno.land/std/examples/echo_server.ts -``` - -Or - -```shell -deno install --allow-net https://deno.land/std/examples/echo_server.ts -``` - -### cat - print file to standard output - -```shell -deno install --allow-read -n deno_cat https://deno.land/std/examples/cat.ts -deno_cat file.txt -``` - -### catj - print flattened JSON to standard output - -A very useful command by Soheil Rashidi ported to Deno. - -```shell -deno install --allow-read https://deno.land/std/examples/catj.ts -catj example.json -catj file1.json file2.json -echo example.json | catj - -``` - -### curl - print the contents of a url to standard output - -```shell -deno run --allow-net=deno.land https://deno.land/std/examples/curl.ts https://deno.land/ -``` - -### gist - easily create and upload Gists - -``` -export GIST_TOKEN=ABC # Generate at https://github.com/settings/tokens -deno install --allow-net --allow-env https://deno.land/std/examples/gist.ts -gist --title "Example gist 1" script.ts -gist --t "Example gist 2" script2.ts -``` - -### chat - WebSocket chat server and browser client - -```shell -deno run --allow-net --allow-read https://deno.land/std/examples/chat/server.ts -``` - -Open http://localhost:8080 on the browser. diff --git a/std/examples/cat.ts b/std/examples/cat.ts deleted file mode 100644 index 04c40034bf..0000000000 --- a/std/examples/cat.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -const filenames = Deno.args; -for (const filename of filenames) { - const file = await Deno.open(filename); - await Deno.copy(file, Deno.stdout); - file.close(); -} diff --git a/std/examples/cat_test.ts b/std/examples/cat_test.ts deleted file mode 100644 index 9664ff713c..0000000000 --- a/std/examples/cat_test.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertStrictEquals } from "../testing/asserts.ts"; -import { dirname, fromFileUrl } from "../path/mod.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); - -Deno.test("[examples/cat] print multiple files", async () => { - const decoder = new TextDecoder(); - const process = Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - "--allow-read", - "cat.ts", - "testdata/cat/hello.txt", - "testdata/cat/world.txt", - ], - cwd: moduleDir, - stdout: "piped", - }); - - try { - const output = await process.output(); - const actual = decoder.decode(output).trim(); - assertStrictEquals(actual, "Hello\nWorld"); - } finally { - process.close(); - } -}); diff --git a/std/examples/catj.ts b/std/examples/catj.ts deleted file mode 100644 index 907f148797..0000000000 --- a/std/examples/catj.ts +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env -S deno run --allow-read -// Ported from: https://github.com/soheilpro/catj -// Copyright (c) 2014 Soheil Rashidi -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -// Install using `deno install` -// $ deno install --allow-read https://deno.land/std/examples/catj.ts - -import { parse } from "../flags/mod.ts"; -import * as colors from "../fmt/colors.ts"; - -const decoder = new TextDecoder(); - -// deno-lint-ignore no-explicit-any -function isObject(arg: any): arg is Record { - return !!arg && arg.constructor === Object; -} - -function isValidIdentifier(value: string): boolean { - // eslint-disable-next-line max-len - return /^(?!(?:do|if|in|for|let|new|try|var|case|else|enum|eval|null|this|true|void|with|break|catch|class|const|false|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$)[\x24A-Z\x5Fa-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC][\x240-9A-Z\x5Fa-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0\u08A2-\u08AC\u08E4-\u08FE\u0900-\u0963\u0966-\u096F\u0971-\u0977\u0979-\u097F\u0981-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58\u0C59\u0C60-\u0C63\u0C66-\u0C6F\u0C82\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D02\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D60-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191C\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1D00-\u1DE6\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA697\uA69F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A\uAA7B\uAA80-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE26\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]*$/ - .test( - value, - ); -} - -function printValue(value: unknown, path: string): void { - if (typeof value === "string") { - value = colors.green('"' + value + '"'); - } else if (typeof value === "number") { - value = value.toString(); - } else if (typeof value === "boolean") { - value = colors.yellow(value.toString()); - } - - console.log(path + " = " + value); -} - -function printObject(obj: Record, path: string): void { - for (const key of Object.keys(obj)) { - const value = obj[key]; - let nodePath = path + colors.cyan(".") + key; - - if (!isValidIdentifier(key)) { - nodePath = (path ? path : colors.cyan(".")) + '["' + key + '"]'; - } - - if (Array.isArray(value)) { - printArray(value, nodePath); - } else if (isObject(value)) { - printObject(value, nodePath); - } else { - printValue(value, nodePath); - } - } -} - -function printArray(array: unknown[], path: string): void { - for (const index in array) { - const value = array[index]; - const nodePath = (path ? path : colors.cyan(".")) + "[" + - colors.cyan(index) + "]"; - - if (Array.isArray(value)) { - printArray(value, nodePath); - } else if (isObject(value)) { - printObject(value, nodePath); - } else { - printValue(value, nodePath); - } - } -} - -// deno-lint-ignore no-explicit-any -function print(data: any[] | Record): void { - if (Array.isArray(data)) { - printArray(data, ""); - } else { - printObject(data, ""); - } -} - -if (import.meta.main) { - const parsedArgs = parse(Deno.args); - - if (parsedArgs.h || parsedArgs.help || parsedArgs._.length === 0) { - console.log("Usage: catj [-h|--help] [file...]"); - console.log(); - console.log("Examples:"); - console.log(); - console.log("// print file:\n catj file.json"); - console.log(); - console.log("// print multiple files:\n catj file1.json file2.json"); - console.log(); - console.log("// print from stdin:\n cat file.json | catj -"); - } - - if (parsedArgs._[0] === "-") { - const contents = await Deno.readAll(Deno.stdin); - const json = JSON.parse(decoder.decode(contents)); - print(json); - } else { - for (const fileName of parsedArgs._) { - const fileContents = await Deno.readFile(fileName.toString()); - const json = JSON.parse(decoder.decode(fileContents)); - print(json); - } - } -} diff --git a/std/examples/catj_test.ts b/std/examples/catj_test.ts deleted file mode 100644 index 365b5c0b7e..0000000000 --- a/std/examples/catj_test.ts +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertStrictEquals } from "../testing/asserts.ts"; -import { dirname, fromFileUrl } from "../path/mod.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); - -Deno.test("[examples/catj] print an array", async () => { - const decoder = new TextDecoder(); - const process = catj("testdata/catj/array.json"); - try { - const output = await process.output(); - const actual = decoder.decode(output).trim(); - const expected = [ - '.[0] = "string"', - ".[1] = 100", - '.[2].key = "value"', - '.[2].array[0] = "foo"', - '.[2].array[1] = "bar"', - ].join("\n"); - - assertStrictEquals(actual, expected); - } finally { - process.stdin.close(); - process.close(); - } -}); - -Deno.test("[examples/catj] print an object", async () => { - const decoder = new TextDecoder(); - const process = catj("testdata/catj/object.json"); - try { - const output = await process.output(); - const actual = decoder.decode(output).trim(); - const expected = [ - '.string = "foobar"', - ".number = 123", - '.array[0].message = "hello"', - ].join("\n"); - - assertStrictEquals(actual, expected); - } finally { - process.stdin.close(); - process.close(); - } -}); - -Deno.test("[examples/catj] print multiple files", async () => { - const decoder = new TextDecoder(); - const process = catj( - "testdata/catj/simple-object.json", - "testdata/catj/simple-array.json", - ); - try { - const output = await process.output(); - const actual = decoder.decode(output).trim(); - const expected = ['.message = "hello"', ".[0] = 1", ".[1] = 2"].join("\n"); - - assertStrictEquals(actual, expected); - } finally { - process.stdin.close(); - process.close(); - } -}); - -Deno.test("[examples/catj] read from stdin", async () => { - const decoder = new TextDecoder(); - const process = catj("-"); - const input = `{ "foo": "bar" }`; - try { - await process.stdin.write(new TextEncoder().encode(input)); - process.stdin.close(); - const output = await process.output(); - const actual = decoder.decode(output).trim(); - - assertStrictEquals(actual, '.foo = "bar"'); - } finally { - process.close(); - } -}); - -function catj( - ...files: string[] -): Deno.Process { - return Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - "--allow-read", - "catj.ts", - ...files, - ], - cwd: moduleDir, - stdin: "piped", - stdout: "piped", - env: { NO_COLOR: "true" }, - }); -} diff --git a/std/examples/chat/index.html b/std/examples/chat/index.html deleted file mode 100644 index 2daf288b3a..0000000000 --- a/std/examples/chat/index.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - ws chat example - - -
- - - - -
-
-
    - - - diff --git a/std/examples/chat/server.ts b/std/examples/chat/server.ts deleted file mode 100644 index 39b5ee1240..0000000000 --- a/std/examples/chat/server.ts +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { listenAndServe } from "../../http/server.ts"; -import { - acceptable, - acceptWebSocket, - isWebSocketCloseEvent, - WebSocket, -} from "../../ws/mod.ts"; -import { fromFileUrl } from "../../path/mod.ts"; - -const clients = new Map(); -let clientId = 0; -function dispatch(msg: string): void { - for (const client of clients.values()) { - client.send(msg); - } -} -async function wsHandler(ws: WebSocket): Promise { - const id = ++clientId; - clients.set(id, ws); - dispatch(`Connected: [${id}]`); - for await (const msg of ws) { - console.log(`msg:${id}`, msg); - if (typeof msg === "string") { - dispatch(`[${id}]: ${msg}`); - } else if (isWebSocketCloseEvent(msg)) { - clients.delete(id); - dispatch(`Closed: [${id}]`); - break; - } - } -} - -listenAndServe({ port: 8080 }, async (req) => { - if (req.method === "GET" && req.url === "/") { - //Serve with hack - const u = new URL("./index.html", import.meta.url); - if (u.protocol.startsWith("http")) { - // server launched by deno run http(s)://.../server.ts, - fetch(u.href).then(async (resp) => { - const body = new Uint8Array(await resp.arrayBuffer()); - return req.respond({ - status: resp.status, - headers: new Headers({ - "content-type": "text/html", - }), - body, - }); - }); - } else { - // server launched by deno run ./server.ts - const file = await Deno.open(fromFileUrl(u)); - req.respond({ - status: 200, - headers: new Headers({ - "content-type": "text/html", - }), - body: file, - }); - } - } - if (req.method === "GET" && req.url === "/favicon.ico") { - req.respond({ - status: 302, - headers: new Headers({ - location: "https://deno.land/favicon.ico", - }), - }); - } - if (req.method === "GET" && req.url === "/ws") { - if (acceptable(req)) { - acceptWebSocket({ - conn: req.conn, - bufReader: req.r, - bufWriter: req.w, - headers: req.headers, - }).then(wsHandler); - } - } -}); -console.log("chat server starting on :8080...."); diff --git a/std/examples/chat/server_test.ts b/std/examples/chat/server_test.ts deleted file mode 100644 index 80a66cb2fa..0000000000 --- a/std/examples/chat/server_test.ts +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "../../testing/asserts.ts"; -import { TextProtoReader } from "../../textproto/mod.ts"; -import { BufReader } from "../../io/bufio.ts"; -import { delay } from "../../async/delay.ts"; -import { dirname, fromFileUrl, resolve } from "../../path/mod.ts"; - -const moduleDir = resolve(dirname(fromFileUrl(import.meta.url))); - -async function startServer(): Promise< - Deno.Process -> { - const server = Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - "--allow-net", - "--allow-read", - "server.ts", - ], - cwd: moduleDir, - stdout: "piped", - }); - try { - assert(server.stdout != null); - const r = new TextProtoReader(new BufReader(server.stdout)); - const s = await r.readLine(); - assert(s !== null && s.includes("chat server starting")); - } catch (err) { - server.stdout.close(); - server.close(); - } - - return server; -} - -Deno.test({ - name: "[examples/chat] GET / should serve html", - async fn() { - const server = await startServer(); - try { - const resp = await fetch("http://127.0.0.1:8080/"); - assertEquals(resp.status, 200); - assertEquals(resp.headers.get("content-type"), "text/html"); - const html = await resp.text(); - assert(html.includes("ws chat example"), "body is ok"); - } finally { - server.close(); - server.stdout.close(); - } - await delay(10); - }, -}); - -Deno.test({ - name: "[examples/chat] GET /ws should upgrade conn to ws", - async fn() { - const server = await startServer(); - let ws: WebSocket; - try { - ws = new WebSocket("ws://127.0.0.1:8080/ws"); - await new Promise((resolve) => { - ws.onmessage = ((message) => { - assertEquals(message.data, "Connected: [1]"); - ws.onmessage = ((message) => { - assertEquals(message.data, "[1]: Hello"); - ws.close(); - resolve(); - }); - ws.send("Hello"); - }); - }); - } catch (err) { - console.log(err); - } finally { - server.close(); - server.stdout.close(); - } - }, -}); diff --git a/std/examples/colors.ts b/std/examples/colors.ts deleted file mode 100644 index 4af8d9bfa0..0000000000 --- a/std/examples/colors.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { bgBlue, bold, italic, red } from "../fmt/colors.ts"; - -if (import.meta.main) { - console.log(bgBlue(italic(red(bold("Hello world!"))))); -} diff --git a/std/examples/colors_test.ts b/std/examples/colors_test.ts deleted file mode 100644 index 4e009e3293..0000000000 --- a/std/examples/colors_test.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertStrictEquals } from "../testing/asserts.ts"; -import { dirname, fromFileUrl } from "../path/mod.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); - -Deno.test("[examples/colors] print a colored text", async () => { - const decoder = new TextDecoder(); - const process = Deno.run({ - cmd: [Deno.execPath(), "run", "--quiet", "colors.ts"], - cwd: moduleDir, - stdout: "piped", - }); - try { - const output = await process.output(); - const actual = decoder.decode(output).trim(); - const expected = "Hello world!"; - assertStrictEquals(actual, expected); - } finally { - process.close(); - } -}); diff --git a/std/examples/curl.ts b/std/examples/curl.ts deleted file mode 100644 index 2ed194446f..0000000000 --- a/std/examples/curl.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -const url_ = Deno.args[0]; -const res = await fetch(url_); - -// TODO(ry) Re-enable streaming in this example. -// Originally we did: await Deno.copy(res.body, Deno.stdout); -// But maybe more JS-y would be: res.pipeTo(Deno.stdout); - -const body = new Uint8Array(await res.arrayBuffer()); -await Deno.stdout.write(body); diff --git a/std/examples/curl_test.ts b/std/examples/curl_test.ts deleted file mode 100644 index 6612b17cc4..0000000000 --- a/std/examples/curl_test.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { serve } from "../http/server.ts"; -import { assertStrictEquals } from "../testing/asserts.ts"; -import { dirname, fromFileUrl } from "../path/mod.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); - -Deno.test({ - name: "[examples/curl] send a request to a specified url", - fn: async () => { - const server = serve({ port: 8081 }); - const serverPromise = (async (): Promise => { - for await (const req of server) { - req.respond({ body: "Hello world" }); - } - })(); - - const decoder = new TextDecoder(); - const process = Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - "--allow-net", - "curl.ts", - "http://localhost:8081", - ], - cwd: moduleDir, - stdout: "piped", - }); - - try { - const output = await process.output(); - const actual = decoder.decode(output).trim(); - const expected = "Hello world"; - - assertStrictEquals(actual, expected); - } finally { - server.close(); - process.close(); - await serverPromise; - } - }, -}); diff --git a/std/examples/echo_server.ts b/std/examples/echo_server.ts deleted file mode 100644 index 03e0d60701..0000000000 --- a/std/examples/echo_server.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -const hostname = "0.0.0.0"; -const port = 8080; -const listener = Deno.listen({ hostname, port }); -console.log(`Listening on ${hostname}:${port}`); -for await (const conn of listener) { - Deno.copy(conn, conn); -} diff --git a/std/examples/echo_server_test.ts b/std/examples/echo_server_test.ts deleted file mode 100644 index 24e0c416bf..0000000000 --- a/std/examples/echo_server_test.ts +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertNotEquals, assertStrictEquals } from "../testing/asserts.ts"; -import { BufReader, ReadLineResult } from "../io/bufio.ts"; -import { dirname, fromFileUrl } from "../path/mod.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); - -Deno.test("[examples/echo_server]", async () => { - const encoder = new TextEncoder(); - const decoder = new TextDecoder(); - const process = Deno.run({ - cmd: [Deno.execPath(), "run", "--quiet", "--allow-net", "echo_server.ts"], - cwd: moduleDir, - stdout: "piped", - }); - - let conn: Deno.Conn | undefined; - try { - const processReader = new BufReader(process.stdout); - const message = await processReader.readLine(); - - assertNotEquals(message, null); - assertStrictEquals( - decoder.decode((message as ReadLineResult).line).trim(), - "Listening on 0.0.0.0:8080", - ); - - conn = await Deno.connect({ hostname: "127.0.0.1", port: 8080 }); - const connReader = new BufReader(conn); - - await conn.write(encoder.encode("Hello echo_server\n")); - const result = await connReader.readLine(); - - assertNotEquals(result, null); - - const actualResponse = decoder - .decode((result as ReadLineResult).line) - .trim(); - const expectedResponse = "Hello echo_server"; - - assertStrictEquals(actualResponse, expectedResponse); - } finally { - conn?.close(); - process.stdout.close(); - process.close(); - } -}); diff --git a/std/examples/flags.ts b/std/examples/flags.ts deleted file mode 100644 index a452494c53..0000000000 --- a/std/examples/flags.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { parse } from "../flags/mod.ts"; - -if (import.meta.main) { - console.dir(parse(Deno.args)); -} diff --git a/std/examples/gist.ts b/std/examples/gist.ts deleted file mode 100755 index 1698ab354b..0000000000 --- a/std/examples/gist.ts +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env -S deno run --allow-net --allow-env -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// A program to post files to gist.github.com. Use the following to install it: -// deno install -f --allow-env --allow-read --allow-net=api.github.com https://deno.land/std/examples/gist.ts -import { parse } from "../flags/mod.ts"; - -function pathBase(p: string): string { - const parts = p.split("/"); - return parts[parts.length - 1]; -} - -const token = Deno.env.get("GIST_TOKEN"); -if (!token) { - console.error("GIST_TOKEN environmental variable not set."); - console.error("Get a token here: https://github.com/settings/tokens"); - Deno.exit(1); -} - -const parsedArgs = parse(Deno.args); - -if (parsedArgs._.length === 0) { - console.error( - "Usage: gist.ts --allow-env --allow-net [-t|--title Example] some_file " + - "[next_file]", - ); - Deno.exit(1); -} - -const files: Record = {}; -for (const filename of parsedArgs._) { - const base = pathBase(filename as string); - const content = await Deno.readFile(filename as string); - const contentStr = new TextDecoder().decode(content); - files[base] = { content: contentStr }; -} - -const content = { - description: parsedArgs.title || parsedArgs.t || "Example", - public: false, - files: files, -}; -const body = JSON.stringify(content); - -const res = await fetch("https://api.github.com/gists", { - method: "POST", - headers: [ - ["Content-Type", "application/json"], - ["User-Agent", "Deno-Gist"], - ["Authorization", `token ${token}`], - ], - body, -}); - -if (res.ok) { - const resObj = await res.json(); - console.log("Success"); - console.log(resObj["html_url"]); -} else { - const err = await res.text(); - console.error("Failure to POST", err); -} diff --git a/std/examples/test.ts b/std/examples/test.ts deleted file mode 100644 index 6afeb221f1..0000000000 --- a/std/examples/test.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { dirname, fromFileUrl, relative, resolve } from "../path/mod.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); - -/** Example of how to do basic tests */ -Deno.test("t1", function (): void { - assertEquals("hello", "hello"); -}); - -Deno.test("t2", function (): void { - assertEquals("world", "world"); -}); - -/** A more complicated test that runs a subprocess. */ -Deno.test("catSmoke", async function (): Promise { - const p = Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - "--allow-read", - relative(Deno.cwd(), resolve(moduleDir, "cat.ts")), - relative(Deno.cwd(), resolve(moduleDir, "..", "README.md")), - ], - stdout: "null", - stderr: "null", - }); - const s = await p.status(); - assertEquals(s.code, 0); - p.close(); -}); diff --git a/std/examples/test_test.ts b/std/examples/test_test.ts deleted file mode 100644 index c8e3c4ac18..0000000000 --- a/std/examples/test_test.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import "./test.ts"; diff --git a/std/examples/testdata/cat/hello.txt b/std/examples/testdata/cat/hello.txt deleted file mode 100644 index e965047ad7..0000000000 --- a/std/examples/testdata/cat/hello.txt +++ /dev/null @@ -1 +0,0 @@ -Hello diff --git a/std/examples/testdata/cat/world.txt b/std/examples/testdata/cat/world.txt deleted file mode 100644 index 216e97ce08..0000000000 --- a/std/examples/testdata/cat/world.txt +++ /dev/null @@ -1 +0,0 @@ -World diff --git a/std/examples/testdata/catj/array.json b/std/examples/testdata/catj/array.json deleted file mode 100644 index 6dac876a08..0000000000 --- a/std/examples/testdata/catj/array.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "string", - 100, - { "key": "value", "array": ["foo", "bar"] } -] diff --git a/std/examples/testdata/catj/object.json b/std/examples/testdata/catj/object.json deleted file mode 100644 index 290c008fff..0000000000 --- a/std/examples/testdata/catj/object.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "string": "foobar", - "number": 123, - "array": [{ "message": "hello" }] -} - diff --git a/std/examples/testdata/catj/simple-array.json b/std/examples/testdata/catj/simple-array.json deleted file mode 100644 index 44e2ace7e5..0000000000 --- a/std/examples/testdata/catj/simple-array.json +++ /dev/null @@ -1 +0,0 @@ -[1, 2] diff --git a/std/examples/testdata/catj/simple-object.json b/std/examples/testdata/catj/simple-object.json deleted file mode 100644 index 52bb626d82..0000000000 --- a/std/examples/testdata/catj/simple-object.json +++ /dev/null @@ -1 +0,0 @@ -{ "message": "hello" } diff --git a/std/examples/welcome.ts b/std/examples/welcome.ts deleted file mode 100644 index f983ca89ba..0000000000 --- a/std/examples/welcome.ts +++ /dev/null @@ -1 +0,0 @@ -console.log("Welcome to Deno!"); diff --git a/std/examples/welcome_test.ts b/std/examples/welcome_test.ts deleted file mode 100644 index 2e26aa9676..0000000000 --- a/std/examples/welcome_test.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertStrictEquals } from "../testing/asserts.ts"; -import { dirname, fromFileUrl } from "../path/mod.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); - -Deno.test("[examples/welcome] print a welcome message", async () => { - const decoder = new TextDecoder(); - const process = Deno.run({ - cmd: [Deno.execPath(), "run", "--quiet", "welcome.ts"], - cwd: moduleDir, - stdout: "piped", - }); - try { - const output = await process.output(); - const actual = decoder.decode(output).trim(); - const expected = "Welcome to Deno!"; - assertStrictEquals(actual, expected); - } finally { - process.close(); - } -}); diff --git a/std/examples/xeval.ts b/std/examples/xeval.ts deleted file mode 100644 index cff15edd01..0000000000 --- a/std/examples/xeval.ts +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { parse } from "../flags/mod.ts"; -import { readStringDelim } from "../io/bufio.ts"; - -// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction. -const AsyncFunction = Object.getPrototypeOf(async function (): Promise {}) - .constructor; - -const HELP_MSG = `xeval - -Run a script for each new-line or otherwise delimited chunk of standard input. - -Print all the usernames in /etc/passwd: - cat /etc/passwd | deno run -A https://deno.land/std/examples/xeval.ts "a = $.split(':'); if (a) console.log(a[0])" - -A complicated way to print the current git branch: - git branch | deno run -A https://deno.land/std/examples/xeval.ts -I 'line' "if (line.startsWith('*')) console.log(line.slice(2))" - -Demonstrates breaking the input up by space delimiter instead of by lines: - cat LICENSE | deno run -A https://deno.land/std/examples/xeval.ts -d " " "if ($ === 'MIT') console.log('MIT licensed')", - -USAGE: - deno run -A https://deno.land/std/examples/xeval.ts [OPTIONS] -OPTIONS: - -d, --delim Set delimiter, defaults to newline - -I, --replvar Set variable name to be used in eval, defaults to $ -ARGS: - `; - -export type XevalFunc = (v: string) => void; - -export interface XevalOptions { - delimiter?: string; -} - -const DEFAULT_DELIMITER = "\n"; - -export async function xeval( - reader: Deno.Reader, - xevalFunc: XevalFunc, - { delimiter = DEFAULT_DELIMITER }: XevalOptions = {}, -): Promise { - for await (const chunk of readStringDelim(reader, delimiter)) { - // Ignore empty chunks. - if (chunk.length > 0) { - await xevalFunc(chunk); - } - } -} - -async function main(): Promise { - const parsedArgs = parse(Deno.args, { - boolean: ["help"], - string: ["delim", "replvar"], - alias: { - delim: ["d"], - replvar: ["I"], - help: ["h"], - }, - default: { - delim: DEFAULT_DELIMITER, - replvar: "$", - }, - }); - if (parsedArgs._.length != 1) { - console.error(HELP_MSG); - console.log(parsedArgs._); - Deno.exit(1); - } - if (parsedArgs.help) { - return console.log(HELP_MSG); - } - - const delimiter = parsedArgs.delim; - const replVar = parsedArgs.replvar; - const code = parsedArgs._[0]; - - // new AsyncFunction()'s error message for this particular case isn't great. - if (!replVar.match(/^[_$A-z][_$A-z0-9]*$/)) { - console.error(`Bad replvar identifier: "${replVar}"`); - Deno.exit(1); - } - - const xEvalFunc = new AsyncFunction(replVar, code); - - await xeval(Deno.stdin, xEvalFunc, { delimiter }); -} - -if (import.meta.main) { - main(); -} diff --git a/std/examples/xeval_test.ts b/std/examples/xeval_test.ts deleted file mode 100644 index 4793c63461..0000000000 --- a/std/examples/xeval_test.ts +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { xeval } from "./xeval.ts"; -import { StringReader } from "../io/readers.ts"; -import { decode, encode } from "../encoding/utf8.ts"; -import { - assert, - assertEquals, - assertStringIncludes, -} from "../testing/asserts.ts"; -import { dirname, fromFileUrl } from "../path/mod.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); - -Deno.test("xevalSuccess", async function (): Promise { - const chunks: string[] = []; - await xeval(new StringReader("a\nb\nc"), ($): number => chunks.push($)); - assertEquals(chunks, ["a", "b", "c"]); -}); - -Deno.test("xevalDelimiter", async function (): Promise { - const chunks: string[] = []; - await xeval( - new StringReader("!MADMADAMADAM!"), - ($): number => chunks.push($), - { - delimiter: "MADAM", - }, - ); - assertEquals(chunks, ["!MAD", "ADAM!"]); -}); - -const xevalPath = "xeval.ts"; - -Deno.test({ - name: "xevalCliReplvar", - fn: async function (): Promise { - const p = Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - xevalPath, - "--replvar=abc", - "console.log(abc)", - ], - cwd: moduleDir, - stdin: "piped", - stdout: "piped", - stderr: "null", - }); - assert(p.stdin != null); - await p.stdin.write(encode("hello")); - p.stdin.close(); - assertEquals(await p.status(), { code: 0, success: true }); - assertEquals(decode(await p.output()).trimEnd(), "hello"); - p.close(); - }, -}); - -Deno.test("xevalCliSyntaxError", async function (): Promise { - const p = Deno.run({ - cmd: [Deno.execPath(), "run", "--quiet", xevalPath, "("], - cwd: moduleDir, - stdin: "null", - stdout: "piped", - stderr: "piped", - }); - assertEquals(await p.status(), { code: 1, success: false }); - assertEquals(decode(await p.output()), ""); - assertStringIncludes(decode(await p.stderrOutput()), "SyntaxError"); - p.close(); -}); diff --git a/std/flags/README.md b/std/flags/README.md deleted file mode 100644 index 7b8e508d4b..0000000000 --- a/std/flags/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# flags - -Command line arguments parser for Deno based on minimist. - -# Example - -```ts -import { parse } from "https://deno.land/std@$STD_VERSION/flags/mod.ts"; - -console.dir(parse(Deno.args)); -``` - -``` -$ deno run https://deno.land/std/examples/flags.ts -a beep -b boop -{ _: [], a: 'beep', b: 'boop' } -``` - -``` -$ deno run https://deno.land/std/examples/flags.ts -x 3 -y 4 -n5 -abc --beep=boop foo bar baz -{ _: [ 'foo', 'bar', 'baz' ], - x: 3, - y: 4, - n: 5, - a: true, - b: true, - c: true, - beep: 'boop' } -``` - -# API - -## const parsedArgs = parse(args, options = {}); - -`parsedArgs._` contains all the arguments that didn't have an option associated -with them. - -Numeric-looking arguments will be returned as numbers unless `options.string` or -`options.boolean` is set for that argument name. - -Any arguments after `'--'` will not be parsed and will end up in `parsedArgs._`. - -options can be: - -- `options.string` - a string or array of strings argument names to always treat - as strings. -- `options.boolean` - a boolean, string or array of strings to always treat as - booleans. if `true` will treat all double hyphenated arguments without equal - signs as boolean (e.g. affects `--foo`, not `-f` or `--foo=bar`). -- `options.alias` - an object mapping string names to strings or arrays of - string argument names to use as aliases. -- `options.default` - an object mapping string argument names to default values. -- `options.stopEarly` - when true, populate `parsedArgs._` with everything after - the first non-option. -- `options['--']` - when true, populate `parsedArgs._` with everything before - the `--` and `parsedArgs['--']` with everything after the `--`. Here's an - example: - ```ts - // $ deno run example.ts -- a arg1 - import { parse } from "https://deno.land/std@$STD_VERSION/flags/mod.ts"; - console.dir(parse(Deno.args, { "--": false })); - // output: { _: [ "a", "arg1" ] } - console.dir(parse(Deno.args, { "--": true })); - // output: { _: [], --: [ "a", "arg1" ] } - ``` -- `options.unknown` - a function which is invoked with a command line parameter - not defined in the `options` configuration object. If the function returns - `false`, the unknown option is not added to `parsedArgs`. diff --git a/std/flags/all_bool_test.ts b/std/flags/all_bool_test.ts deleted file mode 100755 index 3da80cb37c..0000000000 --- a/std/flags/all_bool_test.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse } from "./mod.ts"; - -// flag boolean true (default all --args to boolean) -Deno.test("flagBooleanTrue", function (): void { - const argv = parse(["moo", "--honk", "cow"], { - boolean: true, - }); - - assertEquals(argv, { - honk: true, - _: ["moo", "cow"], - }); - - assertEquals(typeof argv.honk, "boolean"); -}); - -// flag boolean true only affects double hyphen arguments without equals signs -Deno.test("flagBooleanTrueOnlyAffectsDoubleDash", function (): void { - const argv = parse(["moo", "--honk", "cow", "-p", "55", "--tacos=good"], { - boolean: true, - }); - - assertEquals(argv, { - honk: true, - tacos: "good", - p: 55, - _: ["moo", "cow"], - }); - - assertEquals(typeof argv.honk, "boolean"); -}); diff --git a/std/flags/bool_test.ts b/std/flags/bool_test.ts deleted file mode 100755 index bafa21adff..0000000000 --- a/std/flags/bool_test.ts +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse } from "./mod.ts"; - -Deno.test("flagBooleanDefaultFalse", function (): void { - const argv = parse(["moo"], { - boolean: ["t", "verbose"], - default: { verbose: false, t: false }, - }); - - assertEquals(argv, { - verbose: false, - t: false, - _: ["moo"], - }); - - assertEquals(typeof argv.verbose, "boolean"); - assertEquals(typeof argv.t, "boolean"); -}); - -Deno.test("booleanGroups", function (): void { - const argv = parse(["-x", "-z", "one", "two", "three"], { - boolean: ["x", "y", "z"], - }); - - assertEquals(argv, { - x: true, - y: false, - z: true, - _: ["one", "two", "three"], - }); - - assertEquals(typeof argv.x, "boolean"); - assertEquals(typeof argv.y, "boolean"); - assertEquals(typeof argv.z, "boolean"); -}); - -Deno.test("booleanAndAliasWithChainableApi", function (): void { - const aliased = ["-h", "derp"]; - const regular = ["--herp", "derp"]; - const aliasedArgv = parse(aliased, { - boolean: "herp", - alias: { h: "herp" }, - }); - const propertyArgv = parse(regular, { - boolean: "herp", - alias: { h: "herp" }, - }); - const expected = { - herp: true, - h: true, - _: ["derp"], - }; - - assertEquals(aliasedArgv, expected); - assertEquals(propertyArgv, expected); -}); - -Deno.test("booleanAndAliasWithOptionsHash", function (): void { - const aliased = ["-h", "derp"]; - const regular = ["--herp", "derp"]; - const opts = { - alias: { h: "herp" }, - boolean: "herp", - }; - const aliasedArgv = parse(aliased, opts); - const propertyArgv = parse(regular, opts); - const expected = { - herp: true, - h: true, - _: ["derp"], - }; - assertEquals(aliasedArgv, expected); - assertEquals(propertyArgv, expected); -}); - -Deno.test("booleanAndAliasArrayWithOptionsHash", function (): void { - const aliased = ["-h", "derp"]; - const regular = ["--herp", "derp"]; - const alt = ["--harp", "derp"]; - const opts = { - alias: { h: ["herp", "harp"] }, - boolean: "h", - }; - const aliasedArgv = parse(aliased, opts); - const propertyArgv = parse(regular, opts); - const altPropertyArgv = parse(alt, opts); - const expected = { - harp: true, - herp: true, - h: true, - _: ["derp"], - }; - assertEquals(aliasedArgv, expected); - assertEquals(propertyArgv, expected); - assertEquals(altPropertyArgv, expected); -}); - -Deno.test("booleanAndAliasUsingExplicitTrue", function (): void { - const aliased = ["-h", "true"]; - const regular = ["--herp", "true"]; - const opts = { - alias: { h: "herp" }, - boolean: "h", - }; - const aliasedArgv = parse(aliased, opts); - const propertyArgv = parse(regular, opts); - const expected = { - herp: true, - h: true, - _: [], - }; - - assertEquals(aliasedArgv, expected); - assertEquals(propertyArgv, expected); -}); - -// regression, see https://github.com/substack/node-optimist/issues/71 -// boolean and --x=true -Deno.test("booleanAndNonBoolean", function (): void { - const parsed = parse(["--boool", "--other=true"], { - boolean: "boool", - }); - - assertEquals(parsed.boool, true); - assertEquals(parsed.other, "true"); - - const parsed2 = parse(["--boool", "--other=false"], { - boolean: "boool", - }); - - assertEquals(parsed2.boool, true); - assertEquals(parsed2.other, "false"); -}); - -Deno.test("booleanParsingTrue", function (): void { - const parsed = parse(["--boool=true"], { - default: { - boool: false, - }, - boolean: ["boool"], - }); - - assertEquals(parsed.boool, true); -}); - -Deno.test("booleanParsingFalse", function (): void { - const parsed = parse(["--boool=false"], { - default: { - boool: true, - }, - boolean: ["boool"], - }); - - assertEquals(parsed.boool, false); -}); - -Deno.test("booleanParsingTrueLike", function (): void { - const parsed = parse(["-t", "true123"], { boolean: ["t"] }); - assertEquals(parsed.t, true); - - const parsed2 = parse(["-t", "123"], { boolean: ["t"] }); - assertEquals(parsed2.t, true); - - const parsed3 = parse(["-t", "false123"], { boolean: ["t"] }); - assertEquals(parsed3.t, true); -}); - -Deno.test("booleanNegationAfterBoolean", function (): void { - const parsed = parse(["--foo", "--no-foo"], { boolean: ["foo"] }); - assertEquals(parsed.foo, false); - - const parsed2 = parse(["--foo", "--no-foo", "123"], { boolean: ["foo"] }); - assertEquals(parsed2.foo, false); -}); - -Deno.test("booleanAfterBooleanNegation", function (): void { - const parsed = parse(["--no--foo", "--foo"], { boolean: ["foo"] }); - assertEquals(parsed.foo, true); - - const parsed2 = parse(["--no--foo", "--foo", "123"], { boolean: ["foo"] }); - assertEquals(parsed2.foo, true); -}); - -Deno.test("latestFlagIsBooleanNegation", function (): void { - const parsed = parse(["--no-foo", "--foo", "--no-foo"], { boolean: ["foo"] }); - assertEquals(parsed.foo, false); - - const parsed2 = parse(["--no-foo", "--foo", "--no-foo", "123"], { - boolean: ["foo"], - }); - assertEquals(parsed2.foo, false); -}); - -Deno.test("latestFlagIsBoolean", function (): void { - const parsed = parse(["--foo", "--no-foo", "--foo"], { boolean: ["foo"] }); - assertEquals(parsed.foo, true); - - const parsed2 = parse(["--foo", "--no-foo", "--foo", "123"], { - boolean: ["foo"], - }); - assertEquals(parsed2.foo, true); -}); diff --git a/std/flags/dash_test.ts b/std/flags/dash_test.ts deleted file mode 100755 index 959956d99b..0000000000 --- a/std/flags/dash_test.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse } from "./mod.ts"; - -Deno.test("hyphen", function (): void { - assertEquals(parse(["-n", "-"]), { n: "-", _: [] }); - assertEquals(parse(["-"]), { _: ["-"] }); - assertEquals(parse(["-f-"]), { f: "-", _: [] }); - assertEquals(parse(["-b", "-"], { boolean: "b" }), { b: true, _: ["-"] }); - assertEquals(parse(["-s", "-"], { string: "s" }), { s: "-", _: [] }); -}); - -Deno.test("doubleDash", function (): void { - assertEquals(parse(["-a", "--", "b"]), { a: true, _: ["b"] }); - assertEquals(parse(["--a", "--", "b"]), { a: true, _: ["b"] }); - assertEquals(parse(["--a", "--", "b"]), { a: true, _: ["b"] }); -}); - -Deno.test("moveArgsAfterDoubleDashIntoOwnArray", function (): void { - assertEquals( - parse(["--name", "John", "before", "--", "after"], { "--": true }), - { - name: "John", - _: ["before"], - "--": ["after"], - }, - ); -}); diff --git a/std/flags/default_bool_test.ts b/std/flags/default_bool_test.ts deleted file mode 100755 index 625bed3bfd..0000000000 --- a/std/flags/default_bool_test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse } from "./mod.ts"; - -Deno.test("booleanDefaultTrue", function (): void { - const argv = parse([], { - boolean: "sometrue", - default: { sometrue: true }, - }); - assertEquals(argv.sometrue, true); -}); - -Deno.test("booleanDefaultFalse", function (): void { - const argv = parse([], { - boolean: "somefalse", - default: { somefalse: false }, - }); - assertEquals(argv.somefalse, false); -}); - -Deno.test("booleanDefaultNull", function (): void { - const argv = parse([], { - boolean: "maybe", - default: { maybe: null }, - }); - assertEquals(argv.maybe, null); - const argv2 = parse(["--maybe"], { - boolean: "maybe", - default: { maybe: null }, - }); - assertEquals(argv2.maybe, true); -}); diff --git a/std/flags/dotted_test.ts b/std/flags/dotted_test.ts deleted file mode 100755 index aed3941987..0000000000 --- a/std/flags/dotted_test.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse } from "./mod.ts"; - -Deno.test("dottedAlias", function (): void { - const argv = parse(["--a.b", "22"], { - default: { "a.b": 11 }, - alias: { "a.b": "aa.bb" }, - }); - assertEquals(argv.a.b, 22); - assertEquals(argv.aa.bb, 22); -}); - -Deno.test("dottedDefault", function (): void { - const argv = parse([], { default: { "a.b": 11 }, alias: { "a.b": "aa.bb" } }); - assertEquals(argv.a.b, 11); - assertEquals(argv.aa.bb, 11); -}); - -Deno.test("dottedDefaultWithNoAlias", function (): void { - const argv = parse([], { default: { "a.b": 11 } }); - assertEquals(argv.a.b, 11); -}); diff --git a/std/flags/kv_short_test.ts b/std/flags/kv_short_test.ts deleted file mode 100755 index 453d8a9565..0000000000 --- a/std/flags/kv_short_test.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse } from "./mod.ts"; - -Deno.test("short", function (): void { - const argv = parse(["-b=123"]); - assertEquals(argv, { b: 123, _: [] }); -}); - -Deno.test("multiShort", function (): void { - const argv = parse(["-a=whatever", "-b=robots"]); - assertEquals(argv, { a: "whatever", b: "robots", _: [] }); -}); diff --git a/std/flags/long_test.ts b/std/flags/long_test.ts deleted file mode 100755 index b10d101f63..0000000000 --- a/std/flags/long_test.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse } from "./mod.ts"; - -Deno.test("longOpts", function (): void { - assertEquals(parse(["--bool"]), { bool: true, _: [] }); - assertEquals(parse(["--pow", "xixxle"]), { pow: "xixxle", _: [] }); - assertEquals(parse(["--pow=xixxle"]), { pow: "xixxle", _: [] }); - assertEquals(parse(["--host", "localhost", "--port", "555"]), { - host: "localhost", - port: 555, - _: [], - }); - assertEquals(parse(["--host=localhost", "--port=555"]), { - host: "localhost", - port: 555, - _: [], - }); -}); diff --git a/std/flags/mod.ts b/std/flags/mod.ts deleted file mode 100644 index 7c066f9009..0000000000 --- a/std/flags/mod.ts +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { assert } from "../_util/assert.ts"; - -export interface Args { - /** Contains all the arguments that didn't have an option associated with - * them. */ - _: Array; - // deno-lint-ignore no-explicit-any - [key: string]: any; -} - -export interface ArgParsingOptions { - /** When `true`, populate the result `_` with everything before the `--` and - * the result `['--']` with everything after the `--`. Here's an example: - * - * // $ deno run example.ts -- a arg1 - * import { parse } from "https://deno.land/std/flags/mod.ts"; - * console.dir(parse(Deno.args, { "--": false })); - * // output: { _: [ "a", "arg1" ] } - * console.dir(parse(Deno.args, { "--": true })); - * // output: { _: [], --: [ "a", "arg1" ] } - * - * Defaults to `false`. - */ - "--"?: boolean; - - /** An object mapping string names to strings or arrays of string argument - * names to use as aliases */ - alias?: Record; - - /** A boolean, string or array of strings to always treat as booleans. If - * `true` will treat all double hyphenated arguments without equal signs as - * `boolean` (e.g. affects `--foo`, not `-f` or `--foo=bar`) */ - boolean?: boolean | string | string[]; - - /** An object mapping string argument names to default values. */ - default?: Record; - - /** When `true`, populate the result `_` with everything after the first - * non-option. */ - stopEarly?: boolean; - - /** A string or array of strings argument names to always treat as strings. */ - string?: string | string[]; - - /** A function which is invoked with a command line parameter not defined in - * the `options` configuration object. If the function returns `false`, the - * unknown option is not added to `parsedArgs`. */ - unknown?: (arg: string, key?: string, value?: unknown) => unknown; -} - -interface Flags { - bools: Record; - strings: Record; - unknownFn: (arg: string, key?: string, value?: unknown) => unknown; - allBools: boolean; -} - -interface NestedMapping { - [key: string]: NestedMapping | unknown; -} - -function get(obj: Record, key: string): T | undefined { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - return obj[key]; - } -} - -function getForce(obj: Record, key: string): T { - const v = get(obj, key); - assert(v != null); - return v; -} - -function isNumber(x: unknown): boolean { - if (typeof x === "number") return true; - if (/^0x[0-9a-f]+$/i.test(String(x))) return true; - return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(String(x)); -} - -function hasKey(obj: NestedMapping, keys: string[]): boolean { - let o = obj; - keys.slice(0, -1).forEach((key) => { - o = (get(o, key) ?? {}) as NestedMapping; - }); - - const key = keys[keys.length - 1]; - return key in o; -} - -/** Take a set of command line arguments, with an optional set of options, and - * return an object representation of those argument. - * - * const parsedArgs = parse(Deno.args); - */ -export function parse( - args: string[], - { - "--": doubleDash = false, - alias = {}, - boolean = false, - default: defaults = {}, - stopEarly = false, - string = [], - unknown = (i: string): unknown => i, - }: ArgParsingOptions = {}, -): Args { - const flags: Flags = { - bools: {}, - strings: {}, - unknownFn: unknown, - allBools: false, - }; - - if (boolean !== undefined) { - if (typeof boolean === "boolean") { - flags.allBools = !!boolean; - } else { - const booleanArgs = typeof boolean === "string" ? [boolean] : boolean; - - for (const key of booleanArgs.filter(Boolean)) { - flags.bools[key] = true; - } - } - } - - const aliases: Record = {}; - if (alias !== undefined) { - for (const key in alias) { - const val = getForce(alias, key); - if (typeof val === "string") { - aliases[key] = [val]; - } else { - aliases[key] = val; - } - for (const alias of getForce(aliases, key)) { - aliases[alias] = [key].concat(aliases[key].filter((y) => alias !== y)); - } - } - } - - if (string !== undefined) { - const stringArgs = typeof string === "string" ? [string] : string; - - for (const key of stringArgs.filter(Boolean)) { - flags.strings[key] = true; - const alias = get(aliases, key); - if (alias) { - for (const al of alias) { - flags.strings[al] = true; - } - } - } - } - - const argv: Args = { _: [] }; - - function argDefined(key: string, arg: string): boolean { - return ( - (flags.allBools && /^--[^=]+$/.test(arg)) || - get(flags.bools, key) || - !!get(flags.strings, key) || - !!get(aliases, key) - ); - } - - function setKey(obj: NestedMapping, keys: string[], value: unknown): void { - let o = obj; - keys.slice(0, -1).forEach(function (key): void { - if (get(o, key) === undefined) { - o[key] = {}; - } - o = get(o, key) as NestedMapping; - }); - - const key = keys[keys.length - 1]; - if ( - get(o, key) === undefined || - get(flags.bools, key) || - typeof get(o, key) === "boolean" - ) { - o[key] = value; - } else if (Array.isArray(get(o, key))) { - (o[key] as unknown[]).push(value); - } else { - o[key] = [get(o, key), value]; - } - } - - function setArg( - key: string, - val: unknown, - arg: string | undefined = undefined, - ): void { - if (arg && flags.unknownFn && !argDefined(key, arg)) { - if (flags.unknownFn(arg, key, val) === false) return; - } - - const value = !get(flags.strings, key) && isNumber(val) ? Number(val) : val; - setKey(argv, key.split("."), value); - - const alias = get(aliases, key); - if (alias) { - for (const x of alias) { - setKey(argv, x.split("."), value); - } - } - } - - function aliasIsBoolean(key: string): boolean { - return getForce(aliases, key).some( - (x) => typeof get(flags.bools, x) === "boolean", - ); - } - - for (const key of Object.keys(flags.bools)) { - setArg(key, defaults[key] === undefined ? false : defaults[key]); - } - - let notFlags: string[] = []; - - // all args after "--" are not parsed - if (args.includes("--")) { - notFlags = args.slice(args.indexOf("--") + 1); - args = args.slice(0, args.indexOf("--")); - } - - for (let i = 0; i < args.length; i++) { - const arg = args[i]; - - if (/^--.+=/.test(arg)) { - const m = arg.match(/^--([^=]+)=(.*)$/s); - assert(m != null); - const [, key, value] = m; - - if (flags.bools[key]) { - const booleanValue = value !== "false"; - setArg(key, booleanValue, arg); - } else { - setArg(key, value, arg); - } - } else if (/^--no-.+/.test(arg)) { - const m = arg.match(/^--no-(.+)/); - assert(m != null); - setArg(m[1], false, arg); - } else if (/^--.+/.test(arg)) { - const m = arg.match(/^--(.+)/); - assert(m != null); - const [, key] = m; - const next = args[i + 1]; - if ( - next !== undefined && - !/^-/.test(next) && - !get(flags.bools, key) && - !flags.allBools && - (get(aliases, key) ? !aliasIsBoolean(key) : true) - ) { - setArg(key, next, arg); - i++; - } else if (/^(true|false)$/.test(next)) { - setArg(key, next === "true", arg); - i++; - } else { - setArg(key, get(flags.strings, key) ? "" : true, arg); - } - } else if (/^-[^-]+/.test(arg)) { - const letters = arg.slice(1, -1).split(""); - - let broken = false; - for (let j = 0; j < letters.length; j++) { - const next = arg.slice(j + 2); - - if (next === "-") { - setArg(letters[j], next, arg); - continue; - } - - if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) { - setArg(letters[j], next.split(/=(.+)/)[1], arg); - broken = true; - break; - } - - if ( - /[A-Za-z]/.test(letters[j]) && - /-?\d+(\.\d*)?(e-?\d+)?$/.test(next) - ) { - setArg(letters[j], next, arg); - broken = true; - break; - } - - if (letters[j + 1] && letters[j + 1].match(/\W/)) { - setArg(letters[j], arg.slice(j + 2), arg); - broken = true; - break; - } else { - setArg(letters[j], get(flags.strings, letters[j]) ? "" : true, arg); - } - } - - const [key] = arg.slice(-1); - if (!broken && key !== "-") { - if ( - args[i + 1] && - !/^(-|--)[^-]/.test(args[i + 1]) && - !get(flags.bools, key) && - (get(aliases, key) ? !aliasIsBoolean(key) : true) - ) { - setArg(key, args[i + 1], arg); - i++; - } else if (args[i + 1] && /^(true|false)$/.test(args[i + 1])) { - setArg(key, args[i + 1] === "true", arg); - i++; - } else { - setArg(key, get(flags.strings, key) ? "" : true, arg); - } - } - } else { - if (!flags.unknownFn || flags.unknownFn(arg) !== false) { - argv._.push(flags.strings["_"] ?? !isNumber(arg) ? arg : Number(arg)); - } - if (stopEarly) { - argv._.push(...args.slice(i + 1)); - break; - } - } - } - - for (const key of Object.keys(defaults)) { - if (!hasKey(argv, key.split("."))) { - setKey(argv, key.split("."), defaults[key]); - - if (aliases[key]) { - for (const x of aliases[key]) { - setKey(argv, x.split("."), defaults[key]); - } - } - } - } - - if (doubleDash) { - argv["--"] = []; - for (const key of notFlags) { - argv["--"].push(key); - } - } else { - for (const key of notFlags) { - argv._.push(key); - } - } - - return argv; -} diff --git a/std/flags/num_test.ts b/std/flags/num_test.ts deleted file mode 100755 index b57aed4098..0000000000 --- a/std/flags/num_test.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse } from "./mod.ts"; - -Deno.test("nums", function (): void { - const argv = parse([ - "-x", - "1234", - "-y", - "5.67", - "-z", - "1e7", - "-w", - "10f", - "--hex", - "0xdeadbeef", - "789", - ]); - assertEquals(argv, { - x: 1234, - y: 5.67, - z: 1e7, - w: "10f", - hex: 0xdeadbeef, - _: [789], - }); - assertEquals(typeof argv.x, "number"); - assertEquals(typeof argv.y, "number"); - assertEquals(typeof argv.z, "number"); - assertEquals(typeof argv.w, "string"); - assertEquals(typeof argv.hex, "number"); - assertEquals(typeof argv._[0], "number"); -}); - -Deno.test("alreadyNumber", function (): void { - const argv = parse(["-x", "1234", "789"]); - assertEquals(argv, { x: 1234, _: [789] }); - assertEquals(typeof argv.x, "number"); - assertEquals(typeof argv._[0], "number"); -}); diff --git a/std/flags/parse_test.ts b/std/flags/parse_test.ts deleted file mode 100755 index 5b36d0857f..0000000000 --- a/std/flags/parse_test.ts +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse } from "./mod.ts"; - -Deno.test("parseArgs", function (): void { - assertEquals(parse(["--no-moo"]), { moo: false, _: [] }); - assertEquals(parse(["-v", "a", "-v", "b", "-v", "c"]), { - v: ["a", "b", "c"], - _: [], - }); -}); - -Deno.test("comprehensive", function (): void { - assertEquals( - parse([ - "--name=meowmers", - "bare", - "-cats", - "woo", - "-h", - "awesome", - "--multi=quux", - "--key", - "value", - "-b", - "--bool", - "--no-meep", - "--multi=baz", - "-f=abc=def", - "--foo=---=\\n--+34-=/=", - "-e==", - "--", - "--not-a-flag", - "eek", - ]), - { - c: true, - a: true, - t: true, - e: "=", - f: "abc=def", - foo: "---=\\n--+34-=/=", - s: "woo", - h: "awesome", - b: true, - bool: true, - key: "value", - multi: ["quux", "baz"], - meep: false, - name: "meowmers", - _: ["bare", "--not-a-flag", "eek"], - }, - ); -}); - -Deno.test("flagBoolean", function (): void { - const argv = parse(["-t", "moo"], { boolean: "t" }); - assertEquals(argv, { t: true, _: ["moo"] }); - assertEquals(typeof argv.t, "boolean"); -}); - -Deno.test("flagBooleanValue", function (): void { - const argv = parse(["--verbose", "false", "moo", "-t", "true"], { - boolean: ["t", "verbose"], - default: { verbose: true }, - }); - - assertEquals(argv, { - verbose: false, - t: true, - _: ["moo"], - }); - - assertEquals(typeof argv.verbose, "boolean"); - assertEquals(typeof argv.t, "boolean"); -}); - -Deno.test("newlinesInParams", function (): void { - const args = parse(["-s", "X\nX"]); - assertEquals(args, { _: [], s: "X\nX" }); - - // reproduce in bash: - // VALUE="new - // line" - // deno program.js --s="$VALUE" - const args2 = parse(["--s=X\nX"]); - assertEquals(args2, { _: [], s: "X\nX" }); -}); - -Deno.test("strings", function (): void { - const s = parse(["-s", "0001234"], { string: "s" }).s; - assertEquals(s, "0001234"); - assertEquals(typeof s, "string"); - - const x = parse(["-x", "56"], { string: "x" }).x; - assertEquals(x, "56"); - assertEquals(typeof x, "string"); -}); - -Deno.test("stringArgs", function (): void { - const s = parse([" ", " "], { string: "_" })._; - assertEquals(s.length, 2); - assertEquals(typeof s[0], "string"); - assertEquals(s[0], " "); - assertEquals(typeof s[1], "string"); - assertEquals(s[1], " "); -}); - -Deno.test("emptyStrings", function (): void { - const s = parse(["-s"], { string: "s" }).s; - assertEquals(s, ""); - assertEquals(typeof s, "string"); - - const str = parse(["--str"], { string: "str" }).str; - assertEquals(str, ""); - assertEquals(typeof str, "string"); - - const letters = parse(["-art"], { - string: ["a", "t"], - }); - - assertEquals(letters.a, ""); - assertEquals(letters.r, true); - assertEquals(letters.t, ""); -}); - -Deno.test("stringAndAlias", function (): void { - const x = parse(["--str", "000123"], { - string: "s", - alias: { s: "str" }, - }); - - assertEquals(x.str, "000123"); - assertEquals(typeof x.str, "string"); - assertEquals(x.s, "000123"); - assertEquals(typeof x.s, "string"); - - const y = parse(["-s", "000123"], { - string: "str", - alias: { str: "s" }, - }); - - assertEquals(y.str, "000123"); - assertEquals(typeof y.str, "string"); - assertEquals(y.s, "000123"); - assertEquals(typeof y.s, "string"); -}); - -Deno.test("slashBreak", function (): void { - assertEquals(parse(["-I/foo/bar/baz"]), { I: "/foo/bar/baz", _: [] }); - assertEquals(parse(["-xyz/foo/bar/baz"]), { - x: true, - y: true, - z: "/foo/bar/baz", - _: [], - }); -}); - -Deno.test("alias", function (): void { - const argv = parse(["-f", "11", "--zoom", "55"], { - alias: { z: "zoom" }, - }); - assertEquals(argv.zoom, 55); - assertEquals(argv.z, argv.zoom); - assertEquals(argv.f, 11); -}); - -Deno.test("multiAlias", function (): void { - const argv = parse(["-f", "11", "--zoom", "55"], { - alias: { z: ["zm", "zoom"] }, - }); - assertEquals(argv.zoom, 55); - assertEquals(argv.z, argv.zoom); - assertEquals(argv.z, argv.zm); - assertEquals(argv.f, 11); -}); - -Deno.test("nestedDottedObjects", function (): void { - const argv = parse([ - "--foo.bar", - "3", - "--foo.baz", - "4", - "--foo.quux.quibble", - "5", - "--foo.quux.oO", - "--beep.boop", - ]); - - assertEquals(argv.foo, { - bar: 3, - baz: 4, - quux: { - quibble: 5, - oO: true, - }, - }); - assertEquals(argv.beep, { boop: true }); -}); - -Deno.test("flagBuiltinProperty", function (): void { - const argv = parse(["--toString", "--valueOf", "foo"]); - assertEquals(argv, { toString: true, valueOf: "foo", _: [] }); - assertEquals(typeof argv.toString, "boolean"); - assertEquals(typeof argv.valueOf, "string"); -}); diff --git a/std/flags/short_test.ts b/std/flags/short_test.ts deleted file mode 100755 index 9f7936f98e..0000000000 --- a/std/flags/short_test.ts +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse } from "./mod.ts"; - -Deno.test("numbericShortArgs", function (): void { - assertEquals(parse(["-n123"]), { n: 123, _: [] }); - assertEquals(parse(["-123", "456"]), { 1: true, 2: true, 3: 456, _: [] }); -}); - -Deno.test("short", function (): void { - assertEquals(parse(["-b"]), { b: true, _: [] }); - assertEquals(parse(["foo", "bar", "baz"]), { _: ["foo", "bar", "baz"] }); - assertEquals(parse(["-cats"]), { c: true, a: true, t: true, s: true, _: [] }); - assertEquals(parse(["-cats", "meow"]), { - c: true, - a: true, - t: true, - s: "meow", - _: [], - }); - assertEquals(parse(["-h", "localhost"]), { h: "localhost", _: [] }); - assertEquals(parse(["-h", "localhost", "-p", "555"]), { - h: "localhost", - p: 555, - _: [], - }); -}); - -Deno.test("mixedShortBoolAndCapture", function (): void { - assertEquals(parse(["-h", "localhost", "-fp", "555", "script.js"]), { - f: true, - p: 555, - h: "localhost", - _: ["script.js"], - }); -}); - -Deno.test("shortAndLong", function (): void { - assertEquals(parse(["-h", "localhost", "-fp", "555", "script.js"]), { - f: true, - p: 555, - h: "localhost", - _: ["script.js"], - }); -}); diff --git a/std/flags/stop_early_test.ts b/std/flags/stop_early_test.ts deleted file mode 100755 index f7e23881a1..0000000000 --- a/std/flags/stop_early_test.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse } from "./mod.ts"; - -// stops parsing on the first non-option when stopEarly is set -Deno.test("stopParsing", function (): void { - const argv = parse(["--aaa", "bbb", "ccc", "--ddd"], { - stopEarly: true, - }); - - assertEquals(argv, { - aaa: "bbb", - _: ["ccc", "--ddd"], - }); -}); diff --git a/std/flags/test.ts b/std/flags/test.ts deleted file mode 100644 index 590417055f..0000000000 --- a/std/flags/test.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import "./mod.ts"; diff --git a/std/flags/unknown_test.ts b/std/flags/unknown_test.ts deleted file mode 100755 index 4f79215b68..0000000000 --- a/std/flags/unknown_test.ts +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse } from "./mod.ts"; - -Deno.test("booleanAndAliasIsNotUnknown", function (): void { - const unknown: unknown[] = []; - function unknownFn(arg: string, k?: string, v?: unknown): boolean { - unknown.push({ arg, k, v }); - return false; - } - const aliased = ["-h", "true", "--derp", "true"]; - const regular = ["--herp", "true", "-d", "false"]; - const opts = { - alias: { h: "herp" }, - boolean: "h", - unknown: unknownFn, - }; - parse(aliased, opts); - parse(regular, opts); - - assertEquals(unknown, [ - { arg: "--derp", k: "derp", v: "true" }, - { arg: "-d", k: "d", v: "false" }, - ]); -}); - -Deno.test( - "flagBooleanTrueAnyDoubleHyphenArgumentIsNotUnknown", - function (): void { - const unknown: unknown[] = []; - function unknownFn(arg: string, k?: string, v?: unknown): boolean { - unknown.push({ arg, k, v }); - return false; - } - const argv = parse(["--honk", "--tacos=good", "cow", "-p", "55"], { - boolean: true, - unknown: unknownFn, - }); - assertEquals(unknown, [ - { arg: "--tacos=good", k: "tacos", v: "good" }, - { arg: "cow", k: undefined, v: undefined }, - { arg: "-p", k: "p", v: "55" }, - ]); - assertEquals(argv, { - honk: true, - _: [], - }); - }, -); - -Deno.test("stringAndAliasIsNotUnkown", function (): void { - const unknown: unknown[] = []; - function unknownFn(arg: string, k?: string, v?: unknown): boolean { - unknown.push({ arg, k, v }); - return false; - } - const aliased = ["-h", "hello", "--derp", "goodbye"]; - const regular = ["--herp", "hello", "-d", "moon"]; - const opts = { - alias: { h: "herp" }, - string: "h", - unknown: unknownFn, - }; - parse(aliased, opts); - parse(regular, opts); - - assertEquals(unknown, [ - { arg: "--derp", k: "derp", v: "goodbye" }, - { arg: "-d", k: "d", v: "moon" }, - ]); -}); - -Deno.test("defaultAndAliasIsNotUnknown", function (): void { - const unknown: unknown[] = []; - function unknownFn(arg: string, k?: string, v?: unknown): boolean { - unknown.push({ arg, k, v }); - return false; - } - const aliased = ["-h", "hello"]; - const regular = ["--herp", "hello"]; - const opts = { - default: { h: "bar" }, - alias: { h: "herp" }, - unknown: unknownFn, - }; - parse(aliased, opts); - parse(regular, opts); - - assertEquals(unknown, []); -}); - -Deno.test("valueFollowingDoubleHyphenIsNotUnknown", function (): void { - const unknown: unknown[] = []; - function unknownFn(arg: string, k?: string, v?: unknown): boolean { - unknown.push({ arg, k, v }); - return false; - } - const aliased = ["--bad", "--", "good", "arg"]; - const opts = { - "--": true, - unknown: unknownFn, - }; - const argv = parse(aliased, opts); - - assertEquals(unknown, [{ arg: "--bad", k: "bad", v: true }]); - assertEquals(argv, { - "--": ["good", "arg"], - _: [], - }); -}); diff --git a/std/flags/whitespace_test.ts b/std/flags/whitespace_test.ts deleted file mode 100755 index bb9aab1f43..0000000000 --- a/std/flags/whitespace_test.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse } from "./mod.ts"; - -Deno.test("whitespaceShouldBeWhitespace", function (): void { - assertEquals(parse(["-x", "\t"]).x, "\t"); -}); diff --git a/std/fmt/README.md b/std/fmt/README.md deleted file mode 100644 index 8c44d78a77..0000000000 --- a/std/fmt/README.md +++ /dev/null @@ -1,240 +0,0 @@ -# Printf for Deno - -This is very much a work-in-progress. I'm actively soliciting feedback. What -immediately follows are points for discussion. - -If you are looking for the documentation proper, skip to: - - "printf: prints formatted output" - -and - - "Colors" - -below. - -## Discussion - -This is very much a work-in-progress. I'm actively soliciting feedback. - -- What useful features are available in other languages apart from Golang and C? - -- behaviour of `%v` verb. In Golang, this is a shortcut verb to "print the - default format" of the argument. It is currently implemented to format using - `toString` in the default case and `inspect` if the `%#v` alternative format - flag is used in the format directive. Alternatively, `%V` could be used to - distinguish the two. - - `inspect` output is not defined, however. This may be problematic if using - this code on other platforms (and expecting interoperability). To my - knowledge, no suitable specification of object representation aside from JSON - and `toString` exist. ( Aside: see "[Common object formats][3]" in the - "Console Living Standard" which basically says "do whatever" ) - -- `%j` verb. This is an extension particular to this implementation. Currently - not very sophisticated, it just runs `JSON.stringify` on the argument. - Consider possible modifier flags, etc. - -- `<` verb. This is an extension that assumes the argument is an array and will - format each element according to the format (surrounded by [] and separated by - comma) (`<` Mnemonic: pull each element out of array) - -- how to deal with more newfangled JavaScript features (generic Iterables, Map - and Set types, typed Arrays, ...) - -- the implementation is fairly rough around the edges: - -- currently contains little in the way of checking for correctness. Conceivably, - there will be a 'strict' form, e.g. that ensures only Number-ish arguments are - passed to %f flags. - -- assembles output using string concatenation instead of utilizing buffers or - other optimizations. It would be nice to have printf / sprintf / fprintf (etc) - all in one. - -- float formatting is handled by toString() and to `toExponential` along with a - mess of Regexp. Would be nice to use fancy match. - -- some flags that are potentially applicable ( POSIX long and unsigned modifiers - are not likely useful) are missing, namely %q (print quoted), %U (unicode - format) - -# printf: prints formatted output - -sprintf converts and formats a variable number of arguments as is specified by a -`format string`. In it's basic form, a format string may just be a literal. In -case arguments are meant to be formatted, a `directive` is contained in the -format string, preceded by a '%' character: - - % - -E.g. the verb `s` indicates the directive should be replaced by the string -representation of the argument in the corresponding position of the argument -list. E.g.: - - Hello %s! - -applied to the arguments "World" yields "Hello World!". - -The meaning of the format string is modelled after [POSIX][1] format strings as -well as well as [Golang format strings][2]. Both contain elements specific to -the respective programming language that don't apply to JavaScript, so they can -not be fully supported. Furthermore we implement some functionality that is -specific to JS. - -## Verbs - -The following verbs are supported: - -| Verb | Meaning | -| ----- | -------------------------------------------------------------- | -| `%` | print a literal percent | -| `t` | evaluate arg as boolean, print `true` or `false` | -| `b` | eval as number, print binary | -| `c` | eval as number, print character corresponding to the codePoint | -| `o` | eval as number, print octal | -| `x X` | print as hex (ff FF), treat string as list of bytes | -| `e E` | print number in scientific/exponent format 1.123123e+01 | -| `f F` | print number as float with decimal point and no exponent | -| `g G` | use %e %E or %f %F depending on size of argument | -| `s` | interpolate string | -| `T` | type of arg, as returned by `typeof` | -| `v` | value of argument in 'default' format (see below) | -| `j` | argument as formatted by `JSON.stringify` | - -## Width and Precision - -Verbs may be modified by providing them with width and precision, either or both -may be omitted: - - %9f width 9, default precision - %.9f default width, precision 9 - %8.9f width 8, precision 9 - %8.f width 9, precision 0 - -In general, 'width' describes the minimum length of the output, while -'precision' limits the output. - -| verb | precision | -| --------- | --------------------------------------------------------------- | -| `t` | n/a | -| `b c o` | n/a | -| `x X` | n/a for number, strings are truncated to p bytes(!) | -| `e E f F` | number of places after decimal, default 6 | -| `g G` | set maximum number of digits | -| `s` | truncate input | -| `T` | truncate | -| `v` | truncate, or depth if used with # see "'default' format", below | -| `j` | n/a | - -Numerical values for width and precision can be substituted for the `*` char, in -which case the values are obtained from the next args, e.g.: - - sprintf("%*.*f", 9, 8, 456.0) - -is equivalent to: - - sprintf("%9.8f", 456.0) - -## Flags - -The effects of the verb may be further influenced by using flags to modify the -directive: - -| Flag | Verb | Meaning | -| ----- | --------- | -------------------------------------------------------------------------- | -| `+` | numeric | always print sign | -| `-` | all | pad to the right (left justify) | -| `#` | | alternate format | -| `#` | `b o x X` | prefix with `0b 0 0x` | -| `#` | `g G` | don't remove trailing zeros | -| `#` | `v` | ues output of `inspect` instead of `toString` | -| `' '` | | space character | -| `' '` | `x X` | leave spaces between bytes when printing string | -| `' '` | `d` | insert space for missing `+` sign character | -| `0` | all | pad with zero, `-` takes precedence, sign is appended in front of padding | -| `<` | all | format elements of the passed array according to the directive (extension) | - -## 'default' format - -The default format used by `%v` is the result of calling `toString()` on the -relevant argument. If the `#` flags is used, the result of calling `inspect()` -is interpolated. In this case, the precision, if set is passed to `inspect()` as -the 'depth' config parameter. - -## Positional arguments - -Arguments do not need to be consumed in the order they are provided and may be -consumed more than once. E.g.: - - sprintf("%[2]s %[1]s", "World", "Hello") - -returns "Hello World". The presence of a positional indicator resets the arg -counter allowing args to be reused: - - sprintf("dec[%d]=%d hex[%[1]d]=%x oct[%[1]d]=%#o %s", 1, 255, "Third") - -returns `dec[1]=255 hex[1]=0xff oct[1]=0377 Third` - -Width and precision my also use positionals: - - "%[2]*.[1]*d", 1, 2 - -This follows the golang conventions and not POSIX. - -## Errors - -The following errors are handled: - -Incorrect verb: - - S("%h", "") %!(BAD VERB 'h') - -Too few arguments: - - S("%d") %!(MISSING 'd')" - -# Colors - -Adds functions used for displaying colored text. - -## Usage - -```typescript -import { - bgBlue, - bgRgb24, - bgRgb8, - bold, - italic, - red, - rgb24, - rgb8, -} from "https://deno.land/std@$STD_VERSION/fmt/colors.ts"; - -console.log(bgBlue(italic(red(bold("Hello, World!"))))); - -// also supports 8bit colors - -console.log(rgb8("Hello, World!", 42)); - -console.log(bgRgb8("Hello, World!", 42)); - -// and 24bit rgb - -console.log(rgb24("Hello, World!", { - r: 41, - g: 42, - b: 43, -})); - -console.log(bgRgb24("Hello, World!", { - r: 41, - g: 42, - b: 43, -})); -``` - -[1]: https://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html -[2]: https://golang.org/pkg/fmt/ -[3]: https://console.spec.whatwg.org/#object-formats diff --git a/std/fmt/TODO b/std/fmt/TODO deleted file mode 100644 index 7f95c7a78a..0000000000 --- a/std/fmt/TODO +++ /dev/null @@ -1,13 +0,0 @@ - -* "native" formatting, json, arrays, object/structs, functions ... -* %q %U -* Java has a %n flag to print the platform native newline... in POSIX - that means "number of chars printed so far", though. -* Use of Writer and Buffer internally in order to make fprintf, printf, etc. - easier and more elegant. - -* See "Discussion" in README - -* scanf, pack, unpack, annotated hex -* Consistent error handling. -* Rewrite (probably), now that I know how it's done. diff --git a/std/fmt/colors.ts b/std/fmt/colors.ts deleted file mode 100644 index 3596638f56..0000000000 --- a/std/fmt/colors.ts +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// A module to print ANSI terminal colors. Inspired by chalk, kleur, and colors -// on npm. -// -// ``` -// import { bgBlue, red, bold } from "https://deno.land/std/fmt/colors.ts"; -// console.log(bgBlue(red(bold("Hello world!")))); -// ``` -// -// This module supports `NO_COLOR` environmental variable disabling any coloring -// if `NO_COLOR` is set. -// -// This module is browser compatible. - -const noColor = globalThis.Deno?.noColor ?? true; - -interface Code { - open: string; - close: string; - regexp: RegExp; -} - -/** RGB 8-bits per channel. Each in range `0->255` or `0x00->0xff` */ -interface Rgb { - r: number; - g: number; - b: number; -} - -let enabled = !noColor; - -/** - * Set changing text color to enabled or disabled - * @param value - */ -export function setColorEnabled(value: boolean): void { - if (noColor) { - return; - } - - enabled = value; -} - -/** Get whether text color change is enabled or disabled. */ -export function getColorEnabled(): boolean { - return enabled; -} - -/** - * Builds color code - * @param open - * @param close - */ -function code(open: number[], close: number): Code { - return { - open: `\x1b[${open.join(";")}m`, - close: `\x1b[${close}m`, - regexp: new RegExp(`\\x1b\\[${close}m`, "g"), - }; -} - -/** - * Applies color and background based on color code and its associated text - * @param str text to apply color settings to - * @param code color code to apply - */ -function run(str: string, code: Code): string { - return enabled - ? `${code.open}${str.replace(code.regexp, code.open)}${code.close}` - : str; -} - -/** - * Reset the text modified - * @param str text to reset - */ -export function reset(str: string): string { - return run(str, code([0], 0)); -} - -/** - * Make the text bold. - * @param str text to make bold - */ -export function bold(str: string): string { - return run(str, code([1], 22)); -} - -/** - * The text emits only a small amount of light. - * @param str text to dim - */ -export function dim(str: string): string { - return run(str, code([2], 22)); -} - -/** - * Make the text italic. - * @param str text to make italic - */ -export function italic(str: string): string { - return run(str, code([3], 23)); -} - -/** - * Make the text underline. - * @param str text to underline - */ -export function underline(str: string): string { - return run(str, code([4], 24)); -} - -/** - * Invert background color and text color. - * @param str text to invert its color - */ -export function inverse(str: string): string { - return run(str, code([7], 27)); -} - -/** - * Make the text hidden. - * @param str text to hide - */ -export function hidden(str: string): string { - return run(str, code([8], 28)); -} - -/** - * Put horizontal line through the center of the text. - * @param str text to strike through - */ -export function strikethrough(str: string): string { - return run(str, code([9], 29)); -} - -/** - * Set text color to black. - * @param str text to make black - */ -export function black(str: string): string { - return run(str, code([30], 39)); -} - -/** - * Set text color to red. - * @param str text to make red - */ -export function red(str: string): string { - return run(str, code([31], 39)); -} - -/** - * Set text color to green. - * @param str text to make green - */ -export function green(str: string): string { - return run(str, code([32], 39)); -} - -/** - * Set text color to yellow. - * @param str text to make yellow - */ -export function yellow(str: string): string { - return run(str, code([33], 39)); -} - -/** - * Set text color to blue. - * @param str text to make blue - */ -export function blue(str: string): string { - return run(str, code([34], 39)); -} - -/** - * Set text color to magenta. - * @param str text to make magenta - */ -export function magenta(str: string): string { - return run(str, code([35], 39)); -} - -/** - * Set text color to cyan. - * @param str text to make cyan - */ -export function cyan(str: string): string { - return run(str, code([36], 39)); -} - -/** - * Set text color to white. - * @param str text to make white - */ -export function white(str: string): string { - return run(str, code([37], 39)); -} - -/** - * Set text color to gray. - * @param str text to make gray - */ -export function gray(str: string): string { - return brightBlack(str); -} - -/** - * Set text color to bright black. - * @param str text to make bright-black - */ -export function brightBlack(str: string): string { - return run(str, code([90], 39)); -} - -/** - * Set text color to bright red. - * @param str text to make bright-red - */ -export function brightRed(str: string): string { - return run(str, code([91], 39)); -} - -/** - * Set text color to bright green. - * @param str text to make bright-green - */ -export function brightGreen(str: string): string { - return run(str, code([92], 39)); -} - -/** - * Set text color to bright yellow. - * @param str text to make bright-yellow - */ -export function brightYellow(str: string): string { - return run(str, code([93], 39)); -} - -/** - * Set text color to bright blue. - * @param str text to make bright-blue - */ -export function brightBlue(str: string): string { - return run(str, code([94], 39)); -} - -/** - * Set text color to bright magenta. - * @param str text to make bright-magenta - */ -export function brightMagenta(str: string): string { - return run(str, code([95], 39)); -} - -/** - * Set text color to bright cyan. - * @param str text to make bright-cyan - */ -export function brightCyan(str: string): string { - return run(str, code([96], 39)); -} - -/** - * Set text color to bright white. - * @param str text to make bright-white - */ -export function brightWhite(str: string): string { - return run(str, code([97], 39)); -} - -/** - * Set background color to black. - * @param str text to make its background black - */ -export function bgBlack(str: string): string { - return run(str, code([40], 49)); -} - -/** - * Set background color to red. - * @param str text to make its background red - */ -export function bgRed(str: string): string { - return run(str, code([41], 49)); -} - -/** - * Set background color to green. - * @param str text to make its background green - */ -export function bgGreen(str: string): string { - return run(str, code([42], 49)); -} - -/** - * Set background color to yellow. - * @param str text to make its background yellow - */ -export function bgYellow(str: string): string { - return run(str, code([43], 49)); -} - -/** - * Set background color to blue. - * @param str text to make its background blue - */ -export function bgBlue(str: string): string { - return run(str, code([44], 49)); -} - -/** - * Set background color to magenta. - * @param str text to make its background magenta - */ -export function bgMagenta(str: string): string { - return run(str, code([45], 49)); -} - -/** - * Set background color to cyan. - * @param str text to make its background cyan - */ -export function bgCyan(str: string): string { - return run(str, code([46], 49)); -} - -/** - * Set background color to white. - * @param str text to make its background white - */ -export function bgWhite(str: string): string { - return run(str, code([47], 49)); -} - -/** - * Set background color to bright black. - * @param str text to make its background bright-black - */ -export function bgBrightBlack(str: string): string { - return run(str, code([100], 49)); -} - -/** - * Set background color to bright red. - * @param str text to make its background bright-red - */ -export function bgBrightRed(str: string): string { - return run(str, code([101], 49)); -} - -/** - * Set background color to bright green. - * @param str text to make its background bright-green - */ -export function bgBrightGreen(str: string): string { - return run(str, code([102], 49)); -} - -/** - * Set background color to bright yellow. - * @param str text to make its background bright-yellow - */ -export function bgBrightYellow(str: string): string { - return run(str, code([103], 49)); -} - -/** - * Set background color to bright blue. - * @param str text to make its background bright-blue - */ -export function bgBrightBlue(str: string): string { - return run(str, code([104], 49)); -} - -/** - * Set background color to bright magenta. - * @param str text to make its background bright-magenta - */ -export function bgBrightMagenta(str: string): string { - return run(str, code([105], 49)); -} - -/** - * Set background color to bright cyan. - * @param str text to make its background bright-cyan - */ -export function bgBrightCyan(str: string): string { - return run(str, code([106], 49)); -} - -/** - * Set background color to bright white. - * @param str text to make its background bright-white - */ -export function bgBrightWhite(str: string): string { - return run(str, code([107], 49)); -} - -/* Special Color Sequences */ - -/** - * Clam and truncate color codes - * @param n - * @param max number to truncate to - * @param min number to truncate from - */ -function clampAndTruncate(n: number, max = 255, min = 0): number { - return Math.trunc(Math.max(Math.min(n, max), min)); -} - -/** - * Set text color using paletted 8bit colors. - * https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit - * @param str text color to apply paletted 8bit colors to - * @param color code - */ -export function rgb8(str: string, color: number): string { - return run(str, code([38, 5, clampAndTruncate(color)], 39)); -} - -/** - * Set background color using paletted 8bit colors. - * https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit - * @param str text color to apply paletted 8bit background colors to - * @param color code - */ -export function bgRgb8(str: string, color: number): string { - return run(str, code([48, 5, clampAndTruncate(color)], 49)); -} - -/** - * Set text color using 24bit rgb. - * `color` can be a number in range `0x000000` to `0xffffff` or - * an `Rgb`. - * - * To produce the color magenta: - * - * rgba24("foo", 0xff00ff); - * rgba24("foo", {r: 255, g: 0, b: 255}); - * @param str text color to apply 24bit rgb to - * @param color code - */ -export function rgb24(str: string, color: number | Rgb): string { - if (typeof color === "number") { - return run( - str, - code( - [38, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], - 39, - ), - ); - } - return run( - str, - code( - [ - 38, - 2, - clampAndTruncate(color.r), - clampAndTruncate(color.g), - clampAndTruncate(color.b), - ], - 39, - ), - ); -} - -/** - * Set background color using 24bit rgb. - * `color` can be a number in range `0x000000` to `0xffffff` or - * an `Rgb`. - * - * To produce the color magenta: - * - * bgRgba24("foo", 0xff00ff); - * bgRgba24("foo", {r: 255, g: 0, b: 255}); - * @param str text color to apply 24bit rgb to - * @param color code - */ -export function bgRgb24(str: string, color: number | Rgb): string { - if (typeof color === "number") { - return run( - str, - code( - [48, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], - 49, - ), - ); - } - return run( - str, - code( - [ - 48, - 2, - clampAndTruncate(color.r), - clampAndTruncate(color.g), - clampAndTruncate(color.b), - ], - 49, - ), - ); -} - -// https://github.com/chalk/ansi-regex/blob/2b56fb0c7a07108e5b54241e8faec160d393aedb/index.js -const ANSI_PATTERN = new RegExp( - [ - "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", - "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))", - ].join("|"), - "g", -); - -/** - * Remove ANSI escape codes from the string. - * @param string to remove ANSI escape codes from - */ -export function stripColor(string: string): string { - return string.replace(ANSI_PATTERN, ""); -} diff --git a/std/fmt/colors_test.ts b/std/fmt/colors_test.ts deleted file mode 100644 index 7f5940a4d2..0000000000 --- a/std/fmt/colors_test.ts +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import * as c from "./colors.ts"; -import "../examples/colors.ts"; - -Deno.test("singleColor", function (): void { - assertEquals(c.red("foo bar"), "foo bar"); -}); - -Deno.test("doubleColor", function (): void { - assertEquals(c.bgBlue(c.red("foo bar")), "foo bar"); -}); - -Deno.test("replacesCloseCharacters", function (): void { - assertEquals(c.red("Hello"), "Hello"); -}); - -Deno.test("enablingColors", function (): void { - assertEquals(c.getColorEnabled(), true); - c.setColorEnabled(false); - assertEquals(c.bgBlue(c.red("foo bar")), "foo bar"); - c.setColorEnabled(true); - assertEquals(c.red("foo bar"), "foo bar"); -}); - -Deno.test("testBold", function (): void { - assertEquals(c.bold("foo bar"), "foo bar"); -}); - -Deno.test("testDim", function (): void { - assertEquals(c.dim("foo bar"), "foo bar"); -}); - -Deno.test("testItalic", function (): void { - assertEquals(c.italic("foo bar"), "foo bar"); -}); - -Deno.test("testUnderline", function (): void { - assertEquals(c.underline("foo bar"), "foo bar"); -}); - -Deno.test("testInverse", function (): void { - assertEquals(c.inverse("foo bar"), "foo bar"); -}); - -Deno.test("testHidden", function (): void { - assertEquals(c.hidden("foo bar"), "foo bar"); -}); - -Deno.test("testStrikethrough", function (): void { - assertEquals(c.strikethrough("foo bar"), "foo bar"); -}); - -Deno.test("testBlack", function (): void { - assertEquals(c.black("foo bar"), "foo bar"); -}); - -Deno.test("testRed", function (): void { - assertEquals(c.red("foo bar"), "foo bar"); -}); - -Deno.test("testGreen", function (): void { - assertEquals(c.green("foo bar"), "foo bar"); -}); - -Deno.test("testYellow", function (): void { - assertEquals(c.yellow("foo bar"), "foo bar"); -}); - -Deno.test("testBlue", function (): void { - assertEquals(c.blue("foo bar"), "foo bar"); -}); - -Deno.test("testMagenta", function (): void { - assertEquals(c.magenta("foo bar"), "foo bar"); -}); - -Deno.test("testCyan", function (): void { - assertEquals(c.cyan("foo bar"), "foo bar"); -}); - -Deno.test("testWhite", function (): void { - assertEquals(c.white("foo bar"), "foo bar"); -}); - -Deno.test("testGray", function (): void { - assertEquals(c.gray("foo bar"), "foo bar"); -}); - -Deno.test("testBrightBlack", function (): void { - assertEquals(c.brightBlack("foo bar"), "foo bar"); -}); - -Deno.test("testBrightRed", function (): void { - assertEquals(c.brightRed("foo bar"), "foo bar"); -}); - -Deno.test("testBrightGreen", function (): void { - assertEquals(c.brightGreen("foo bar"), "foo bar"); -}); - -Deno.test("testBrightYellow", function (): void { - assertEquals(c.brightYellow("foo bar"), "foo bar"); -}); - -Deno.test("testBrightBlue", function (): void { - assertEquals(c.brightBlue("foo bar"), "foo bar"); -}); - -Deno.test("testBrightMagenta", function (): void { - assertEquals(c.brightMagenta("foo bar"), "foo bar"); -}); - -Deno.test("testBrightCyan", function (): void { - assertEquals(c.brightCyan("foo bar"), "foo bar"); -}); - -Deno.test("testBrightWhite", function (): void { - assertEquals(c.brightWhite("foo bar"), "foo bar"); -}); - -Deno.test("testBgBlack", function (): void { - assertEquals(c.bgBlack("foo bar"), "foo bar"); -}); - -Deno.test("testBgRed", function (): void { - assertEquals(c.bgRed("foo bar"), "foo bar"); -}); - -Deno.test("testBgGreen", function (): void { - assertEquals(c.bgGreen("foo bar"), "foo bar"); -}); - -Deno.test("testBgYellow", function (): void { - assertEquals(c.bgYellow("foo bar"), "foo bar"); -}); - -Deno.test("testBgBlue", function (): void { - assertEquals(c.bgBlue("foo bar"), "foo bar"); -}); - -Deno.test("testBgMagenta", function (): void { - assertEquals(c.bgMagenta("foo bar"), "foo bar"); -}); - -Deno.test("testBgCyan", function (): void { - assertEquals(c.bgCyan("foo bar"), "foo bar"); -}); - -Deno.test("testBgWhite", function (): void { - assertEquals(c.bgWhite("foo bar"), "foo bar"); -}); - -Deno.test("testBgBrightBlack", function (): void { - assertEquals(c.bgBrightBlack("foo bar"), "foo bar"); -}); - -Deno.test("testBgBrightRed", function (): void { - assertEquals(c.bgBrightRed("foo bar"), "foo bar"); -}); - -Deno.test("testBgBrightGreen", function (): void { - assertEquals(c.bgBrightGreen("foo bar"), "foo bar"); -}); - -Deno.test("testBgBrightYellow", function (): void { - assertEquals(c.bgBrightYellow("foo bar"), "foo bar"); -}); - -Deno.test("testBgBrightBlue", function (): void { - assertEquals(c.bgBrightBlue("foo bar"), "foo bar"); -}); - -Deno.test("testBgBrightMagenta", function (): void { - assertEquals(c.bgBrightMagenta("foo bar"), "foo bar"); -}); - -Deno.test("testBgBrightCyan", function (): void { - assertEquals(c.bgBrightCyan("foo bar"), "foo bar"); -}); - -Deno.test("testBgBrightWhite", function (): void { - assertEquals(c.bgBrightWhite("foo bar"), "foo bar"); -}); - -Deno.test("testClampUsingRgb8", function (): void { - assertEquals(c.rgb8("foo bar", -10), "foo bar"); -}); - -Deno.test("testTruncateUsingRgb8", function (): void { - assertEquals(c.rgb8("foo bar", 42.5), "foo bar"); -}); - -Deno.test("testRgb8", function (): void { - assertEquals(c.rgb8("foo bar", 42), "foo bar"); -}); - -Deno.test("test_bgRgb8", function (): void { - assertEquals(c.bgRgb8("foo bar", 42), "foo bar"); -}); - -Deno.test("test_rgb24", function (): void { - assertEquals( - c.rgb24("foo bar", { - r: 41, - g: 42, - b: 43, - }), - "foo bar", - ); -}); - -Deno.test("test_rgb24number", function (): void { - assertEquals(c.rgb24("foo bar", 0x070809), "foo bar"); -}); - -Deno.test("test_bgRgb24", function (): void { - assertEquals( - c.bgRgb24("foo bar", { - r: 41, - g: 42, - b: 43, - }), - "foo bar", - ); -}); - -Deno.test("test_bgRgb24number", function (): void { - assertEquals(c.bgRgb24("foo bar", 0x070809), "foo bar"); -}); diff --git a/std/fmt/printf.ts b/std/fmt/printf.ts deleted file mode 100644 index 6a821f2089..0000000000 --- a/std/fmt/printf.ts +++ /dev/null @@ -1,757 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -/** - * This implementation is inspired by POSIX and Golang but does not port - * implementation code. */ - -enum State { - PASSTHROUGH, - PERCENT, - POSITIONAL, - PRECISION, - WIDTH, -} - -enum WorP { - WIDTH, - PRECISION, -} - -class Flags { - plus?: boolean; - dash?: boolean; - sharp?: boolean; - space?: boolean; - zero?: boolean; - lessthan?: boolean; - width = -1; - precision = -1; -} - -const min = Math.min; -const UNICODE_REPLACEMENT_CHARACTER = "\ufffd"; -const DEFAULT_PRECISION = 6; -const FLOAT_REGEXP = /(-?)(\d)\.?(\d*)e([+-])(\d+)/; - -enum F { - sign = 1, - mantissa, - fractional, - esign, - exponent, -} - -class Printf { - format: string; - args: unknown[]; - i: number; - - state: State = State.PASSTHROUGH; - verb = ""; - buf = ""; - argNum = 0; - flags: Flags = new Flags(); - - haveSeen: boolean[]; - - // barf, store precision and width errors for later processing ... - tmpError?: string; - - constructor(format: string, ...args: unknown[]) { - this.format = format; - this.args = args; - this.haveSeen = new Array(args.length); - this.i = 0; - } - - doPrintf(): string { - for (; this.i < this.format.length; ++this.i) { - const c = this.format[this.i]; - switch (this.state) { - case State.PASSTHROUGH: - if (c === "%") { - this.state = State.PERCENT; - } else { - this.buf += c; - } - break; - case State.PERCENT: - if (c === "%") { - this.buf += c; - this.state = State.PASSTHROUGH; - } else { - this.handleFormat(); - } - break; - default: - throw Error("Should be unreachable, certainly a bug in the lib."); - } - } - // check for unhandled args - let extras = false; - let err = "%!(EXTRA"; - for (let i = 0; i !== this.haveSeen.length; ++i) { - if (!this.haveSeen[i]) { - extras = true; - err += ` '${Deno.inspect(this.args[i])}'`; - } - } - err += ")"; - if (extras) { - this.buf += err; - } - return this.buf; - } - - // %[]... - handleFormat(): void { - this.flags = new Flags(); - const flags = this.flags; - for (; this.i < this.format.length; ++this.i) { - const c = this.format[this.i]; - switch (this.state) { - case State.PERCENT: - switch (c) { - case "[": - this.handlePositional(); - this.state = State.POSITIONAL; - break; - case "+": - flags.plus = true; - break; - case "<": - flags.lessthan = true; - break; - case "-": - flags.dash = true; - flags.zero = false; // only left pad zeros, dash takes precedence - break; - case "#": - flags.sharp = true; - break; - case " ": - flags.space = true; - break; - case "0": - // only left pad zeros, dash takes precedence - flags.zero = !flags.dash; - break; - default: - if (("1" <= c && c <= "9") || c === "." || c === "*") { - if (c === ".") { - this.flags.precision = 0; - this.state = State.PRECISION; - this.i++; - } else { - this.state = State.WIDTH; - } - this.handleWidthAndPrecision(flags); - } else { - this.handleVerb(); - return; // always end in verb - } - } // switch c - break; - case State.POSITIONAL: - // TODO(bartlomieju): either a verb or * only verb for now - if (c === "*") { - const worp = this.flags.precision === -1 - ? WorP.WIDTH - : WorP.PRECISION; - this.handleWidthOrPrecisionRef(worp); - this.state = State.PERCENT; - break; - } else { - this.handleVerb(); - return; // always end in verb - } - default: - throw new Error(`Should not be here ${this.state}, library bug!`); - } // switch state - } - } - - /** - * Handle width or precision - * @param wOrP - */ - handleWidthOrPrecisionRef(wOrP: WorP): void { - if (this.argNum >= this.args.length) { - // handle Positional should have already taken care of it... - return; - } - const arg = this.args[this.argNum]; - this.haveSeen[this.argNum] = true; - if (typeof arg === "number") { - switch (wOrP) { - case WorP.WIDTH: - this.flags.width = arg; - break; - default: - this.flags.precision = arg; - } - } else { - const tmp = wOrP === WorP.WIDTH ? "WIDTH" : "PREC"; - this.tmpError = `%!(BAD ${tmp} '${this.args[this.argNum]}')`; - } - this.argNum++; - } - - /** - * Handle width and precision - * @param flags - */ - handleWidthAndPrecision(flags: Flags): void { - const fmt = this.format; - for (; this.i !== this.format.length; ++this.i) { - const c = fmt[this.i]; - switch (this.state) { - case State.WIDTH: - switch (c) { - case ".": - // initialize precision, %9.f -> precision=0 - this.flags.precision = 0; - this.state = State.PRECISION; - break; - case "*": - this.handleWidthOrPrecisionRef(WorP.WIDTH); - // force . or flag at this point - break; - default: { - const val = parseInt(c); - // most likely parseInt does something stupid that makes - // it unusable for this scenario ... - // if we encounter a non (number|*|.) we're done with prec & wid - if (isNaN(val)) { - this.i--; - this.state = State.PERCENT; - return; - } - flags.width = flags.width == -1 ? 0 : flags.width; - flags.width *= 10; - flags.width += val; - } - } // switch c - break; - case State.PRECISION: { - if (c === "*") { - this.handleWidthOrPrecisionRef(WorP.PRECISION); - break; - } - const val = parseInt(c); - if (isNaN(val)) { - // one too far, rewind - this.i--; - this.state = State.PERCENT; - return; - } - flags.precision *= 10; - flags.precision += val; - break; - } - default: - throw new Error("can't be here. bug."); - } // switch state - } - } - - /** Handle positional */ - handlePositional(): void { - if (this.format[this.i] !== "[") { - // sanity only - throw new Error("Can't happen? Bug."); - } - let positional = 0; - const format = this.format; - this.i++; - let err = false; - for (; this.i !== this.format.length; ++this.i) { - if (format[this.i] === "]") { - break; - } - positional *= 10; - const val = parseInt(format[this.i]); - if (isNaN(val)) { - //throw new Error( - // `invalid character in positional: ${format}[${format[this.i]}]` - //); - this.tmpError = "%!(BAD INDEX)"; - err = true; - } - positional += val; - } - if (positional - 1 >= this.args.length) { - this.tmpError = "%!(BAD INDEX)"; - err = true; - } - this.argNum = err ? this.argNum : positional - 1; - return; - } - - /** Handle less than */ - handleLessThan(): string { - // deno-lint-ignore no-explicit-any - const arg = this.args[this.argNum] as any; - if ((arg || {}).constructor.name !== "Array") { - throw new Error(`arg ${arg} is not an array. Todo better error handling`); - } - let str = "[ "; - for (let i = 0; i !== arg.length; ++i) { - if (i !== 0) str += ", "; - str += this._handleVerb(arg[i]); - } - return str + " ]"; - } - - /** Handle verb */ - handleVerb(): void { - const verb = this.format[this.i]; - this.verb = verb; - if (this.tmpError) { - this.buf += this.tmpError; - this.tmpError = undefined; - if (this.argNum < this.haveSeen.length) { - this.haveSeen[this.argNum] = true; // keep track of used args - } - } else if (this.args.length <= this.argNum) { - this.buf += `%!(MISSING '${verb}')`; - } else { - const arg = this.args[this.argNum]; // check out of range - this.haveSeen[this.argNum] = true; // keep track of used args - if (this.flags.lessthan) { - this.buf += this.handleLessThan(); - } else { - this.buf += this._handleVerb(arg); - } - } - this.argNum++; // if there is a further positional, it will reset. - this.state = State.PASSTHROUGH; - } - - // deno-lint-ignore no-explicit-any - _handleVerb(arg: any): string { - switch (this.verb) { - case "t": - return this.pad(arg.toString()); - case "b": - return this.fmtNumber(arg as number, 2); - case "c": - return this.fmtNumberCodePoint(arg as number); - case "d": - return this.fmtNumber(arg as number, 10); - case "o": - return this.fmtNumber(arg as number, 8); - case "x": - return this.fmtHex(arg); - case "X": - return this.fmtHex(arg, true); - case "e": - return this.fmtFloatE(arg as number); - case "E": - return this.fmtFloatE(arg as number, true); - case "f": - case "F": - return this.fmtFloatF(arg as number); - case "g": - return this.fmtFloatG(arg as number); - case "G": - return this.fmtFloatG(arg as number, true); - case "s": - return this.fmtString(arg as string); - case "T": - return this.fmtString(typeof arg); - case "v": - return this.fmtV(arg); - case "j": - return this.fmtJ(arg); - default: - return `%!(BAD VERB '${this.verb}')`; - } - } - - /** - * Pad a string - * @param s text to pad - */ - pad(s: string): string { - const padding = this.flags.zero ? "0" : " "; - - if (this.flags.dash) { - return s.padEnd(this.flags.width, padding); - } - - return s.padStart(this.flags.width, padding); - } - - /** - * Pad a number - * @param nStr - * @param neg - */ - padNum(nStr: string, neg: boolean): string { - let sign: string; - if (neg) { - sign = "-"; - } else if (this.flags.plus || this.flags.space) { - sign = this.flags.plus ? "+" : " "; - } else { - sign = ""; - } - const zero = this.flags.zero; - if (!zero) { - // sign comes in front of padding when padding w/ zero, - // in from of value if padding with spaces. - nStr = sign + nStr; - } - - const pad = zero ? "0" : " "; - const len = zero ? this.flags.width - sign.length : this.flags.width; - - if (this.flags.dash) { - nStr = nStr.padEnd(len, pad); - } else { - nStr = nStr.padStart(len, pad); - } - - if (zero) { - // see above - nStr = sign + nStr; - } - return nStr; - } - - /** - * Format a number - * @param n - * @param radix - * @param upcase - */ - fmtNumber(n: number, radix: number, upcase = false): string { - let num = Math.abs(n).toString(radix); - const prec = this.flags.precision; - if (prec !== -1) { - this.flags.zero = false; - num = n === 0 && prec === 0 ? "" : num; - while (num.length < prec) { - num = "0" + num; - } - } - let prefix = ""; - if (this.flags.sharp) { - switch (radix) { - case 2: - prefix += "0b"; - break; - case 8: - // don't annotate octal 0 with 0... - prefix += num.startsWith("0") ? "" : "0"; - break; - case 16: - prefix += "0x"; - break; - default: - throw new Error("cannot handle base: " + radix); - } - } - // don't add prefix in front of value truncated by precision=0, val=0 - num = num.length === 0 ? num : prefix + num; - if (upcase) { - num = num.toUpperCase(); - } - return this.padNum(num, n < 0); - } - - /** - * Format number with code points - * @param n - */ - fmtNumberCodePoint(n: number): string { - let s = ""; - try { - s = String.fromCodePoint(n); - } catch (RangeError) { - s = UNICODE_REPLACEMENT_CHARACTER; - } - return this.pad(s); - } - - /** - * Format special float - * @param n - */ - fmtFloatSpecial(n: number): string { - // formatting of NaN and Inf are pants-on-head - // stupid and more or less arbitrary. - - if (isNaN(n)) { - this.flags.zero = false; - return this.padNum("NaN", false); - } - if (n === Number.POSITIVE_INFINITY) { - this.flags.zero = false; - this.flags.plus = true; - return this.padNum("Inf", false); - } - if (n === Number.NEGATIVE_INFINITY) { - this.flags.zero = false; - return this.padNum("Inf", true); - } - return ""; - } - - /** - * Round fraction to precision - * @param fractional - * @param precision - */ - roundFractionToPrecision(fractional: string, precision: number): string { - if (fractional.length > precision) { - fractional = "1" + fractional; // prepend a 1 in case of leading 0 - let tmp = parseInt(fractional.substr(0, precision + 2)) / 10; - tmp = Math.round(tmp); - fractional = Math.floor(tmp).toString(); - fractional = fractional.substr(1); // remove extra 1 - } else { - while (fractional.length < precision) { - fractional += "0"; - } - } - return fractional; - } - - /** - * Format float E - * @param n - * @param upcase - */ - fmtFloatE(n: number, upcase = false): string { - const special = this.fmtFloatSpecial(n); - if (special !== "") { - return special; - } - - const m = n.toExponential().match(FLOAT_REGEXP); - if (!m) { - throw Error("can't happen, bug"); - } - - let fractional = m[F.fractional]; - const precision = this.flags.precision !== -1 - ? this.flags.precision - : DEFAULT_PRECISION; - fractional = this.roundFractionToPrecision(fractional, precision); - - let e = m[F.exponent]; - // scientific notation output with exponent padded to minlen 2 - e = e.length == 1 ? "0" + e : e; - - const val = `${m[F.mantissa]}.${fractional}${upcase ? "E" : "e"}${ - m[F.esign] - }${e}`; - return this.padNum(val, n < 0); - } - - /** - * Format float F - * @param n - */ - fmtFloatF(n: number): string { - const special = this.fmtFloatSpecial(n); - if (special !== "") { - return special; - } - - // stupid helper that turns a number into a (potentially) - // VERY long string. - function expandNumber(n: number): string { - if (Number.isSafeInteger(n)) { - return n.toString() + "."; - } - - const t = n.toExponential().split("e"); - let m = t[0].replace(".", ""); - const e = parseInt(t[1]); - if (e < 0) { - let nStr = "0."; - for (let i = 0; i !== Math.abs(e) - 1; ++i) { - nStr += "0"; - } - return (nStr += m); - } else { - const splIdx = e + 1; - while (m.length < splIdx) { - m += "0"; - } - return m.substr(0, splIdx) + "." + m.substr(splIdx); - } - } - // avoiding sign makes padding easier - const val = expandNumber(Math.abs(n)) as string; - const arr = val.split("."); - const dig = arr[0]; - let fractional = arr[1]; - - const precision = this.flags.precision !== -1 - ? this.flags.precision - : DEFAULT_PRECISION; - fractional = this.roundFractionToPrecision(fractional, precision); - - return this.padNum(`${dig}.${fractional}`, n < 0); - } - - /** - * Format float G - * @param n - * @param upcase - */ - fmtFloatG(n: number, upcase = false): string { - const special = this.fmtFloatSpecial(n); - if (special !== "") { - return special; - } - - // The double argument representing a floating-point number shall be - // converted in the style f or e (or in the style F or E in - // the case of a G conversion specifier), depending on the - // value converted and the precision. Let P equal the - // precision if non-zero, 6 if the precision is omitted, or 1 - // if the precision is zero. Then, if a conversion with style E would - // have an exponent of X: - - // - If P > X>=-4, the conversion shall be with style f (or F ) - // and precision P -( X+1). - - // - Otherwise, the conversion shall be with style e (or E ) - // and precision P -1. - - // Finally, unless the '#' flag is used, any trailing zeros shall be - // removed from the fractional portion of the result and the - // decimal-point character shall be removed if there is no - // fractional portion remaining. - - // A double argument representing an infinity or NaN shall be - // converted in the style of an f or F conversion specifier. - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html - - let P = this.flags.precision !== -1 - ? this.flags.precision - : DEFAULT_PRECISION; - P = P === 0 ? 1 : P; - - const m = n.toExponential().match(FLOAT_REGEXP); - if (!m) { - throw Error("can't happen"); - } - - const X = parseInt(m[F.exponent]) * (m[F.esign] === "-" ? -1 : 1); - let nStr = ""; - if (P > X && X >= -4) { - this.flags.precision = P - (X + 1); - nStr = this.fmtFloatF(n); - if (!this.flags.sharp) { - nStr = nStr.replace(/\.?0*$/, ""); - } - } else { - this.flags.precision = P - 1; - nStr = this.fmtFloatE(n); - if (!this.flags.sharp) { - nStr = nStr.replace(/\.?0*e/, upcase ? "E" : "e"); - } - } - return nStr; - } - - /** - * Format string - * @param s - */ - fmtString(s: string): string { - if (this.flags.precision !== -1) { - s = s.substr(0, this.flags.precision); - } - return this.pad(s); - } - - /** - * Format hex - * @param val - * @param upper - */ - fmtHex(val: string | number, upper = false): string { - // allow others types ? - switch (typeof val) { - case "number": - return this.fmtNumber(val as number, 16, upper); - case "string": { - const sharp = this.flags.sharp && val.length !== 0; - let hex = sharp ? "0x" : ""; - const prec = this.flags.precision; - const end = prec !== -1 ? min(prec, val.length) : val.length; - for (let i = 0; i !== end; ++i) { - if (i !== 0 && this.flags.space) { - hex += sharp ? " 0x" : " "; - } - // TODO(bartlomieju): for now only taking into account the - // lower half of the codePoint, ie. as if a string - // is a list of 8bit values instead of UCS2 runes - const c = (val.charCodeAt(i) & 0xff).toString(16); - hex += c.length === 1 ? `0${c}` : c; - } - if (upper) { - hex = hex.toUpperCase(); - } - return this.pad(hex); - } - default: - throw new Error( - "currently only number and string are implemented for hex", - ); - } - } - - /** - * Format value - * @param val - */ - fmtV(val: Record): string { - if (this.flags.sharp) { - const options = this.flags.precision !== -1 - ? { depth: this.flags.precision } - : {}; - return this.pad(Deno.inspect(val, options)); - } else { - const p = this.flags.precision; - return p === -1 ? val.toString() : val.toString().substr(0, p); - } - } - - /** - * Format JSON - * @param val - */ - fmtJ(val: unknown): string { - return JSON.stringify(val); - } -} - -/** - * Converts and format a variable number of `args` as is specified by `format`. - * `sprintf` returns the formatted string. - * - * @param format - * @param args - */ -export function sprintf(format: string, ...args: unknown[]): string { - const printf = new Printf(format, ...args); - return printf.doPrintf(); -} - -/** - * Converts and format a variable number of `args` as is specified by `format`. - * `printf` writes the formatted string to standard output. - * @param format - * @param args - */ -export function printf(format: string, ...args: unknown[]): void { - const s = sprintf(format, ...args); - Deno.stdout.writeSync(new TextEncoder().encode(s)); -} diff --git a/std/fmt/printf_test.ts b/std/fmt/printf_test.ts deleted file mode 100644 index f240858481..0000000000 --- a/std/fmt/printf_test.ts +++ /dev/null @@ -1,674 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// -// A number of test-cases based on: -// -// https://golang.org/src/fmt/fmt_test.go -// BSD: Copyright (c) 2009 The Go Authors. All rights reserved. - -import { sprintf } from "./printf.ts"; -import { assertEquals } from "../testing/asserts.ts"; - -const S = sprintf; - -Deno.test("noVerb", function (): void { - assertEquals(sprintf("bla"), "bla"); -}); - -Deno.test("percent", function (): void { - assertEquals(sprintf("%%"), "%"); - assertEquals(sprintf("!%%!"), "!%!"); - assertEquals(sprintf("!%%"), "!%"); - assertEquals(sprintf("%%!"), "%!"); -}); -Deno.test("testBoolean", function (): void { - assertEquals(sprintf("%t", true), "true"); - assertEquals(sprintf("%10t", true), " true"); - assertEquals(sprintf("%-10t", false), "false "); - assertEquals(sprintf("%t", false), "false"); - assertEquals(sprintf("bla%t", true), "blatrue"); - assertEquals(sprintf("%tbla", false), "falsebla"); -}); - -Deno.test("testIntegerB", function (): void { - assertEquals(S("%b", 4), "100"); - assertEquals(S("%b", -4), "-100"); - assertEquals( - S("%b", 4.1), - "100.0001100110011001100110011001100110011001100110011", - ); - assertEquals( - S("%b", -4.1), - "-100.0001100110011001100110011001100110011001100110011", - ); - assertEquals( - S("%b", Number.MAX_SAFE_INTEGER), - "11111111111111111111111111111111111111111111111111111", - ); - assertEquals( - S("%b", Number.MIN_SAFE_INTEGER), - "-11111111111111111111111111111111111111111111111111111", - ); - // width - - assertEquals(S("%4b", 4), " 100"); -}); - -Deno.test("testIntegerC", function (): void { - assertEquals(S("%c", 0x31), "1"); - assertEquals(S("%c%b", 0x31, 1), "11"); - assertEquals(S("%c", 0x1f4a9), "💩"); - //width - assertEquals(S("%4c", 0x31), " 1"); -}); - -Deno.test("testIntegerD", function (): void { - assertEquals(S("%d", 4), "4"); - assertEquals(S("%d", -4), "-4"); - assertEquals(S("%d", Number.MAX_SAFE_INTEGER), "9007199254740991"); - assertEquals(S("%d", Number.MIN_SAFE_INTEGER), "-9007199254740991"); -}); - -Deno.test("testIntegerO", function (): void { - assertEquals(S("%o", 4), "4"); - assertEquals(S("%o", -4), "-4"); - assertEquals(S("%o", 9), "11"); - assertEquals(S("%o", -9), "-11"); - assertEquals(S("%o", Number.MAX_SAFE_INTEGER), "377777777777777777"); - assertEquals(S("%o", Number.MIN_SAFE_INTEGER), "-377777777777777777"); - // width - assertEquals(S("%4o", 4), " 4"); -}); -Deno.test("testIntegerx", function (): void { - assertEquals(S("%x", 4), "4"); - assertEquals(S("%x", -4), "-4"); - assertEquals(S("%x", 9), "9"); - assertEquals(S("%x", -9), "-9"); - assertEquals(S("%x", Number.MAX_SAFE_INTEGER), "1fffffffffffff"); - assertEquals(S("%x", Number.MIN_SAFE_INTEGER), "-1fffffffffffff"); - // width - assertEquals(S("%4x", -4), " -4"); - assertEquals(S("%-4x", -4), "-4 "); - // plus - assertEquals(S("%+4x", 4), " +4"); - assertEquals(S("%-+4x", 4), "+4 "); -}); -Deno.test("testIntegerX", function (): void { - assertEquals(S("%X", 4), "4"); - assertEquals(S("%X", -4), "-4"); - assertEquals(S("%X", 9), "9"); - assertEquals(S("%X", -9), "-9"); - assertEquals(S("%X", Number.MAX_SAFE_INTEGER), "1FFFFFFFFFFFFF"); - assertEquals(S("%X", Number.MIN_SAFE_INTEGER), "-1FFFFFFFFFFFFF"); -}); - -Deno.test("testFloate", function (): void { - assertEquals(S("%e", 4), "4.000000e+00"); - assertEquals(S("%e", -4), "-4.000000e+00"); - assertEquals(S("%e", 4.1), "4.100000e+00"); - assertEquals(S("%e", -4.1), "-4.100000e+00"); - assertEquals(S("%e", Number.MAX_SAFE_INTEGER), "9.007199e+15"); - assertEquals(S("%e", Number.MIN_SAFE_INTEGER), "-9.007199e+15"); -}); -Deno.test("testFloatE", function (): void { - assertEquals(S("%E", 4), "4.000000E+00"); - assertEquals(S("%E", -4), "-4.000000E+00"); - assertEquals(S("%E", 4.1), "4.100000E+00"); - assertEquals(S("%E", -4.1), "-4.100000E+00"); - assertEquals(S("%E", Number.MAX_SAFE_INTEGER), "9.007199E+15"); - assertEquals(S("%E", Number.MIN_SAFE_INTEGER), "-9.007199E+15"); - assertEquals(S("%E", Number.MIN_VALUE), "5.000000E-324"); - assertEquals(S("%E", Number.MAX_VALUE), "1.797693E+308"); -}); -Deno.test("testFloatfF", function (): void { - assertEquals(S("%f", 4), "4.000000"); - assertEquals(S("%F", 4), "4.000000"); - assertEquals(S("%f", -4), "-4.000000"); - assertEquals(S("%F", -4), "-4.000000"); - assertEquals(S("%f", 4.1), "4.100000"); - assertEquals(S("%F", 4.1), "4.100000"); - assertEquals(S("%f", -4.1), "-4.100000"); - assertEquals(S("%F", -4.1), "-4.100000"); - assertEquals(S("%f", Number.MAX_SAFE_INTEGER), "9007199254740991.000000"); - assertEquals(S("%F", Number.MAX_SAFE_INTEGER), "9007199254740991.000000"); - assertEquals(S("%f", Number.MIN_SAFE_INTEGER), "-9007199254740991.000000"); - assertEquals(S("%F", Number.MIN_SAFE_INTEGER), "-9007199254740991.000000"); - assertEquals(S("%f", Number.MIN_VALUE), "0.000000"); - assertEquals( - S("%.324f", Number.MIN_VALUE), - // eslint-disable-next-line max-len - "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005", - ); - assertEquals(S("%F", Number.MIN_VALUE), "0.000000"); - assertEquals( - S("%f", Number.MAX_VALUE), - // eslint-disable-next-line max-len - "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000", - ); - assertEquals( - S("%F", Number.MAX_VALUE), - // eslint-disable-next-line max-len - "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000", - ); -}); - -Deno.test("testString", function (): void { - assertEquals(S("%s World%s", "Hello", "!"), "Hello World!"); -}); - -Deno.test("testHex", function (): void { - assertEquals(S("%x", "123"), "313233"); - assertEquals(S("%x", "n"), "6e"); -}); -Deno.test("testHeX", function (): void { - assertEquals(S("%X", "123"), "313233"); - assertEquals(S("%X", "n"), "6E"); -}); - -Deno.test("testType", function (): void { - assertEquals(S("%T", new Date()), "object"); - assertEquals(S("%T", 123), "number"); - assertEquals(S("%T", "123"), "string"); - assertEquals(S("%.3T", "123"), "str"); -}); - -Deno.test("testPositional", function (): void { - assertEquals(S("%[1]d%[2]d", 1, 2), "12"); - assertEquals(S("%[2]d%[1]d", 1, 2), "21"); -}); - -Deno.test("testSharp", function (): void { - assertEquals(S("%#x", "123"), "0x313233"); - assertEquals(S("%#X", "123"), "0X313233"); - assertEquals(S("%#x", 123), "0x7b"); - assertEquals(S("%#X", 123), "0X7B"); - assertEquals(S("%#o", 123), "0173"); - assertEquals(S("%#b", 4), "0b100"); -}); - -Deno.test("testWidthAndPrecision", function (): void { - assertEquals( - S("%9.99d", 9), - // eslint-disable-next-line max-len - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009", - ); - assertEquals(S("%1.12d", 9), "000000000009"); - assertEquals(S("%2s", "a"), " a"); - assertEquals(S("%2d", 1), " 1"); - assertEquals(S("%#4x", 1), " 0x1"); - - assertEquals( - S("%*.99d", 9, 9), - // eslint-disable-next-line max-len - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009", - ); - assertEquals( - S("%9.*d", 99, 9), - // eslint-disable-next-line max-len - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009", - ); - assertEquals(S("%*s", 2, "a"), " a"); - assertEquals(S("%*d", 2, 1), " 1"); - assertEquals(S("%#*x", 4, 1), " 0x1"); -}); - -Deno.test("testDash", function (): void { - assertEquals(S("%-2s", "a"), "a "); - assertEquals(S("%-2d", 1), "1 "); -}); -Deno.test("testPlus", function (): void { - assertEquals(S("%-+3d", 1), "+1 "); - assertEquals(S("%+3d", 1), " +1"); - assertEquals(S("%+3d", -1), " -1"); -}); - -Deno.test("testSpace", function (): void { - assertEquals(S("% -3d", 3), " 3 "); -}); - -Deno.test("testZero", function (): void { - assertEquals(S("%04s", "a"), "000a"); -}); - -// relevant test cases from fmt_test.go -// deno-lint-ignore no-explicit-any -const tests: Array<[string, any, string]> = [ - ["%d", 12345, "12345"], - ["%v", 12345, "12345"], - ["%t", true, "true"], - // basic string - ["%s", "abc", "abc"], - // ["%q", "abc", `"abc"`], // TODO: need %q? - ["%x", "abc", "616263"], - ["%x", "\xff\xf0\x0f\xff", "fff00fff"], - ["%X", "\xff\xf0\x0f\xff", "FFF00FFF"], - ["%x", "", ""], - ["% x", "", ""], - ["%#x", "", ""], - ["%# x", "", ""], - ["%x", "xyz", "78797a"], - ["%X", "xyz", "78797A"], - ["% x", "xyz", "78 79 7a"], - ["% X", "xyz", "78 79 7A"], - ["%#x", "xyz", "0x78797a"], - ["%#X", "xyz", "0X78797A"], - ["%# x", "xyz", "0x78 0x79 0x7a"], - ["%# X", "xyz", "0X78 0X79 0X7A"], - // basic bytes : TODO special handling for Buffer? other std types? - // escaped strings : TODO decide whether to have %q - - // characters - ["%c", "x".charCodeAt(0), "x"], - ["%c", 0xe4, "ä"], - ["%c", 0x672c, "本"], - ["%c", "日".charCodeAt(0), "日"], - // Specifying precision should have no effect. - ["%.0c", "⌘".charCodeAt(0), "⌘"], - ["%3c", "⌘".charCodeAt(0), " ⌘"], - ["%-3c", "⌘".charCodeAt(0), "⌘ "], - // Runes that are not printable. - // {"%c", '\U00000e00', "\u0e00"}, - // TODO(bartlomieju) check if \U escape exists in js - //["%c", '\U0010ffff'.codePointAt(0), "\U0010ffff"], - - // Runes that are not valid. - ["%c", -1, "�"], - // TODO(bartomieju): surrogate half, doesn't make sense in itself, how - // to determine in JS? - // ["%c", 0xDC80, "�"], - ["%c", 0x110000, "�"], - ["%c", 0xfffffffff, "�"], - // TODO(bartlomieju): - // escaped characters - // Runes that are not printable. - // Runes that are not valid. - - // width - ["%5s", "abc", " abc"], - ["%2s", "\u263a", " ☺"], - ["%-5s", "abc", "abc "], - ["%05s", "abc", "00abc"], - ["%5s", "abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"], - ["%.5s", "abcdefghijklmnopqrstuvwxyz", "abcde"], - ["%.0s", "日本語日本語", ""], - ["%.5s", "日本語日本語", "日本語日本"], - ["%.10s", "日本語日本語", "日本語日本語"], - // ["%08q", "abc", `000"abc"`], - // TODO(bartlomieju): verb q - // ["%-8q", "abc", `"abc" `], - //["%.5q", "abcdefghijklmnopqrstuvwxyz", `"abcde"`], - ["%.5x", "abcdefghijklmnopqrstuvwxyz", "6162636465"], - //["%.3q", "日本語日本語", `"日本語"`], - //["%.1q", "日本語", `"日"`] - // change of go testcase utf-8([日]) = 0xe697a5, utf-16= 65e5 and - // our %x takes lower byte of string "%.1x", "日本語", "e6"],, - ["%.1x", "日本語", "e5"], - //["%10.1q", "日本語日本語", ` "日"`], - // ["%10v", null, " "], - // TODO(bartlomieju): null, undefined ... - // ["%-10v", null, " "], - - // integers - ["%d", 12345, "12345"], - ["%d", -12345, "-12345"], - // ["%d", ^uint8(0), "255"], - //["%d", ^uint16(0), "65535"], - //["%d", ^uint32(0), "4294967295"], - //["%d", ^uint64(0), "18446744073709551615"], - ["%d", -1 << 7, "-128"], - ["%d", -1 << 15, "-32768"], - ["%d", -1 << 31, "-2147483648"], - //["%d", (-1 << 63), "-9223372036854775808"], - ["%.d", 0, ""], - ["%.0d", 0, ""], - ["%6.0d", 0, " "], - ["%06.0d", 0, " "], // 0 flag should be ignored - ["% d", 12345, " 12345"], - ["%+d", 12345, "+12345"], - ["%+d", -12345, "-12345"], - ["%b", 7, "111"], - ["%b", -6, "-110"], - // ["%b", ^uint32(0), "11111111111111111111111111111111"], - // ["%b", ^uint64(0), - // "1111111111111111111111111111111111111111111111111111111111111111"], - // ["%b", int64(-1 << 63), zeroFill("-1", 63, "")], - // 0 octal notation not allowed in struct node... - ["%o", parseInt("01234", 8), "1234"], - ["%#o", parseInt("01234", 8), "01234"], - // ["%o", ^uint32(0), "37777777777"], - // ["%o", ^uint64(0), "1777777777777777777777"], - ["%#X", 0, "0X0"], - ["%x", 0x12abcdef, "12abcdef"], - ["%X", 0x12abcdef, "12ABCDEF"], - // ["%x", ^uint32(0), "ffffffff"], - // ["%X", ^uint64(0), "FFFFFFFFFFFFFFFF"], - ["%.20b", 7, "00000000000000000111"], - ["%10d", 12345, " 12345"], - ["%10d", -12345, " -12345"], - ["%+10d", 12345, " +12345"], - ["%010d", 12345, "0000012345"], - ["%010d", -12345, "-000012345"], - ["%20.8d", 1234, " 00001234"], - ["%20.8d", -1234, " -00001234"], - ["%020.8d", 1234, " 00001234"], - ["%020.8d", -1234, " -00001234"], - ["%-20.8d", 1234, "00001234 "], - ["%-20.8d", -1234, "-00001234 "], - ["%-#20.8x", 0x1234abc, "0x01234abc "], - ["%-#20.8X", 0x1234abc, "0X01234ABC "], - ["%-#20.8o", parseInt("01234", 8), "00001234 "], - // Test correct f.intbuf overflow checks. - // TODO(bartlomieju): lazy - // unicode format - // TODO(bartlomieju): decide whether unicode verb makes sense %U - - // floats - ["%+.3e", 0.0, "+0.000e+00"], - ["%+.3e", 1.0, "+1.000e+00"], - ["%+.3f", -1.0, "-1.000"], - ["%+.3F", -1.0, "-1.000"], - //["%+.3F", float32(-1.0), "-1.000"], - ["%+07.2f", 1.0, "+001.00"], - ["%+07.2f", -1.0, "-001.00"], - ["%-07.2f", 1.0, "1.00 "], - ["%-07.2f", -1.0, "-1.00 "], - ["%+-07.2f", 1.0, "+1.00 "], - ["%+-07.2f", -1.0, "-1.00 "], - ["%-+07.2f", 1.0, "+1.00 "], - ["%-+07.2f", -1.0, "-1.00 "], - ["%+10.2f", +1.0, " +1.00"], - ["%+10.2f", -1.0, " -1.00"], - ["% .3E", -1.0, "-1.000E+00"], - ["% .3e", 1.0, " 1.000e+00"], - ["%+.3g", 0.0, "+0"], - ["%+.3g", 1.0, "+1"], - ["%+.3g", -1.0, "-1"], - ["% .3g", -1.0, "-1"], - ["% .3g", 1.0, " 1"], - // //["%b", float32(1.0), "8388608p-23"], - // ["%b", 1.0, "4503599627370496p-52"], - // // Test sharp flag used with floats. - ["%#g", 1e-323, "1.00000e-323"], - ["%#g", -1.0, "-1.00000"], - ["%#g", 1.1, "1.10000"], - ["%#g", 123456.0, "123456."], - //["%#g", 1234567.0, "1.234567e+06"], - // the line above is incorrect in go (according to - // my posix reading) %f-> prec = prec-1 - ["%#g", 1234567.0, "1.23457e+06"], - ["%#g", 1230000.0, "1.23000e+06"], - ["%#g", 1000000.0, "1.00000e+06"], - ["%#.0f", 1.0, "1."], - ["%#.0e", 1.0, "1.e+00"], - ["%#.0g", 1.0, "1."], - ["%#.0g", 1100000.0, "1.e+06"], - ["%#.4f", 1.0, "1.0000"], - ["%#.4e", 1.0, "1.0000e+00"], - ["%#.4g", 1.0, "1.000"], - ["%#.4g", 100000.0, "1.000e+05"], - ["%#.0f", 123.0, "123."], - ["%#.0e", 123.0, "1.e+02"], - ["%#.0g", 123.0, "1.e+02"], - ["%#.4f", 123.0, "123.0000"], - ["%#.4e", 123.0, "1.2300e+02"], - ["%#.4g", 123.0, "123.0"], - ["%#.4g", 123000.0, "1.230e+05"], - ["%#9.4g", 1.0, " 1.000"], - // The sharp flag has no effect for binary float format. - // ["%#b", 1.0, "4503599627370496p-52"], // TODO binary for floats - // Precision has no effect for binary float format. - //["%.4b", float32(1.0), "8388608p-23"], // TODO s.above - // ["%.4b", -1.0, "-4503599627370496p-52"], - // Test correct f.intbuf boundary checks. - //["%.68f", 1.0, zeroFill("1.", 68, "")], // TODO zerofill - //["%.68f", -1.0, zeroFill("-1.", 68, "")], //TODO s.a. - // float infinites and NaNs - ["%f", Number.POSITIVE_INFINITY, "+Inf"], - ["%.1f", Number.NEGATIVE_INFINITY, "-Inf"], - ["% f", NaN, " NaN"], - ["%20f", Number.POSITIVE_INFINITY, " +Inf"], - // ["% 20F", Number.POSITIVE_INFINITY, " Inf"], // TODO : wut? - ["% 20e", Number.NEGATIVE_INFINITY, " -Inf"], - ["%+20E", Number.NEGATIVE_INFINITY, " -Inf"], - ["% +20g", Number.NEGATIVE_INFINITY, " -Inf"], - ["%+-20G", Number.POSITIVE_INFINITY, "+Inf "], - ["%20e", NaN, " NaN"], - ["% +20E", NaN, " +NaN"], - ["% -20g", NaN, " NaN "], - ["%+-20G", NaN, "+NaN "], - // Zero padding does not apply to infinities and NaN. - ["%+020e", Number.POSITIVE_INFINITY, " +Inf"], - ["%-020f", Number.NEGATIVE_INFINITY, "-Inf "], - ["%-020E", NaN, "NaN "], - // complex values // go specific - // old test/fmt_test.go - ["%e", 1.0, "1.000000e+00"], - ["%e", 1234.5678e3, "1.234568e+06"], - ["%e", 1234.5678e-8, "1.234568e-05"], - ["%e", -7.0, "-7.000000e+00"], - ["%e", -1e-9, "-1.000000e-09"], - ["%f", 1234.5678e3, "1234567.800000"], - ["%f", 1234.5678e-8, "0.000012"], - ["%f", -7.0, "-7.000000"], - ["%f", -1e-9, "-0.000000"], - // ["%g", 1234.5678e3, "1.2345678e+06"], - // I believe the above test from go is incorrect according to posix, s. above. - ["%g", 1234.5678e3, "1.23457e+06"], - //["%g", float32(1234.5678e3), "1.2345678e+06"], - //["%g", 1234.5678e-8, "1.2345678e-05"], // posix, see above - ["%g", 1234.5678e-8, "1.23457e-05"], - ["%g", -7.0, "-7"], - ["%g", -1e-9, "-1e-09"], - //["%g", float32(-1e-9), "-1e-09"], - ["%E", 1.0, "1.000000E+00"], - ["%E", 1234.5678e3, "1.234568E+06"], - ["%E", 1234.5678e-8, "1.234568E-05"], - ["%E", -7.0, "-7.000000E+00"], - ["%E", -1e-9, "-1.000000E-09"], - //["%G", 1234.5678e3, "1.2345678E+06"], // posix, see above - ["%G", 1234.5678e3, "1.23457E+06"], - //["%G", float32(1234.5678e3), "1.2345678E+06"], - //["%G", 1234.5678e-8, "1.2345678E-05"], // posic, see above - ["%G", 1234.5678e-8, "1.23457E-05"], - ["%G", -7.0, "-7"], - ["%G", -1e-9, "-1E-09"], - //["%G", float32(-1e-9), "-1E-09"], - ["%20.5s", "qwertyuiop", " qwert"], - ["%.5s", "qwertyuiop", "qwert"], - ["%-20.5s", "qwertyuiop", "qwert "], - ["%20c", "x".charCodeAt(0), " x"], - ["%-20c", "x".charCodeAt(0), "x "], - ["%20.6e", 1.2345e3, " 1.234500e+03"], - ["%20.6e", 1.2345e-3, " 1.234500e-03"], - ["%20e", 1.2345e3, " 1.234500e+03"], - ["%20e", 1.2345e-3, " 1.234500e-03"], - ["%20.8e", 1.2345e3, " 1.23450000e+03"], - ["%20f", 1.23456789e3, " 1234.567890"], - ["%20f", 1.23456789e-3, " 0.001235"], - ["%20f", 12345678901.23456789, " 12345678901.234568"], - ["%-20f", 1.23456789e3, "1234.567890 "], - ["%20.8f", 1.23456789e3, " 1234.56789000"], - ["%20.8f", 1.23456789e-3, " 0.00123457"], - // ["%g", 1.23456789e3, "1234.56789"], - // posix ... precision(2) = precision(def=6) - (exp(3)+1) - ["%g", 1.23456789e3, "1234.57"], - // ["%g", 1.23456789e-3, "0.00123456789"], posix... - ["%g", 1.23456789e-3, "0.00123457"], // see above prec6 = precdef6 - (-3+1) - //["%g", 1.23456789e20, "1.23456789e+20"], - ["%g", 1.23456789e20, "1.23457e+20"], - // arrays - // TODO(bartlomieju): - // slice : go specific - - // TODO(bartlomieju): decide how to handle deeper types, arrays, objects - // byte arrays and slices with %b,%c,%d,%o,%U and %v - // f.space should and f.plus should not have an effect with %v. - // f.space and f.plus should have an effect with %d. - - // Padding with byte slices. - // Same for strings - ["%2x", "", " "], // 103 - ["%#2x", "", " "], - ["% 02x", "", "00"], - ["%# 02x", "", "00"], - ["%-2x", "", " "], - ["%-02x", "", " "], - ["%8x", "\xab", " ab"], - ["% 8x", "\xab", " ab"], - ["%#8x", "\xab", " 0xab"], - ["%# 8x", "\xab", " 0xab"], - ["%08x", "\xab", "000000ab"], - ["% 08x", "\xab", "000000ab"], - ["%#08x", "\xab", "00000xab"], - ["%# 08x", "\xab", "00000xab"], - ["%10x", "\xab\xcd", " abcd"], - ["% 10x", "\xab\xcd", " ab cd"], - ["%#10x", "\xab\xcd", " 0xabcd"], - ["%# 10x", "\xab\xcd", " 0xab 0xcd"], - ["%010x", "\xab\xcd", "000000abcd"], - ["% 010x", "\xab\xcd", "00000ab cd"], - ["%#010x", "\xab\xcd", "00000xabcd"], - ["%# 010x", "\xab\xcd", "00xab 0xcd"], - ["%-10X", "\xab", "AB "], - ["% -010X", "\xab", "AB "], - ["%#-10X", "\xab\xcd", "0XABCD "], - ["%# -010X", "\xab\xcd", "0XAB 0XCD "], - // renamings - // Formatter - // GoStringer - - // %T TODO possibly %#T object(constructor) - ["%T", {}, "object"], - ["%T", 1, "number"], - ["%T", "", "string"], - ["%T", undefined, "undefined"], - ["%T", null, "object"], - ["%T", S, "function"], - ["%T", true, "boolean"], - ["%T", Symbol(), "symbol"], - // %p with pointers - - // erroneous things - // {"", nil, "%!(EXTRA )"}, - // {"", 2, "%!(EXTRA int=2)"}, - // {"no args", "hello", "no args%!(EXTRA string=hello)"}, - // {"%s %", "hello", "hello %!(NOVERB)"}, - // {"%s %.2", "hello", "hello %!(NOVERB)"}, - // {"%017091901790959340919092959340919017929593813360", 0, - // "%!(NOVERB)%!(EXTRA int=0)"}, - // {"%184467440737095516170v", 0, "%!(NOVERB)%!(EXTRA int=0)"}, - // // Extra argument errors should format without flags set. - // {"%010.2", "12345", "%!(NOVERB)%!(EXTRA string=12345)"}, - // - // // Test that maps with non-reflexive keys print all keys and values. - // {"%v", map[float64]int{NaN: 1, NaN: 1}, "map[NaN:1 NaN:1]"}, - - // more floats - - ["%.2f", 1.0, "1.00"], - ["%.2f", -1.0, "-1.00"], - ["% .2f", 1.0, " 1.00"], - ["% .2f", -1.0, "-1.00"], - ["%+.2f", 1.0, "+1.00"], - ["%+.2f", -1.0, "-1.00"], - ["%7.2f", 1.0, " 1.00"], - ["%7.2f", -1.0, " -1.00"], - ["% 7.2f", 1.0, " 1.00"], - ["% 7.2f", -1.0, " -1.00"], - ["%+7.2f", 1.0, " +1.00"], - ["%+7.2f", -1.0, " -1.00"], - ["% +7.2f", 1.0, " +1.00"], - ["% +7.2f", -1.0, " -1.00"], - ["%07.2f", 1.0, "0001.00"], - ["%07.2f", -1.0, "-001.00"], - ["% 07.2f", 1.0, " 001.00"], //153 here - ["% 07.2f", -1.0, "-001.00"], - ["%+07.2f", 1.0, "+001.00"], - ["%+07.2f", -1.0, "-001.00"], - ["% +07.2f", 1.0, "+001.00"], - ["% +07.2f", -1.0, "-001.00"], -]; - -Deno.test("testThorough", function (): void { - tests.forEach((t, i): void => { - // p(t) - const is = S(t[0], t[1]); - const should = t[2]; - assertEquals( - is, - should, - `failed case[${i}] : is >${is}< should >${should}<`, - ); - }); -}); - -Deno.test("testWeirdos", function (): void { - assertEquals(S("%.d", 9), "9"); - assertEquals( - S("dec[%d]=%d hex[%[1]d]=%#x oct[%[1]d]=%#o %s", 1, 255, "Third"), - "dec[1]=255 hex[1]=0xff oct[1]=0377 Third", - ); -}); - -Deno.test("formatV", function (): void { - const a = { a: { a: { a: { a: { a: { a: { a: {} } } } } } } }; - assertEquals(S("%v", a), "[object Object]"); - assertEquals(S("%#v", a), `{ a: { a: { a: { a: [Object] } } } }`); - assertEquals( - S("%#.8v", a), - "{ a: { a: { a: { a: { a: { a: { a: {} } } } } } } }", - ); - assertEquals(S("%#.1v", a), `{ a: [Object] }`); -}); - -Deno.test("formatJ", function (): void { - const a = { a: { a: { a: { a: { a: { a: { a: {} } } } } } } }; - assertEquals(S("%j", a), `{"a":{"a":{"a":{"a":{"a":{"a":{"a":{}}}}}}}}`); -}); - -Deno.test("flagLessThan", function (): void { - const a = { a: { a: { a: { a: { a: { a: { a: {} } } } } } } }; - const aArray = [a, a, a]; - assertEquals( - S("%<#.1v", aArray), - `[ { a: [Object] }, { a: [Object] }, { a: [Object] } ]`, - ); - const fArray = [1.2345, 0.98765, 123456789.5678]; - assertEquals(S("%<.2f", fArray), "[ 1.23, 0.99, 123456789.57 ]"); -}); - -Deno.test("testErrors", function (): void { - // wrong type : TODO strict mode ... - //assertEquals(S("%f", "not a number"), "%!(BADTYPE flag=f type=string)") - assertEquals(S("A %h", ""), "A %!(BAD VERB 'h')"); - assertEquals(S("%J", ""), "%!(BAD VERB 'J')"); - assertEquals(S("bla%J", ""), "bla%!(BAD VERB 'J')"); - assertEquals(S("%Jbla", ""), "%!(BAD VERB 'J')bla"); - - assertEquals(S("%d"), "%!(MISSING 'd')"); - assertEquals(S("%d %d", 1), "1 %!(MISSING 'd')"); - assertEquals(S("%d %f A", 1), "1 %!(MISSING 'f') A"); - - assertEquals(S("%*.2f", "a", 1.1), "%!(BAD WIDTH 'a')"); - assertEquals(S("%.*f", "a", 1.1), "%!(BAD PREC 'a')"); - assertEquals( - S("%.[2]*f", 1.23, "p"), - `%!(BAD PREC 'p')%!(EXTRA '1.23')`, - ); - assertEquals(S("%.[2]*[1]f Yippie!", 1.23, "p"), "%!(BAD PREC 'p') Yippie!"); - - assertEquals(S("%[1]*.2f", "a", "p"), "%!(BAD WIDTH 'a')"); - - assertEquals(S("A", "a", "p"), `A%!(EXTRA '"a"' '"p"')`); - assertEquals(S("%[2]s %[2]s", "a", "p"), `p p%!(EXTRA '"a"')`); - - // remains to be determined how to handle bad indices ... - // (realistically) the entire error handling is still up for grabs. - assertEquals(S("%[hallo]s %d %d %d", 1, 2, 3, 4), "%!(BAD INDEX) 2 3 4"); - assertEquals( - S("%[5]s", 1, 2, 3, 4), - `%!(BAD INDEX)%!(EXTRA '2' '3' '4')`, - ); - assertEquals(S("%[5]f"), "%!(BAD INDEX)"); - assertEquals(S("%.[5]f"), "%!(BAD INDEX)"); - assertEquals(S("%.[5]*f"), "%!(BAD INDEX)"); -}); diff --git a/std/fs/README.md b/std/fs/README.md deleted file mode 100644 index c82fb1219f..0000000000 --- a/std/fs/README.md +++ /dev/null @@ -1,177 +0,0 @@ -# fs - -fs module is made to provide helpers to manipulate the filesystem. - -## Usage - -Most of the following modules are exposed in `mod.ts`. This feature is currently -unstable. To enable it use `deno run --unstable`. - -### emptyDir - -Ensures that a directory is empty. Deletes directory contents if the directory -is not empty. If the directory does not exist, it is created. The directory -itself is not deleted. - -```ts -import { - emptyDir, - emptyDirSync, -} from "https://deno.land/std@$STD_VERSION/fs/mod.ts"; - -emptyDir("./foo"); // returns a promise -emptyDirSync("./foo"); // void -``` - -### ensureDir - -Ensures that the directory exists. If the directory structure does not exist, it -is created. Like `mkdir -p`. - -```ts -import { - ensureDir, - ensureDirSync, -} from "https://deno.land/std@$STD_VERSION/fs/mod.ts"; - -ensureDir("./bar"); // returns a promise -ensureDirSync("./ensureDirSync"); // void -``` - -### ensureFile - -Ensures that the file exists. If the file that is requested to be created is in -directories that do not exist, these directories are created. If the file -already exists, it is **NOT MODIFIED**. - -```ts -import { - ensureFile, - ensureFileSync, -} from "https://deno.land/std@$STD_VERSION/fs/mod.ts"; - -ensureFile("./folder/targetFile.dat"); // returns promise -ensureFileSync("./folder/targetFile.dat"); // void -``` - -### ensureSymlink - -Ensures that the link exists. If the directory structure does not exist, it is -created. - -```ts -import { - ensureSymlink, - ensureSymlinkSync, -} from "https://deno.land/std@$STD_VERSION/fs/mod.ts"; - -ensureSymlink("./folder/targetFile.dat", "./folder/targetFile.link.dat"); // returns promise -ensureSymlinkSync("./folder/targetFile.dat", "./folder/targetFile.link.dat"); // void -``` - -### EOL - -Detects and format the passed string for the targeted End Of Line character. - -```ts -import { format, detect, EOL } from "https://deno.land/std@$STD_VERSION/fs/mod.ts"; - -const CRLFinput = "deno\r\nis not\r\nnode"; -const Mixedinput = "deno\nis not\r\nnode"; -const LFinput = "deno\nis not\nnode"; -const NoNLinput = "deno is not node"; - -detect(LFinput); // output EOL.LF -detect(CRLFinput); // output EOL.CRLF -detect(Mixedinput); // output EOL.CRLF -detect(NoNLinput); // output null - -format(CRLFinput, EOL.LF); // output "deno\nis not\nnode" -... -``` - -### exists - -Test whether or not the given path exists by checking with the file system. - -```ts -import { - exists, - existsSync, -} from "https://deno.land/std@$STD_VERSION/fs/mod.ts"; - -exists("./foo"); // returns a Promise -existsSync("./foo"); // returns boolean -``` - -### move - -Moves a file or directory. Overwrites it if option provided. - -```ts -import { move, moveSync } from "https://deno.land/std@$STD_VERSION/fs/mod.ts"; - -move("./foo", "./bar"); // returns a promise -moveSync("./foo", "./bar"); // void -moveSync("./foo", "./existingFolder", { overwrite: true }); -// Will overwrite existingFolder -``` - -### copy - -copy a file or directory. Overwrites it if option provided. - -```ts -import { copy, copySync } from "https://deno.land/std@$STD_VERSION/fs/mod.ts"; - -copy("./foo", "./bar"); // returns a promise -copySync("./foo", "./bar"); // void -copySync("./foo", "./existingFolder", { overwrite: true }); -// Will overwrite existingFolder -``` - -### walk - -Iterate all files in a directory recursively. - -```ts -import { walk, walkSync } from "https://deno.land/std@$STD_VERSION/fs/mod.ts"; - -for (const entry of walkSync(".")) { - console.log(entry.path); -} - -// Async -async function printFilesNames() { - for await (const entry of walk(".")) { - console.log(entry.path); - } -} - -printFilesNames().then(() => console.log("Done!")); -``` - -### expandGlob - -Expand the glob string from the specified `root` directory and yield each result -as a `WalkEntry` object. - -```ts -import { expandGlob } from "https://deno.land/std@$STD_VERSION/fs/mod.ts"; - -for await (const file of expandGlob("**/*.ts")) { - console.log(file); -} -``` - -### expandGlobSync - -Synchronous version of `expandGlob()`. - -```ts -import { expandGlobSync } from "https://deno.land/std@$STD_VERSION/fs/mod.ts"; - -for (const file of expandGlobSync("**/*.ts")) { - console.log(file); -} -``` diff --git a/std/fs/_util.ts b/std/fs/_util.ts deleted file mode 100644 index a9445a3947..0000000000 --- a/std/fs/_util.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import * as path from "../path/mod.ts"; - -/** - * Test whether or not `dest` is a sub-directory of `src` - * @param src src file path - * @param dest dest file path - * @param sep path separator - */ -export function isSubdir( - src: string, - dest: string, - sep: string = path.sep, -): boolean { - if (src === dest) { - return false; - } - const srcArray = src.split(sep); - const destArray = dest.split(sep); - return srcArray.every((current, i) => destArray[i] === current); -} - -export type PathType = "file" | "dir" | "symlink"; - -/** - * Get a human readable file type string. - * - * @param fileInfo A FileInfo describes a file and is returned by `stat`, - * `lstat` - */ -export function getFileInfoType(fileInfo: Deno.FileInfo): PathType | undefined { - return fileInfo.isFile - ? "file" - : fileInfo.isDirectory - ? "dir" - : fileInfo.isSymlink - ? "symlink" - : undefined; -} diff --git a/std/fs/_util_test.ts b/std/fs/_util_test.ts deleted file mode 100644 index 9a24caaf0d..0000000000 --- a/std/fs/_util_test.ts +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright the Browserify authors. MIT License. - -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "../path/mod.ts"; -import { getFileInfoType, isSubdir, PathType } from "./_util.ts"; -import { ensureFileSync } from "./ensure_file.ts"; -import { ensureDirSync } from "./ensure_dir.ts"; - -const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); -const testdataDir = path.resolve(moduleDir, "testdata"); - -Deno.test("_isSubdir", function (): void { - const pairs = [ - ["", "", false, path.posix.sep], - ["/first/second", "/first", false, path.posix.sep], - ["/first", "/first", false, path.posix.sep], - ["/first", "/first/second", true, path.posix.sep], - ["first", "first/second", true, path.posix.sep], - ["../first", "../first/second", true, path.posix.sep], - ["c:\\first", "c:\\first", false, path.win32.sep], - ["c:\\first", "c:\\first\\second", true, path.win32.sep], - ]; - - pairs.forEach(function (p): void { - const src = p[0] as string; - const dest = p[1] as string; - const expected = p[2] as boolean; - const sep = p[3] as string; - assertEquals( - isSubdir(src, dest, sep), - expected, - `'${src}' should ${expected ? "" : "not"} be parent dir of '${dest}'`, - ); - }); -}); - -Deno.test("_getFileInfoType", function (): void { - const pairs = [ - [path.join(testdataDir, "file_type_1"), "file"], - [path.join(testdataDir, "file_type_dir_1"), "dir"], - ]; - - pairs.forEach(function (p): void { - const filePath = p[0] as string; - const type = p[1] as PathType; - switch (type) { - case "file": - ensureFileSync(filePath); - break; - case "dir": - ensureDirSync(filePath); - break; - case "symlink": - // TODO(axetroy): test symlink - break; - } - - const stat = Deno.statSync(filePath); - - Deno.removeSync(filePath, { recursive: true }); - - assertEquals(getFileInfoType(stat), type); - }); -}); diff --git a/std/fs/copy.ts b/std/fs/copy.ts deleted file mode 100644 index 05ea6056be..0000000000 --- a/std/fs/copy.ts +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import * as path from "../path/mod.ts"; -import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; -import { getFileInfoType, isSubdir } from "./_util.ts"; -import { assert } from "../_util/assert.ts"; -import { isWindows } from "../_util/os.ts"; - -export interface CopyOptions { - /** - * overwrite existing file or directory. Default is `false` - */ - overwrite?: boolean; - /** - * When `true`, will set last modification and access times to the ones of the - * original source files. - * When `false`, timestamp behavior is OS-dependent. - * Default is `false`. - */ - preserveTimestamps?: boolean; -} - -interface InternalCopyOptions extends CopyOptions { - /** - * default is `false` - */ - isFolder?: boolean; -} - -async function ensureValidCopy( - src: string, - dest: string, - options: InternalCopyOptions, -): Promise { - let destStat: Deno.FileInfo; - - try { - destStat = await Deno.lstat(dest); - } catch (err) { - if (err instanceof Deno.errors.NotFound) { - return; - } - throw err; - } - - if (options.isFolder && !destStat.isDirectory) { - throw new Error( - `Cannot overwrite non-directory '${dest}' with directory '${src}'.`, - ); - } - if (!options.overwrite) { - throw new Error(`'${dest}' already exists.`); - } - - return destStat; -} - -function ensureValidCopySync( - src: string, - dest: string, - options: InternalCopyOptions, -): Deno.FileInfo | undefined { - let destStat: Deno.FileInfo; - try { - destStat = Deno.lstatSync(dest); - } catch (err) { - if (err instanceof Deno.errors.NotFound) { - return; - } - throw err; - } - - if (options.isFolder && !destStat.isDirectory) { - throw new Error( - `Cannot overwrite non-directory '${dest}' with directory '${src}'.`, - ); - } - if (!options.overwrite) { - throw new Error(`'${dest}' already exists.`); - } - - return destStat; -} - -/* copy file to dest */ -async function copyFile( - src: string, - dest: string, - options: InternalCopyOptions, -): Promise { - await ensureValidCopy(src, dest, options); - await Deno.copyFile(src, dest); - if (options.preserveTimestamps) { - const statInfo = await Deno.stat(src); - assert(statInfo.atime instanceof Date, `statInfo.atime is unavailable`); - assert(statInfo.mtime instanceof Date, `statInfo.mtime is unavailable`); - await Deno.utime(dest, statInfo.atime, statInfo.mtime); - } -} -/* copy file to dest synchronously */ -function copyFileSync( - src: string, - dest: string, - options: InternalCopyOptions, -): void { - ensureValidCopySync(src, dest, options); - Deno.copyFileSync(src, dest); - if (options.preserveTimestamps) { - const statInfo = Deno.statSync(src); - assert(statInfo.atime instanceof Date, `statInfo.atime is unavailable`); - assert(statInfo.mtime instanceof Date, `statInfo.mtime is unavailable`); - Deno.utimeSync(dest, statInfo.atime, statInfo.mtime); - } -} - -/* copy symlink to dest */ -async function copySymLink( - src: string, - dest: string, - options: InternalCopyOptions, -): Promise { - await ensureValidCopy(src, dest, options); - const originSrcFilePath = await Deno.readLink(src); - const type = getFileInfoType(await Deno.lstat(src)); - if (isWindows) { - await Deno.symlink(originSrcFilePath, dest, { - type: type === "dir" ? "dir" : "file", - }); - } else { - await Deno.symlink(originSrcFilePath, dest); - } - if (options.preserveTimestamps) { - const statInfo = await Deno.lstat(src); - assert(statInfo.atime instanceof Date, `statInfo.atime is unavailable`); - assert(statInfo.mtime instanceof Date, `statInfo.mtime is unavailable`); - await Deno.utime(dest, statInfo.atime, statInfo.mtime); - } -} - -/* copy symlink to dest synchronously */ -function copySymlinkSync( - src: string, - dest: string, - options: InternalCopyOptions, -): void { - ensureValidCopySync(src, dest, options); - const originSrcFilePath = Deno.readLinkSync(src); - const type = getFileInfoType(Deno.lstatSync(src)); - if (isWindows) { - Deno.symlinkSync(originSrcFilePath, dest, { - type: type === "dir" ? "dir" : "file", - }); - } else { - Deno.symlinkSync(originSrcFilePath, dest); - } - - if (options.preserveTimestamps) { - const statInfo = Deno.lstatSync(src); - assert(statInfo.atime instanceof Date, `statInfo.atime is unavailable`); - assert(statInfo.mtime instanceof Date, `statInfo.mtime is unavailable`); - Deno.utimeSync(dest, statInfo.atime, statInfo.mtime); - } -} - -/* copy folder from src to dest. */ -async function copyDir( - src: string, - dest: string, - options: CopyOptions, -): Promise { - const destStat = await ensureValidCopy(src, dest, { - ...options, - isFolder: true, - }); - - if (!destStat) { - await ensureDir(dest); - } - - if (options.preserveTimestamps) { - const srcStatInfo = await Deno.stat(src); - assert(srcStatInfo.atime instanceof Date, `statInfo.atime is unavailable`); - assert(srcStatInfo.mtime instanceof Date, `statInfo.mtime is unavailable`); - await Deno.utime(dest, srcStatInfo.atime, srcStatInfo.mtime); - } - - for await (const entry of Deno.readDir(src)) { - const srcPath = path.join(src, entry.name); - const destPath = path.join(dest, path.basename(srcPath as string)); - if (entry.isSymlink) { - await copySymLink(srcPath, destPath, options); - } else if (entry.isDirectory) { - await copyDir(srcPath, destPath, options); - } else if (entry.isFile) { - await copyFile(srcPath, destPath, options); - } - } -} - -/* copy folder from src to dest synchronously */ -function copyDirSync(src: string, dest: string, options: CopyOptions): void { - const destStat = ensureValidCopySync(src, dest, { - ...options, - isFolder: true, - }); - - if (!destStat) { - ensureDirSync(dest); - } - - if (options.preserveTimestamps) { - const srcStatInfo = Deno.statSync(src); - assert(srcStatInfo.atime instanceof Date, `statInfo.atime is unavailable`); - assert(srcStatInfo.mtime instanceof Date, `statInfo.mtime is unavailable`); - Deno.utimeSync(dest, srcStatInfo.atime, srcStatInfo.mtime); - } - - for (const entry of Deno.readDirSync(src)) { - assert(entry.name != null, "file.name must be set"); - const srcPath = path.join(src, entry.name); - const destPath = path.join(dest, path.basename(srcPath as string)); - if (entry.isSymlink) { - copySymlinkSync(srcPath, destPath, options); - } else if (entry.isDirectory) { - copyDirSync(srcPath, destPath, options); - } else if (entry.isFile) { - copyFileSync(srcPath, destPath, options); - } - } -} - -/** - * Copy a file or directory. The directory can have contents. Like `cp -r`. - * Requires the `--allow-read` and `--allow-write` flag. - * @param src the file/directory path. - * Note that if `src` is a directory it will copy everything inside - * of this directory, not the entire directory itself - * @param dest the destination path. Note that if `src` is a file, `dest` cannot - * be a directory - * @param options - */ -export async function copy( - src: string, - dest: string, - options: CopyOptions = {}, -): Promise { - src = path.resolve(src); - dest = path.resolve(dest); - - if (src === dest) { - throw new Error("Source and destination cannot be the same."); - } - - const srcStat = await Deno.lstat(src); - - if (srcStat.isDirectory && isSubdir(src, dest)) { - throw new Error( - `Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`, - ); - } - - if (srcStat.isSymlink) { - await copySymLink(src, dest, options); - } else if (srcStat.isDirectory) { - await copyDir(src, dest, options); - } else if (srcStat.isFile) { - await copyFile(src, dest, options); - } -} - -/** - * Copy a file or directory. The directory can have contents. Like `cp -r`. - * Requires the `--allow-read` and `--allow-write` flag. - * @param src the file/directory path. - * Note that if `src` is a directory it will copy everything inside - * of this directory, not the entire directory itself - * @param dest the destination path. Note that if `src` is a file, `dest` cannot - * be a directory - * @param options - */ -export function copySync( - src: string, - dest: string, - options: CopyOptions = {}, -): void { - src = path.resolve(src); - dest = path.resolve(dest); - - if (src === dest) { - throw new Error("Source and destination cannot be the same."); - } - - const srcStat = Deno.lstatSync(src); - - if (srcStat.isDirectory && isSubdir(src, dest)) { - throw new Error( - `Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`, - ); - } - - if (srcStat.isSymlink) { - copySymlinkSync(src, dest, options); - } else if (srcStat.isDirectory) { - copyDirSync(src, dest, options); - } else if (srcStat.isFile) { - copyFileSync(src, dest, options); - } -} diff --git a/std/fs/copy_test.ts b/std/fs/copy_test.ts deleted file mode 100644 index 40d7cb19a7..0000000000 --- a/std/fs/copy_test.ts +++ /dev/null @@ -1,520 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertThrows, - assertThrowsAsync, -} from "../testing/asserts.ts"; -import * as path from "../path/mod.ts"; -import { copy, copySync } from "./copy.ts"; -import { exists, existsSync } from "./exists.ts"; -import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; -import { ensureFile, ensureFileSync } from "./ensure_file.ts"; -import { ensureSymlink, ensureSymlinkSync } from "./ensure_symlink.ts"; - -const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); -const testdataDir = path.resolve(moduleDir, "testdata"); - -function testCopy( - name: string, - cb: (tempDir: string) => Promise, - ignore = false, -): void { - Deno.test({ - name, - async fn(): Promise { - const tempDir = await Deno.makeTempDir({ - prefix: "deno_std_copy_async_test_", - }); - await cb(tempDir); - await Deno.remove(tempDir, { recursive: true }); - }, - ignore, - }); -} - -function testCopySync(name: string, cb: (tempDir: string) => void): void { - Deno.test({ - name, - fn: (): void => { - const tempDir = Deno.makeTempDirSync({ - prefix: "deno_std_copy_sync_test_", - }); - cb(tempDir); - Deno.removeSync(tempDir, { recursive: true }); - }, - }); -} - -testCopy( - "[fs] copy file if it does no exist", - async (tempDir: string): Promise => { - const srcFile = path.join(testdataDir, "copy_file_not_exists.txt"); - const destFile = path.join(tempDir, "copy_file_not_exists_1.txt"); - await assertThrowsAsync( - async (): Promise => { - await copy(srcFile, destFile); - }, - ); - }, -); - -testCopy( - "[fs] copy if src and dest are the same paths", - async (tempDir: string): Promise => { - const srcFile = path.join(tempDir, "copy_file_same.txt"); - const destFile = path.join(tempDir, "copy_file_same.txt"); - await assertThrowsAsync( - async (): Promise => { - await copy(srcFile, destFile); - }, - Error, - "Source and destination cannot be the same.", - ); - }, -); - -testCopy( - "[fs] copy file", - async (tempDir: string): Promise => { - const srcFile = path.join(testdataDir, "copy_file.txt"); - const destFile = path.join(tempDir, "copy_file_copy.txt"); - - const srcContent = new TextDecoder().decode(await Deno.readFile(srcFile)); - - assertEquals( - await exists(srcFile), - true, - `source should exist before copy`, - ); - assertEquals( - await exists(destFile), - false, - "destination should not exist before copy", - ); - - await copy(srcFile, destFile); - - assertEquals(await exists(srcFile), true, "source should exist after copy"); - assertEquals( - await exists(destFile), - true, - "destination should exist before copy", - ); - - const destContent = new TextDecoder().decode(await Deno.readFile(destFile)); - - assertEquals( - srcContent, - destContent, - "source and destination should have the same content", - ); - - // Copy again and it should throw an error. - await assertThrowsAsync( - async (): Promise => { - await copy(srcFile, destFile); - }, - Error, - `'${destFile}' already exists.`, - ); - - // Modify destination file. - await Deno.writeFile(destFile, new TextEncoder().encode("txt copy")); - - assertEquals( - new TextDecoder().decode(await Deno.readFile(destFile)), - "txt copy", - ); - - // Copy again with overwrite option. - await copy(srcFile, destFile, { overwrite: true }); - - // Make sure the file has been overwritten. - assertEquals( - new TextDecoder().decode(await Deno.readFile(destFile)), - "txt", - ); - }, -); - -testCopy( - "[fs] copy with preserve timestamps", - async (tempDir: string): Promise => { - const srcFile = path.join(testdataDir, "copy_file.txt"); - const destFile = path.join(tempDir, "copy_file_copy.txt"); - - const srcStatInfo = await Deno.stat(srcFile); - - assert(srcStatInfo.atime instanceof Date); - assert(srcStatInfo.mtime instanceof Date); - - // Copy with overwrite and preserve timestamps options. - await copy(srcFile, destFile, { - overwrite: true, - preserveTimestamps: true, - }); - - const destStatInfo = await Deno.stat(destFile); - - assert(destStatInfo.atime instanceof Date); - assert(destStatInfo.mtime instanceof Date); - assertEquals(destStatInfo.atime, srcStatInfo.atime); - assertEquals(destStatInfo.mtime, srcStatInfo.mtime); - }, -); - -testCopy( - "[fs] copy directory to its subdirectory", - async (tempDir: string): Promise => { - const srcDir = path.join(tempDir, "parent"); - const destDir = path.join(srcDir, "child"); - - await ensureDir(srcDir); - - await assertThrowsAsync( - async (): Promise => { - await copy(srcDir, destDir); - }, - Error, - `Cannot copy '${srcDir}' to a subdirectory of itself, '${destDir}'.`, - ); - }, -); - -testCopy( - "[fs] copy directory and destination exist and not a directory", - async (tempDir: string): Promise => { - const srcDir = path.join(tempDir, "parent"); - const destDir = path.join(tempDir, "child.txt"); - - await ensureDir(srcDir); - await ensureFile(destDir); - - await assertThrowsAsync( - async (): Promise => { - await copy(srcDir, destDir); - }, - Error, - `Cannot overwrite non-directory '${destDir}' with directory '${srcDir}'.`, - ); - }, -); - -testCopy( - "[fs] copy directory", - async (tempDir: string): Promise => { - const srcDir = path.join(testdataDir, "copy_dir"); - const destDir = path.join(tempDir, "copy_dir"); - const srcFile = path.join(srcDir, "0.txt"); - const destFile = path.join(destDir, "0.txt"); - const srcNestFile = path.join(srcDir, "nest", "0.txt"); - const destNestFile = path.join(destDir, "nest", "0.txt"); - - await copy(srcDir, destDir); - - assertEquals(await exists(destFile), true); - assertEquals(await exists(destNestFile), true); - - // After copy. The source and destination should have the same content. - assertEquals( - new TextDecoder().decode(await Deno.readFile(srcFile)), - new TextDecoder().decode(await Deno.readFile(destFile)), - ); - assertEquals( - new TextDecoder().decode(await Deno.readFile(srcNestFile)), - new TextDecoder().decode(await Deno.readFile(destNestFile)), - ); - - // Copy again without overwrite option and it should throw an error. - await assertThrowsAsync( - async (): Promise => { - await copy(srcDir, destDir); - }, - Error, - `'${destDir}' already exists.`, - ); - - // Modify the file in the destination directory. - await Deno.writeFile(destNestFile, new TextEncoder().encode("nest copy")); - assertEquals( - new TextDecoder().decode(await Deno.readFile(destNestFile)), - "nest copy", - ); - - // Copy again with overwrite option. - await copy(srcDir, destDir, { overwrite: true }); - - // Make sure the file has been overwritten. - assertEquals( - new TextDecoder().decode(await Deno.readFile(destNestFile)), - "nest", - ); - }, -); - -testCopy( - "[fs] copy symlink file", - async (tempDir: string): Promise => { - const dir = path.join(testdataDir, "copy_dir_link_file"); - const srcLink = path.join(dir, "0.txt"); - const destLink = path.join(tempDir, "0_copy.txt"); - - assert( - (await Deno.lstat(srcLink)).isSymlink, - `'${srcLink}' should be symlink type`, - ); - - await copy(srcLink, destLink); - - const statInfo = await Deno.lstat(destLink); - - assert(statInfo.isSymlink, `'${destLink}' should be symlink type`); - }, -); - -testCopy( - "[fs] copy symlink directory", - async (tempDir: string): Promise => { - const srcDir = path.join(testdataDir, "copy_dir"); - const srcLink = path.join(tempDir, "copy_dir_link"); - const destLink = path.join(tempDir, "copy_dir_link_copy"); - - await ensureSymlink(srcDir, srcLink); - - assert( - (await Deno.lstat(srcLink)).isSymlink, - `'${srcLink}' should be symlink type`, - ); - - await copy(srcLink, destLink); - - const statInfo = await Deno.lstat(destLink); - - assert(statInfo.isSymlink); - }, -); - -testCopySync( - "[fs] copy file synchronously if it does not exist", - (tempDir: string): void => { - const srcFile = path.join(testdataDir, "copy_file_not_exists_sync.txt"); - const destFile = path.join(tempDir, "copy_file_not_exists_1_sync.txt"); - assertThrows((): void => { - copySync(srcFile, destFile); - }); - }, -); - -testCopySync( - "[fs] copy synchronously with preserve timestamps", - (tempDir: string): void => { - const srcFile = path.join(testdataDir, "copy_file.txt"); - const destFile = path.join(tempDir, "copy_file_copy.txt"); - - const srcStatInfo = Deno.statSync(srcFile); - - assert(srcStatInfo.atime instanceof Date); - assert(srcStatInfo.mtime instanceof Date); - - // Copy with overwrite and preserve timestamps options. - copySync(srcFile, destFile, { - overwrite: true, - preserveTimestamps: true, - }); - - const destStatInfo = Deno.statSync(destFile); - - assert(destStatInfo.atime instanceof Date); - assert(destStatInfo.mtime instanceof Date); - // TODO(bartlomieju): Activate test when https://github.com/denoland/deno/issues/2411 - // is fixed - // assertEquals(destStatInfo.atime, srcStatInfo.atime); - // assertEquals(destStatInfo.mtime, srcStatInfo.mtime); - }, -); - -testCopySync( - "[fs] copy synchronously if src and dest are the same paths", - (): void => { - const srcFile = path.join(testdataDir, "copy_file_same_sync.txt"); - assertThrows( - (): void => { - copySync(srcFile, srcFile); - }, - Error, - "Source and destination cannot be the same.", - ); - }, -); - -testCopySync("[fs] copy file synchronously", (tempDir: string): void => { - const srcFile = path.join(testdataDir, "copy_file.txt"); - const destFile = path.join(tempDir, "copy_file_copy_sync.txt"); - - const srcContent = new TextDecoder().decode(Deno.readFileSync(srcFile)); - - assertEquals(existsSync(srcFile), true); - assertEquals(existsSync(destFile), false); - - copySync(srcFile, destFile); - - assertEquals(existsSync(srcFile), true); - assertEquals(existsSync(destFile), true); - - const destContent = new TextDecoder().decode(Deno.readFileSync(destFile)); - - assertEquals(srcContent, destContent); - - // Copy again without overwrite option and it should throw an error. - assertThrows( - (): void => { - copySync(srcFile, destFile); - }, - Error, - `'${destFile}' already exists.`, - ); - - // Modify destination file. - Deno.writeFileSync(destFile, new TextEncoder().encode("txt copy")); - - assertEquals( - new TextDecoder().decode(Deno.readFileSync(destFile)), - "txt copy", - ); - - // Copy again with overwrite option. - copySync(srcFile, destFile, { overwrite: true }); - - // Make sure the file has been overwritten. - assertEquals(new TextDecoder().decode(Deno.readFileSync(destFile)), "txt"); -}); - -testCopySync( - "[fs] copy directory synchronously to its subdirectory", - (tempDir: string): void => { - const srcDir = path.join(tempDir, "parent"); - const destDir = path.join(srcDir, "child"); - - ensureDirSync(srcDir); - - assertThrows( - (): void => { - copySync(srcDir, destDir); - }, - Error, - `Cannot copy '${srcDir}' to a subdirectory of itself, '${destDir}'.`, - ); - }, -); - -testCopySync( - "[fs] copy directory synchronously, and destination exist and not a " + - "directory", - (tempDir: string): void => { - const srcDir = path.join(tempDir, "parent_sync"); - const destDir = path.join(tempDir, "child.txt"); - - ensureDirSync(srcDir); - ensureFileSync(destDir); - - assertThrows( - (): void => { - copySync(srcDir, destDir); - }, - Error, - `Cannot overwrite non-directory '${destDir}' with directory '${srcDir}'.`, - ); - }, -); - -testCopySync("[fs] copy directory synchronously", (tempDir: string): void => { - const srcDir = path.join(testdataDir, "copy_dir"); - const destDir = path.join(tempDir, "copy_dir_copy_sync"); - const srcFile = path.join(srcDir, "0.txt"); - const destFile = path.join(destDir, "0.txt"); - const srcNestFile = path.join(srcDir, "nest", "0.txt"); - const destNestFile = path.join(destDir, "nest", "0.txt"); - - copySync(srcDir, destDir); - - assertEquals(existsSync(destFile), true); - assertEquals(existsSync(destNestFile), true); - - // After copy. The source and destination should have the same content. - assertEquals( - new TextDecoder().decode(Deno.readFileSync(srcFile)), - new TextDecoder().decode(Deno.readFileSync(destFile)), - ); - assertEquals( - new TextDecoder().decode(Deno.readFileSync(srcNestFile)), - new TextDecoder().decode(Deno.readFileSync(destNestFile)), - ); - - // Copy again without overwrite option and it should throw an error. - assertThrows( - (): void => { - copySync(srcDir, destDir); - }, - Error, - `'${destDir}' already exists.`, - ); - - // Modify the file in the destination directory. - Deno.writeFileSync(destNestFile, new TextEncoder().encode("nest copy")); - assertEquals( - new TextDecoder().decode(Deno.readFileSync(destNestFile)), - "nest copy", - ); - - // Copy again with overwrite option. - copySync(srcDir, destDir, { overwrite: true }); - - // Make sure the file has been overwritten. - assertEquals( - new TextDecoder().decode(Deno.readFileSync(destNestFile)), - "nest", - ); -}); - -testCopySync( - "[fs] copy symlink file synchronously", - (tempDir: string): void => { - const dir = path.join(testdataDir, "copy_dir_link_file"); - const srcLink = path.join(dir, "0.txt"); - const destLink = path.join(tempDir, "0_copy.txt"); - - assert( - Deno.lstatSync(srcLink).isSymlink, - `'${srcLink}' should be symlink type`, - ); - - copySync(srcLink, destLink); - - const statInfo = Deno.lstatSync(destLink); - - assert(statInfo.isSymlink, `'${destLink}' should be symlink type`); - }, -); - -testCopySync( - "[fs] copy symlink directory synchronously", - (tempDir: string): void => { - const originDir = path.join(testdataDir, "copy_dir"); - const srcLink = path.join(tempDir, "copy_dir_link"); - const destLink = path.join(tempDir, "copy_dir_link_copy"); - - ensureSymlinkSync(originDir, srcLink); - - assert( - Deno.lstatSync(srcLink).isSymlink, - `'${srcLink}' should be symlink type`, - ); - - copySync(srcLink, destLink); - - const statInfo = Deno.lstatSync(destLink); - - assert(statInfo.isSymlink); - }, -); diff --git a/std/fs/empty_dir.ts b/std/fs/empty_dir.ts deleted file mode 100644 index df73ad6b78..0000000000 --- a/std/fs/empty_dir.ts +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { join } from "../path/mod.ts"; - -/** - * Ensures that a directory is empty. - * Deletes directory contents if the directory is not empty. - * If the directory does not exist, it is created. - * The directory itself is not deleted. - * Requires the `--allow-read` and `--allow-write` flag. - */ -export async function emptyDir(dir: string): Promise { - try { - const items = []; - for await (const dirEntry of Deno.readDir(dir)) { - items.push(dirEntry); - } - - while (items.length) { - const item = items.shift(); - if (item && item.name) { - const filepath = join(dir, item.name); - await Deno.remove(filepath, { recursive: true }); - } - } - } catch (err) { - if (!(err instanceof Deno.errors.NotFound)) { - throw err; - } - - // if not exist. then create it - await Deno.mkdir(dir, { recursive: true }); - } -} - -/** - * Ensures that a directory is empty. - * Deletes directory contents if the directory is not empty. - * If the directory does not exist, it is created. - * The directory itself is not deleted. - * Requires the `--allow-read` and `--allow-write` flag. - */ -export function emptyDirSync(dir: string): void { - try { - const items = [...Deno.readDirSync(dir)]; - - // If the directory exists, remove all entries inside it. - while (items.length) { - const item = items.shift(); - if (item && item.name) { - const filepath = join(dir, item.name); - Deno.removeSync(filepath, { recursive: true }); - } - } - } catch (err) { - if (!(err instanceof Deno.errors.NotFound)) { - throw err; - } - // if not exist. then create it - Deno.mkdirSync(dir, { recursive: true }); - return; - } -} diff --git a/std/fs/empty_dir_test.ts b/std/fs/empty_dir_test.ts deleted file mode 100644 index a78f221eb3..0000000000 --- a/std/fs/empty_dir_test.ts +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertStringIncludes, - assertThrows, - assertThrowsAsync, -} from "../testing/asserts.ts"; -import * as path from "../path/mod.ts"; -import { emptyDir, emptyDirSync } from "./empty_dir.ts"; - -const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); -const testdataDir = path.resolve(moduleDir, "testdata"); - -Deno.test("emptyDirIfItNotExist", async function (): Promise { - const testDir = path.join(testdataDir, "empty_dir_test_1"); - const testNestDir = path.join(testDir, "nest"); - // empty a dir which not exist. then it will create new one - await emptyDir(testNestDir); - - try { - // check the dir - const stat = await Deno.stat(testNestDir); - assertEquals(stat.isDirectory, true); - } finally { - // remove the test dir - await Deno.remove(testDir, { recursive: true }); - } -}); - -Deno.test("emptyDirSyncIfItNotExist", function (): void { - const testDir = path.join(testdataDir, "empty_dir_test_2"); - const testNestDir = path.join(testDir, "nest"); - // empty a dir which does not exist, then it will a create new one. - emptyDirSync(testNestDir); - - try { - // check the dir - const stat = Deno.statSync(testNestDir); - assertEquals(stat.isDirectory, true); - } finally { - // remove the test dir - Deno.removeSync(testDir, { recursive: true }); - } -}); - -Deno.test("emptyDirIfItExist", async function (): Promise { - const testDir = path.join(testdataDir, "empty_dir_test_3"); - const testNestDir = path.join(testDir, "nest"); - // create test dir - await emptyDir(testNestDir); - const testDirFile = path.join(testNestDir, "test.ts"); - // create test file in test dir - await Deno.writeFile(testDirFile, new Uint8Array()); - - // before empty: make sure file/directory exist - const beforeFileStat = await Deno.stat(testDirFile); - assertEquals(beforeFileStat.isFile, true); - - const beforeDirStat = await Deno.stat(testNestDir); - assertEquals(beforeDirStat.isDirectory, true); - - await emptyDir(testDir); - - // after empty: file/directory have already been removed - try { - // test dir still there - const stat = await Deno.stat(testDir); - assertEquals(stat.isDirectory, true); - - // nest directory have been removed - await assertThrowsAsync( - async (): Promise => { - await Deno.stat(testNestDir); - }, - ); - - // test file have been removed - await assertThrowsAsync( - async (): Promise => { - await Deno.stat(testDirFile); - }, - ); - } finally { - // remote test dir - await Deno.remove(testDir, { recursive: true }); - } -}); - -Deno.test("emptyDirSyncIfItExist", function (): void { - const testDir = path.join(testdataDir, "empty_dir_test_4"); - const testNestDir = path.join(testDir, "nest"); - // create test dir - emptyDirSync(testNestDir); - const testDirFile = path.join(testNestDir, "test.ts"); - // create test file in test dir - Deno.writeFileSync(testDirFile, new Uint8Array()); - - // before empty: make sure file/directory exist - const beforeFileStat = Deno.statSync(testDirFile); - assertEquals(beforeFileStat.isFile, true); - - const beforeDirStat = Deno.statSync(testNestDir); - assertEquals(beforeDirStat.isDirectory, true); - - emptyDirSync(testDir); - - // after empty: file/directory have already remove - try { - // test dir still present - const stat = Deno.statSync(testDir); - assertEquals(stat.isDirectory, true); - - // nest directory have been removed - assertThrows((): void => { - Deno.statSync(testNestDir); - }); - - // test file have been removed - assertThrows((): void => { - Deno.statSync(testDirFile); - }); - } finally { - // remote test dir - Deno.removeSync(testDir, { recursive: true }); - } -}); - -interface Scenes { - read: boolean; // --allow-read - write: boolean; // --allow-write - async: boolean; - output: string; -} -const scenes: Scenes[] = [ - // 1 - { - read: false, - write: false, - async: true, - output: "run again with the --allow-read flag", - }, - { - read: false, - write: false, - async: false, - output: "run again with the --allow-read flag", - }, - // 2 - { - read: true, - write: false, - async: true, - output: "run again with the --allow-write flag", - }, - { - read: true, - write: false, - async: false, - output: "run again with the --allow-write flag", - }, - // 3 - { - read: false, - write: true, - async: true, - output: "run again with the --allow-read flag", - }, - { - read: false, - write: true, - async: false, - output: "run again with the --allow-read flag", - }, - // 4 - { - read: true, - write: true, - async: true, - output: "success", - }, - { - read: true, - write: true, - async: false, - output: "success", - }, -]; -for (const s of scenes) { - let title = `test ${s.async ? "emptyDir" : "emptyDirSync"}`; - title += `("testdata/testfolder") ${s.read ? "with" : "without"}`; - title += ` --allow-read & ${s.write ? "with" : "without"} --allow-write`; - Deno.test(`[fs] emptyDirPermission ${title}`, async function (): Promise< - void - > { - const testfolder = path.join(testdataDir, "testfolder"); - - try { - await Deno.mkdir(testfolder); - - await Deno.writeFile( - path.join(testfolder, "child.txt"), - new TextEncoder().encode("hello world"), - ); - - try { - const args = [Deno.execPath(), "run", "--quiet"]; - - if (s.read) { - args.push("--allow-read"); - } - - if (s.write) { - args.push("--allow-write"); - } - - args.push( - path.join( - testdataDir, - s.async ? "empty_dir.ts" : "empty_dir_sync.ts", - ), - ); - args.push("testfolder"); - - const p = Deno.run({ - stdout: "piped", - cwd: testdataDir, - cmd: args, - }); - - assert(p.stdout); - const output = await p.output(); - p.close(); - assertStringIncludes(new TextDecoder().decode(output), s.output); - } catch (err) { - await Deno.remove(testfolder, { recursive: true }); - throw err; - } - } finally { - // Make the test rerunnable - // Otherwise it would throw an error due to mkdir fail. - await Deno.remove(testfolder, { recursive: true }); - // done - } - }); -} diff --git a/std/fs/ensure_dir.ts b/std/fs/ensure_dir.ts deleted file mode 100644 index 20259a6f72..0000000000 --- a/std/fs/ensure_dir.ts +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { getFileInfoType } from "./_util.ts"; - -/** - * Ensures that the directory exists. - * If the directory structure does not exist, it is created. Like mkdir -p. - * Requires the `--allow-read` and `--allow-write` flag. - */ -export async function ensureDir(dir: string): Promise { - try { - const fileInfo = await Deno.lstat(dir); - if (!fileInfo.isDirectory) { - throw new Error( - `Ensure path exists, expected 'dir', got '${ - getFileInfoType(fileInfo) - }'`, - ); - } - } catch (err) { - if (err instanceof Deno.errors.NotFound) { - // if dir not exists. then create it. - await Deno.mkdir(dir, { recursive: true }); - return; - } - throw err; - } -} - -/** - * Ensures that the directory exists. - * If the directory structure does not exist, it is created. Like mkdir -p. - * Requires the `--allow-read` and `--allow-write` flag. - */ -export function ensureDirSync(dir: string): void { - try { - const fileInfo = Deno.lstatSync(dir); - if (!fileInfo.isDirectory) { - throw new Error( - `Ensure path exists, expected 'dir', got '${ - getFileInfoType(fileInfo) - }'`, - ); - } - } catch (err) { - if (err instanceof Deno.errors.NotFound) { - // if dir not exists. then create it. - Deno.mkdirSync(dir, { recursive: true }); - return; - } - throw err; - } -} diff --git a/std/fs/ensure_dir_test.ts b/std/fs/ensure_dir_test.ts deleted file mode 100644 index ad8b75a9fa..0000000000 --- a/std/fs/ensure_dir_test.ts +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertThrows, assertThrowsAsync } from "../testing/asserts.ts"; -import * as path from "../path/mod.ts"; -import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; -import { ensureFile, ensureFileSync } from "./ensure_file.ts"; - -const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); -const testdataDir = path.resolve(moduleDir, "testdata"); - -Deno.test("ensureDirIfItNotExist", async function (): Promise { - const baseDir = path.join(testdataDir, "ensure_dir_not_exist"); - const testDir = path.join(baseDir, "test"); - - await ensureDir(testDir); - - await assertThrowsAsync( - async (): Promise => { - await Deno.stat(testDir).then((): void => { - throw new Error("test dir should exists."); - }); - }, - ); - - await Deno.remove(baseDir, { recursive: true }); -}); - -Deno.test("ensureDirSyncIfItNotExist", function (): void { - const baseDir = path.join(testdataDir, "ensure_dir_sync_not_exist"); - const testDir = path.join(baseDir, "test"); - - ensureDirSync(testDir); - - Deno.statSync(testDir); - - Deno.removeSync(baseDir, { recursive: true }); -}); - -Deno.test("ensureDirIfItExist", async function (): Promise { - const baseDir = path.join(testdataDir, "ensure_dir_exist"); - const testDir = path.join(baseDir, "test"); - - // create test directory - await Deno.mkdir(testDir, { recursive: true }); - - await ensureDir(testDir); - - await assertThrowsAsync( - async (): Promise => { - await Deno.stat(testDir).then((): void => { - throw new Error("test dir should still exists."); - }); - }, - ); - - await Deno.remove(baseDir, { recursive: true }); -}); - -Deno.test("ensureDirSyncIfItExist", function (): void { - const baseDir = path.join(testdataDir, "ensure_dir_sync_exist"); - const testDir = path.join(baseDir, "test"); - - // create test directory - Deno.mkdirSync(testDir, { recursive: true }); - - ensureDirSync(testDir); - - assertThrows((): void => { - Deno.statSync(testDir); - throw new Error("test dir should still exists."); - }); - - Deno.removeSync(baseDir, { recursive: true }); -}); - -Deno.test("ensureDirIfItAsFile", async function (): Promise { - const baseDir = path.join(testdataDir, "ensure_dir_exist_file"); - const testFile = path.join(baseDir, "test"); - - await ensureFile(testFile); - - await assertThrowsAsync( - async (): Promise => { - await ensureDir(testFile); - }, - Error, - `Ensure path exists, expected 'dir', got 'file'`, - ); - - await Deno.remove(baseDir, { recursive: true }); -}); - -Deno.test("ensureDirSyncIfItAsFile", function (): void { - const baseDir = path.join(testdataDir, "ensure_dir_exist_file_async"); - const testFile = path.join(baseDir, "test"); - - ensureFileSync(testFile); - - assertThrows( - (): void => { - ensureDirSync(testFile); - }, - Error, - `Ensure path exists, expected 'dir', got 'file'`, - ); - - Deno.removeSync(baseDir, { recursive: true }); -}); diff --git a/std/fs/ensure_file.ts b/std/fs/ensure_file.ts deleted file mode 100644 index f70732c370..0000000000 --- a/std/fs/ensure_file.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import * as path from "../path/mod.ts"; -import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; -import { getFileInfoType } from "./_util.ts"; - -/** - * Ensures that the file exists. - * If the file that is requested to be created is in directories that do not - * exist. - * these directories are created. If the file already exists, - * it is NOTMODIFIED. - * Requires the `--allow-read` and `--allow-write` flag. - */ -export async function ensureFile(filePath: string): Promise { - try { - // if file exists - const stat = await Deno.lstat(filePath); - if (!stat.isFile) { - throw new Error( - `Ensure path exists, expected 'file', got '${getFileInfoType(stat)}'`, - ); - } - } catch (err) { - // if file not exists - if (err instanceof Deno.errors.NotFound) { - // ensure dir exists - await ensureDir(path.dirname(filePath)); - // create file - await Deno.writeFile(filePath, new Uint8Array()); - return; - } - - throw err; - } -} - -/** - * Ensures that the file exists. - * If the file that is requested to be created is in directories that do not - * exist, - * these directories are created. If the file already exists, - * it is NOT MODIFIED. - * Requires the `--allow-read` and `--allow-write` flag. - */ -export function ensureFileSync(filePath: string): void { - try { - // if file exists - const stat = Deno.lstatSync(filePath); - if (!stat.isFile) { - throw new Error( - `Ensure path exists, expected 'file', got '${getFileInfoType(stat)}'`, - ); - } - } catch (err) { - // if file not exists - if (err instanceof Deno.errors.NotFound) { - // ensure dir exists - ensureDirSync(path.dirname(filePath)); - // create file - Deno.writeFileSync(filePath, new Uint8Array()); - return; - } - throw err; - } -} diff --git a/std/fs/ensure_file_test.ts b/std/fs/ensure_file_test.ts deleted file mode 100644 index e106034261..0000000000 --- a/std/fs/ensure_file_test.ts +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertThrows, assertThrowsAsync } from "../testing/asserts.ts"; -import * as path from "../path/mod.ts"; -import { ensureFile, ensureFileSync } from "./ensure_file.ts"; - -const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); -const testdataDir = path.resolve(moduleDir, "testdata"); - -Deno.test("ensureFileIfItNotExist", async function (): Promise { - const testDir = path.join(testdataDir, "ensure_file_1"); - const testFile = path.join(testDir, "test.txt"); - - await ensureFile(testFile); - - await assertThrowsAsync( - async (): Promise => { - await Deno.stat(testFile).then((): void => { - throw new Error("test file should exists."); - }); - }, - ); - - await Deno.remove(testDir, { recursive: true }); -}); - -Deno.test("ensureFileSyncIfItNotExist", function (): void { - const testDir = path.join(testdataDir, "ensure_file_2"); - const testFile = path.join(testDir, "test.txt"); - - ensureFileSync(testFile); - - assertThrows((): void => { - Deno.statSync(testFile); - throw new Error("test file should exists."); - }); - - Deno.removeSync(testDir, { recursive: true }); -}); - -Deno.test("ensureFileIfItExist", async function (): Promise { - const testDir = path.join(testdataDir, "ensure_file_3"); - const testFile = path.join(testDir, "test.txt"); - - await Deno.mkdir(testDir, { recursive: true }); - await Deno.writeFile(testFile, new Uint8Array()); - - await ensureFile(testFile); - - await assertThrowsAsync( - async (): Promise => { - await Deno.stat(testFile).then((): void => { - throw new Error("test file should exists."); - }); - }, - ); - - await Deno.remove(testDir, { recursive: true }); -}); - -Deno.test("ensureFileSyncIfItExist", function (): void { - const testDir = path.join(testdataDir, "ensure_file_4"); - const testFile = path.join(testDir, "test.txt"); - - Deno.mkdirSync(testDir, { recursive: true }); - Deno.writeFileSync(testFile, new Uint8Array()); - - ensureFileSync(testFile); - - assertThrows((): void => { - Deno.statSync(testFile); - throw new Error("test file should exists."); - }); - - Deno.removeSync(testDir, { recursive: true }); -}); - -Deno.test("ensureFileIfItExistAsDir", async function (): Promise { - const testDir = path.join(testdataDir, "ensure_file_5"); - - await Deno.mkdir(testDir, { recursive: true }); - - await assertThrowsAsync( - async (): Promise => { - await ensureFile(testDir); - }, - Error, - `Ensure path exists, expected 'file', got 'dir'`, - ); - - await Deno.remove(testDir, { recursive: true }); -}); - -Deno.test("ensureFileSyncIfItExistAsDir", function (): void { - const testDir = path.join(testdataDir, "ensure_file_6"); - - Deno.mkdirSync(testDir, { recursive: true }); - - assertThrows( - (): void => { - ensureFileSync(testDir); - }, - Error, - `Ensure path exists, expected 'file', got 'dir'`, - ); - - Deno.removeSync(testDir, { recursive: true }); -}); diff --git a/std/fs/ensure_link.ts b/std/fs/ensure_link.ts deleted file mode 100644 index 56d740ef0a..0000000000 --- a/std/fs/ensure_link.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import * as path from "../path/mod.ts"; -import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; -import { exists, existsSync } from "./exists.ts"; -import { getFileInfoType } from "./_util.ts"; - -/** - * Ensures that the hard link exists. - * If the directory structure does not exist, it is created. - * - * @param src the source file path. Directory hard links are not allowed. - * @param dest the destination link path - */ -export async function ensureLink(src: string, dest: string): Promise { - if (await exists(dest)) { - const destStatInfo = await Deno.lstat(dest); - const destFilePathType = getFileInfoType(destStatInfo); - if (destFilePathType !== "file") { - throw new Error( - `Ensure path exists, expected 'file', got '${destFilePathType}'`, - ); - } - return; - } - - await ensureDir(path.dirname(dest)); - - await Deno.link(src, dest); -} - -/** - * Ensures that the hard link exists. - * If the directory structure does not exist, it is created. - * - * @param src the source file path. Directory hard links are not allowed. - * @param dest the destination link path - */ -export function ensureLinkSync(src: string, dest: string): void { - if (existsSync(dest)) { - const destStatInfo = Deno.lstatSync(dest); - const destFilePathType = getFileInfoType(destStatInfo); - if (destFilePathType !== "file") { - throw new Error( - `Ensure path exists, expected 'file', got '${destFilePathType}'`, - ); - } - return; - } - - ensureDirSync(path.dirname(dest)); - - Deno.linkSync(src, dest); -} diff --git a/std/fs/ensure_link_test.ts b/std/fs/ensure_link_test.ts deleted file mode 100644 index d45b058cab..0000000000 --- a/std/fs/ensure_link_test.ts +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// TODO(axetroy): Add test for Windows once symlink is implemented for Windows. -import { - assertEquals, - assertThrows, - assertThrowsAsync, -} from "../testing/asserts.ts"; -import * as path from "../path/mod.ts"; -import { ensureLink, ensureLinkSync } from "./ensure_link.ts"; - -const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); -const testdataDir = path.resolve(moduleDir, "testdata"); - -Deno.test("ensureLinkIfItNotExist", async function (): Promise { - const srcDir = path.join(testdataDir, "ensure_link_1"); - const destDir = path.join(testdataDir, "ensure_link_1_2"); - const testFile = path.join(srcDir, "test.txt"); - const linkFile = path.join(destDir, "link.txt"); - - await assertThrowsAsync( - async (): Promise => { - await ensureLink(testFile, linkFile); - }, - ); - - await Deno.remove(destDir, { recursive: true }); -}); - -Deno.test("ensureLinkSyncIfItNotExist", function (): void { - const testDir = path.join(testdataDir, "ensure_link_2"); - const testFile = path.join(testDir, "test.txt"); - const linkFile = path.join(testDir, "link.txt"); - - assertThrows((): void => { - ensureLinkSync(testFile, linkFile); - }); - - Deno.removeSync(testDir, { recursive: true }); -}); - -Deno.test("ensureLinkIfItExist", async function (): Promise { - const testDir = path.join(testdataDir, "ensure_link_3"); - const testFile = path.join(testDir, "test.txt"); - const linkFile = path.join(testDir, "link.txt"); - - await Deno.mkdir(testDir, { recursive: true }); - await Deno.writeFile(testFile, new Uint8Array()); - - await ensureLink(testFile, linkFile); - - const srcStat = await Deno.lstat(testFile); - const linkStat = await Deno.lstat(linkFile); - - assertEquals(srcStat.isFile, true); - assertEquals(linkStat.isFile, true); - - // har link success. try to change one of them. they should be change both. - - // let's change origin file. - await Deno.writeFile(testFile, new TextEncoder().encode("123")); - - const testFileContent1 = new TextDecoder().decode( - await Deno.readFile(testFile), - ); - const linkFileContent1 = new TextDecoder().decode( - await Deno.readFile(testFile), - ); - - assertEquals(testFileContent1, "123"); - assertEquals(testFileContent1, linkFileContent1); - - // let's change link file. - await Deno.writeFile(testFile, new TextEncoder().encode("abc")); - - const testFileContent2 = new TextDecoder().decode( - await Deno.readFile(testFile), - ); - const linkFileContent2 = new TextDecoder().decode( - await Deno.readFile(testFile), - ); - - assertEquals(testFileContent2, "abc"); - assertEquals(testFileContent2, linkFileContent2); - - await Deno.remove(testDir, { recursive: true }); -}); - -Deno.test("ensureLinkSyncIfItExist", function (): void { - const testDir = path.join(testdataDir, "ensure_link_4"); - const testFile = path.join(testDir, "test.txt"); - const linkFile = path.join(testDir, "link.txt"); - - Deno.mkdirSync(testDir, { recursive: true }); - Deno.writeFileSync(testFile, new Uint8Array()); - - ensureLinkSync(testFile, linkFile); - - const srcStat = Deno.lstatSync(testFile); - - const linkStat = Deno.lstatSync(linkFile); - - assertEquals(srcStat.isFile, true); - assertEquals(linkStat.isFile, true); - - // har link success. try to change one of them. they should be change both. - - // let's change origin file. - Deno.writeFileSync(testFile, new TextEncoder().encode("123")); - - const testFileContent1 = new TextDecoder().decode( - Deno.readFileSync(testFile), - ); - const linkFileContent1 = new TextDecoder().decode( - Deno.readFileSync(testFile), - ); - - assertEquals(testFileContent1, "123"); - assertEquals(testFileContent1, linkFileContent1); - - // let's change link file. - Deno.writeFileSync(testFile, new TextEncoder().encode("abc")); - - const testFileContent2 = new TextDecoder().decode( - Deno.readFileSync(testFile), - ); - const linkFileContent2 = new TextDecoder().decode( - Deno.readFileSync(testFile), - ); - - assertEquals(testFileContent2, "abc"); - assertEquals(testFileContent2, linkFileContent2); - - Deno.removeSync(testDir, { recursive: true }); -}); - -Deno.test("ensureLinkDirectoryIfItExist", async function (): Promise { - const testDir = path.join(testdataDir, "ensure_link_origin_3"); - const linkDir = path.join(testdataDir, "ensure_link_link_3"); - const testFile = path.join(testDir, "test.txt"); - - await Deno.mkdir(testDir, { recursive: true }); - await Deno.writeFile(testFile, new Uint8Array()); - - await assertThrowsAsync( - async (): Promise => { - await ensureLink(testDir, linkDir); - }, - // "Operation not permitted (os error 1)" // throw an local matching test - // "Access is denied. (os error 5)" // throw in CI - ); - - Deno.removeSync(testDir, { recursive: true }); -}); - -Deno.test("ensureLinkSyncDirectoryIfItExist", function (): void { - const testDir = path.join(testdataDir, "ensure_link_origin_3"); - const linkDir = path.join(testdataDir, "ensure_link_link_3"); - const testFile = path.join(testDir, "test.txt"); - - Deno.mkdirSync(testDir, { recursive: true }); - Deno.writeFileSync(testFile, new Uint8Array()); - - assertThrows( - (): void => { - ensureLinkSync(testDir, linkDir); - }, - // "Operation not permitted (os error 1)" // throw an local matching test - // "Access is denied. (os error 5)" // throw in CI - ); - - Deno.removeSync(testDir, { recursive: true }); -}); diff --git a/std/fs/ensure_symlink.ts b/std/fs/ensure_symlink.ts deleted file mode 100644 index 63ef6f1626..0000000000 --- a/std/fs/ensure_symlink.ts +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import * as path from "../path/mod.ts"; -import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; -import { exists, existsSync } from "./exists.ts"; -import { getFileInfoType } from "./_util.ts"; -import { isWindows } from "../_util/os.ts"; - -/** - * Ensures that the link exists. - * If the directory structure does not exist, it is created. - * - * @param src the source file path - * @param dest the destination link path - */ -export async function ensureSymlink(src: string, dest: string): Promise { - const srcStatInfo = await Deno.lstat(src); - const srcFilePathType = getFileInfoType(srcStatInfo); - - if (await exists(dest)) { - const destStatInfo = await Deno.lstat(dest); - const destFilePathType = getFileInfoType(destStatInfo); - if (destFilePathType !== "symlink") { - throw new Error( - `Ensure path exists, expected 'symlink', got '${destFilePathType}'`, - ); - } - return; - } - - await ensureDir(path.dirname(dest)); - - const options: Deno.SymlinkOptions | undefined = isWindows - ? { - type: srcFilePathType === "dir" ? "dir" : "file", - } - : undefined; - - await Deno.symlink(src, dest, options); -} - -/** - * Ensures that the link exists. - * If the directory structure does not exist, it is created. - * - * @param src the source file path - * @param dest the destination link path - */ -export function ensureSymlinkSync(src: string, dest: string): void { - const srcStatInfo = Deno.lstatSync(src); - const srcFilePathType = getFileInfoType(srcStatInfo); - - if (existsSync(dest)) { - const destStatInfo = Deno.lstatSync(dest); - const destFilePathType = getFileInfoType(destStatInfo); - if (destFilePathType !== "symlink") { - throw new Error( - `Ensure path exists, expected 'symlink', got '${destFilePathType}'`, - ); - } - return; - } - - ensureDirSync(path.dirname(dest)); - - const options: Deno.SymlinkOptions | undefined = isWindows - ? { - type: srcFilePathType === "dir" ? "dir" : "file", - } - : undefined; - - Deno.symlinkSync(src, dest, options); -} diff --git a/std/fs/ensure_symlink_test.ts b/std/fs/ensure_symlink_test.ts deleted file mode 100644 index 64adfa8126..0000000000 --- a/std/fs/ensure_symlink_test.ts +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// TODO(axetroy): Add test for Windows once symlink is implemented for Windows. -import { - assertEquals, - assertThrows, - assertThrowsAsync, -} from "../testing/asserts.ts"; -import * as path from "../path/mod.ts"; -import { ensureSymlink, ensureSymlinkSync } from "./ensure_symlink.ts"; - -const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); -const testdataDir = path.resolve(moduleDir, "testdata"); - -Deno.test("ensureSymlinkIfItNotExist", async function (): Promise { - const testDir = path.join(testdataDir, "link_file_1"); - const testFile = path.join(testDir, "test.txt"); - - await assertThrowsAsync( - async (): Promise => { - await ensureSymlink(testFile, path.join(testDir, "test1.txt")); - }, - ); - - await assertThrowsAsync( - async (): Promise => { - await Deno.stat(testFile).then((): void => { - throw new Error("test file should exists."); - }); - }, - ); -}); - -Deno.test("ensureSymlinkSyncIfItNotExist", function (): void { - const testDir = path.join(testdataDir, "link_file_2"); - const testFile = path.join(testDir, "test.txt"); - - assertThrows((): void => { - ensureSymlinkSync(testFile, path.join(testDir, "test1.txt")); - }); - - assertThrows((): void => { - Deno.statSync(testFile); - throw new Error("test file should exists."); - }); -}); - -Deno.test("ensureSymlinkIfItExist", async function (): Promise { - const testDir = path.join(testdataDir, "link_file_3"); - const testFile = path.join(testDir, "test.txt"); - const linkFile = path.join(testDir, "link.txt"); - - await Deno.mkdir(testDir, { recursive: true }); - await Deno.writeFile(testFile, new Uint8Array()); - - await ensureSymlink(testFile, linkFile); - - const srcStat = await Deno.lstat(testFile); - const linkStat = await Deno.lstat(linkFile); - - assertEquals(srcStat.isFile, true); - assertEquals(linkStat.isSymlink, true); - - await Deno.remove(testDir, { recursive: true }); -}); - -Deno.test("ensureSymlinkSyncIfItExist", function (): void { - const testDir = path.join(testdataDir, "link_file_4"); - const testFile = path.join(testDir, "test.txt"); - const linkFile = path.join(testDir, "link.txt"); - - Deno.mkdirSync(testDir, { recursive: true }); - Deno.writeFileSync(testFile, new Uint8Array()); - - ensureSymlinkSync(testFile, linkFile); - - const srcStat = Deno.lstatSync(testFile); - - const linkStat = Deno.lstatSync(linkFile); - - assertEquals(srcStat.isFile, true); - assertEquals(linkStat.isSymlink, true); - - Deno.removeSync(testDir, { recursive: true }); -}); - -Deno.test("ensureSymlinkDirectoryIfItExist", async function (): Promise { - const testDir = path.join(testdataDir, "link_file_origin_3"); - const linkDir = path.join(testdataDir, "link_file_link_3"); - const testFile = path.join(testDir, "test.txt"); - - await Deno.mkdir(testDir, { recursive: true }); - await Deno.writeFile(testFile, new Uint8Array()); - - await ensureSymlink(testDir, linkDir); - - const testDirStat = await Deno.lstat(testDir); - const linkDirStat = await Deno.lstat(linkDir); - const testFileStat = await Deno.lstat(testFile); - - assertEquals(testFileStat.isFile, true); - assertEquals(testDirStat.isDirectory, true); - assertEquals(linkDirStat.isSymlink, true); - - await Deno.remove(linkDir, { recursive: true }); - await Deno.remove(testDir, { recursive: true }); -}); - -Deno.test("ensureSymlinkSyncDirectoryIfItExist", function (): void { - const testDir = path.join(testdataDir, "link_file_origin_3"); - const linkDir = path.join(testdataDir, "link_file_link_3"); - const testFile = path.join(testDir, "test.txt"); - - Deno.mkdirSync(testDir, { recursive: true }); - Deno.writeFileSync(testFile, new Uint8Array()); - - ensureSymlinkSync(testDir, linkDir); - - const testDirStat = Deno.lstatSync(testDir); - const linkDirStat = Deno.lstatSync(linkDir); - const testFileStat = Deno.lstatSync(testFile); - - assertEquals(testFileStat.isFile, true); - assertEquals(testDirStat.isDirectory, true); - assertEquals(linkDirStat.isSymlink, true); - - Deno.removeSync(linkDir, { recursive: true }); - Deno.removeSync(testDir, { recursive: true }); -}); diff --git a/std/fs/eol.ts b/std/fs/eol.ts deleted file mode 100644 index 25797bd719..0000000000 --- a/std/fs/eol.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -/** EndOfLine character enum */ -export enum EOL { - LF = "\n", - CRLF = "\r\n", -} - -const regDetect = /(?:\r?\n)/g; - -/** - * Detect the EOL character for string input. - * returns null if no newline - */ -export function detect(content: string): EOL | null { - const d = content.match(regDetect); - if (!d || d.length === 0) { - return null; - } - const hasCRLF = d.some((x: string): boolean => x === EOL.CRLF); - - return hasCRLF ? EOL.CRLF : EOL.LF; -} - -/** Format the file to the targeted EOL */ -export function format(content: string, eol: EOL): string { - return content.replace(regDetect, eol); -} diff --git a/std/fs/eol_test.ts b/std/fs/eol_test.ts deleted file mode 100644 index 23e02f4a34..0000000000 --- a/std/fs/eol_test.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { detect, EOL, format } from "./eol.ts"; - -const CRLFinput = "deno\r\nis not\r\nnode"; -const Mixedinput = "deno\nis not\r\nnode"; -const Mixedinput2 = "deno\r\nis not\nnode"; -const LFinput = "deno\nis not\nnode"; -const NoNLinput = "deno is not node"; - -Deno.test({ - name: "[EOL] Detect CR LF", - fn(): void { - assertEquals(detect(CRLFinput), EOL.CRLF); - }, -}); - -Deno.test({ - name: "[EOL] Detect LF", - fn(): void { - assertEquals(detect(LFinput), EOL.LF); - }, -}); - -Deno.test({ - name: "[EOL] Detect No New Line", - fn(): void { - assertEquals(detect(NoNLinput), null); - }, -}); - -Deno.test({ - name: "[EOL] Detect Mixed", - fn(): void { - assertEquals(detect(Mixedinput), EOL.CRLF); - assertEquals(detect(Mixedinput2), EOL.CRLF); - }, -}); - -Deno.test({ - name: "[EOL] Format", - fn(): void { - assertEquals(format(CRLFinput, EOL.LF), LFinput); - assertEquals(format(LFinput, EOL.LF), LFinput); - assertEquals(format(LFinput, EOL.CRLF), CRLFinput); - assertEquals(format(CRLFinput, EOL.CRLF), CRLFinput); - assertEquals(format(CRLFinput, EOL.CRLF), CRLFinput); - assertEquals(format(NoNLinput, EOL.CRLF), NoNLinput); - assertEquals(format(Mixedinput, EOL.CRLF), CRLFinput); - assertEquals(format(Mixedinput, EOL.LF), LFinput); - assertEquals(format(Mixedinput2, EOL.CRLF), CRLFinput); - assertEquals(format(Mixedinput2, EOL.LF), LFinput); - }, -}); diff --git a/std/fs/exists.ts b/std/fs/exists.ts deleted file mode 100644 index e98bbcc70f..0000000000 --- a/std/fs/exists.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -/** - * Test whether or not the given path exists by checking with the file system - */ -export async function exists(filePath: string): Promise { - try { - await Deno.lstat(filePath); - return true; - } catch (err) { - if (err instanceof Deno.errors.NotFound) { - return false; - } - - throw err; - } -} - -/** - * Test whether or not the given path exists by checking with the file system - */ -export function existsSync(filePath: string): boolean { - try { - Deno.lstatSync(filePath); - return true; - } catch (err) { - if (err instanceof Deno.errors.NotFound) { - return false; - } - throw err; - } -} diff --git a/std/fs/exists_test.ts b/std/fs/exists_test.ts deleted file mode 100644 index b3004ec0e9..0000000000 --- a/std/fs/exists_test.ts +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals, assertStringIncludes } from "../testing/asserts.ts"; -import * as path from "../path/mod.ts"; -import { exists, existsSync } from "./exists.ts"; - -const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); -const testdataDir = path.resolve(moduleDir, "testdata"); - -Deno.test("[fs] existsFile", async function (): Promise { - assertEquals( - await exists(path.join(testdataDir, "not_exist_file.ts")), - false, - ); - assertEquals(await existsSync(path.join(testdataDir, "0.ts")), true); -}); - -Deno.test("[fs] existsFileSync", function (): void { - assertEquals(existsSync(path.join(testdataDir, "not_exist_file.ts")), false); - assertEquals(existsSync(path.join(testdataDir, "0.ts")), true); -}); - -Deno.test("[fs] existsDirectory", async function (): Promise { - assertEquals( - await exists(path.join(testdataDir, "not_exist_directory")), - false, - ); - assertEquals(existsSync(testdataDir), true); -}); - -Deno.test("[fs] existsDirectorySync", function (): void { - assertEquals( - existsSync(path.join(testdataDir, "not_exist_directory")), - false, - ); - assertEquals(existsSync(testdataDir), true); -}); - -Deno.test("[fs] existsLinkSync", function (): void { - // TODO(axetroy): generate link file use Deno api instead of set a link file - // in repository - assertEquals(existsSync(path.join(testdataDir, "0-link")), true); -}); - -Deno.test("[fs] existsLink", async function (): Promise { - // TODO(axetroy): generate link file use Deno api instead of set a link file - // in repository - assertEquals(await exists(path.join(testdataDir, "0-link")), true); -}); - -interface Scenes { - read: boolean; // --allow-read - async: boolean; - output: string; - file: string; // target file to run -} - -const scenes: Scenes[] = [ - // 1 - { - read: false, - async: true, - output: "run again with the --allow-read flag", - file: "0.ts", - }, - { - read: false, - async: false, - output: "run again with the --allow-read flag", - file: "0.ts", - }, - // 2 - { - read: true, - async: true, - output: "exist", - file: "0.ts", - }, - { - read: true, - async: false, - output: "exist", - file: "0.ts", - }, - // 3 - { - read: false, - async: true, - output: "run again with the --allow-read flag", - file: "no_exist_file_for_test.ts", - }, - { - read: false, - async: false, - output: "run again with the --allow-read flag", - file: "no_exist_file_for_test.ts", - }, - // 4 - { - read: true, - async: true, - output: "not exist", - file: "no_exist_file_for_test.ts", - }, - { - read: true, - async: false, - output: "not exist", - file: "no_exist_file_for_test.ts", - }, -]; - -for (const s of scenes) { - let title = `test ${s.async ? "exists" : "existsSync"}("testdata/${s.file}")`; - title += ` ${s.read ? "with" : "without"} --allow-read`; - Deno.test(`[fs] existsPermission ${title}`, async function (): Promise { - const args = [Deno.execPath(), "run", "--quiet"]; - - if (s.read) { - args.push("--allow-read"); - } - - args.push(path.join(testdataDir, s.async ? "exists.ts" : "exists_sync.ts")); - args.push(s.file); - - const p = Deno.run({ - stdout: "piped", - cwd: testdataDir, - cmd: args, - }); - - const output = await p.output(); - p.close(); - assertStringIncludes(new TextDecoder().decode(output), s.output); - }); - // done -} diff --git a/std/fs/expand_glob.ts b/std/fs/expand_glob.ts deleted file mode 100644 index ec0f560d82..0000000000 --- a/std/fs/expand_glob.ts +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - GlobOptions, - globToRegExp, - isAbsolute, - isGlob, - joinGlobs, - normalize, - SEP_PATTERN, -} from "../path/mod.ts"; -import { - _createWalkEntry, - _createWalkEntrySync, - walk, - WalkEntry, - walkSync, -} from "./walk.ts"; -import { assert } from "../_util/assert.ts"; -import { isWindows } from "../_util/os.ts"; - -export interface ExpandGlobOptions extends Omit { - root?: string; - exclude?: string[]; - includeDirs?: boolean; -} - -interface SplitPath { - segments: string[]; - isAbsolute: boolean; - hasTrailingSep: boolean; - // Defined for any absolute Windows path. - winRoot?: string; -} - -function split(path: string): SplitPath { - const s = SEP_PATTERN.source; - const segments = path - .replace(new RegExp(`^${s}|${s}$`, "g"), "") - .split(SEP_PATTERN); - const isAbsolute_ = isAbsolute(path); - return { - segments, - isAbsolute: isAbsolute_, - hasTrailingSep: !!path.match(new RegExp(`${s}$`)), - winRoot: isWindows && isAbsolute_ ? segments.shift() : undefined, - }; -} - -function throwUnlessNotFound(error: Error): void { - if (!(error instanceof Deno.errors.NotFound)) { - throw error; - } -} - -function comparePath(a: WalkEntry, b: WalkEntry): number { - if (a.path < b.path) return -1; - if (a.path > b.path) return 1; - return 0; -} - -/** Expand the glob string from the specified `root` directory and yield each - * result as a `WalkEntry` object. - * - * See [`globToRegExp()`](../path/glob.ts#globToRegExp) for details on supported - * syntax. - * - * Example: - * - * for await (const file of expandGlob("**\/*.ts")) { - * console.log(file); - * } - */ -export async function* expandGlob( - glob: string, - { - root = Deno.cwd(), - exclude = [], - includeDirs = true, - extended = false, - globstar = false, - }: ExpandGlobOptions = {}, -): AsyncIterableIterator { - const globOptions: GlobOptions = { extended, globstar }; - const absRoot = isAbsolute(root) - ? normalize(root) - : joinGlobs([Deno.cwd(), root], globOptions); - const resolveFromRoot = (path: string): string => - isAbsolute(path) - ? normalize(path) - : joinGlobs([absRoot, path], globOptions); - const excludePatterns = exclude - .map(resolveFromRoot) - .map((s: string): RegExp => globToRegExp(s, globOptions)); - const shouldInclude = (path: string): boolean => - !excludePatterns.some((p: RegExp): boolean => !!path.match(p)); - const { segments, hasTrailingSep, winRoot } = split(resolveFromRoot(glob)); - - let fixedRoot = winRoot != undefined ? winRoot : "/"; - while (segments.length > 0 && !isGlob(segments[0])) { - const seg = segments.shift(); - assert(seg != null); - fixedRoot = joinGlobs([fixedRoot, seg], globOptions); - } - - let fixedRootInfo: WalkEntry; - try { - fixedRootInfo = await _createWalkEntry(fixedRoot); - } catch (error) { - return throwUnlessNotFound(error); - } - - async function* advanceMatch( - walkInfo: WalkEntry, - globSegment: string, - ): AsyncIterableIterator { - if (!walkInfo.isDirectory) { - return; - } else if (globSegment == "..") { - const parentPath = joinGlobs([walkInfo.path, ".."], globOptions); - try { - if (shouldInclude(parentPath)) { - return yield await _createWalkEntry(parentPath); - } - } catch (error) { - throwUnlessNotFound(error); - } - return; - } else if (globSegment == "**") { - return yield* walk(walkInfo.path, { - includeFiles: false, - skip: excludePatterns, - }); - } - yield* walk(walkInfo.path, { - maxDepth: 1, - match: [ - globToRegExp( - joinGlobs([walkInfo.path, globSegment], globOptions), - globOptions, - ), - ], - skip: excludePatterns, - }); - } - - let currentMatches: WalkEntry[] = [fixedRootInfo]; - for (const segment of segments) { - // Advancing the list of current matches may introduce duplicates, so we - // pass everything through this Map. - const nextMatchMap: Map = new Map(); - for (const currentMatch of currentMatches) { - for await (const nextMatch of advanceMatch(currentMatch, segment)) { - nextMatchMap.set(nextMatch.path, nextMatch); - } - } - currentMatches = [...nextMatchMap.values()].sort(comparePath); - } - if (hasTrailingSep) { - currentMatches = currentMatches.filter( - (entry: WalkEntry): boolean => entry.isDirectory, - ); - } - if (!includeDirs) { - currentMatches = currentMatches.filter( - (entry: WalkEntry): boolean => !entry.isDirectory, - ); - } - yield* currentMatches; -} - -/** Synchronous version of `expandGlob()`. - * - * Example: - * - * for (const file of expandGlobSync("**\/*.ts")) { - * console.log(file); - * } - */ -export function* expandGlobSync( - glob: string, - { - root = Deno.cwd(), - exclude = [], - includeDirs = true, - extended = false, - globstar = false, - }: ExpandGlobOptions = {}, -): IterableIterator { - const globOptions: GlobOptions = { extended, globstar }; - const absRoot = isAbsolute(root) - ? normalize(root) - : joinGlobs([Deno.cwd(), root], globOptions); - const resolveFromRoot = (path: string): string => - isAbsolute(path) - ? normalize(path) - : joinGlobs([absRoot, path], globOptions); - const excludePatterns = exclude - .map(resolveFromRoot) - .map((s: string): RegExp => globToRegExp(s, globOptions)); - const shouldInclude = (path: string): boolean => - !excludePatterns.some((p: RegExp): boolean => !!path.match(p)); - const { segments, hasTrailingSep, winRoot } = split(resolveFromRoot(glob)); - - let fixedRoot = winRoot != undefined ? winRoot : "/"; - while (segments.length > 0 && !isGlob(segments[0])) { - const seg = segments.shift(); - assert(seg != null); - fixedRoot = joinGlobs([fixedRoot, seg], globOptions); - } - - let fixedRootInfo: WalkEntry; - try { - fixedRootInfo = _createWalkEntrySync(fixedRoot); - } catch (error) { - return throwUnlessNotFound(error); - } - - function* advanceMatch( - walkInfo: WalkEntry, - globSegment: string, - ): IterableIterator { - if (!walkInfo.isDirectory) { - return; - } else if (globSegment == "..") { - const parentPath = joinGlobs([walkInfo.path, ".."], globOptions); - try { - if (shouldInclude(parentPath)) { - return yield _createWalkEntrySync(parentPath); - } - } catch (error) { - throwUnlessNotFound(error); - } - return; - } else if (globSegment == "**") { - return yield* walkSync(walkInfo.path, { - includeFiles: false, - skip: excludePatterns, - }); - } - yield* walkSync(walkInfo.path, { - maxDepth: 1, - match: [ - globToRegExp( - joinGlobs([walkInfo.path, globSegment], globOptions), - globOptions, - ), - ], - skip: excludePatterns, - }); - } - - let currentMatches: WalkEntry[] = [fixedRootInfo]; - for (const segment of segments) { - // Advancing the list of current matches may introduce duplicates, so we - // pass everything through this Map. - const nextMatchMap: Map = new Map(); - for (const currentMatch of currentMatches) { - for (const nextMatch of advanceMatch(currentMatch, segment)) { - nextMatchMap.set(nextMatch.path, nextMatch); - } - } - currentMatches = [...nextMatchMap.values()].sort(comparePath); - } - if (hasTrailingSep) { - currentMatches = currentMatches.filter( - (entry: WalkEntry): boolean => entry.isDirectory, - ); - } - if (!includeDirs) { - currentMatches = currentMatches.filter( - (entry: WalkEntry): boolean => !entry.isDirectory, - ); - } - yield* currentMatches; -} diff --git a/std/fs/expand_glob_test.ts b/std/fs/expand_glob_test.ts deleted file mode 100644 index da9e89e6b7..0000000000 --- a/std/fs/expand_glob_test.ts +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { decode } from "../encoding/utf8.ts"; -import { - assert, - assertEquals, - assertStringIncludes, -} from "../testing/asserts.ts"; -import { - fromFileUrl, - join, - joinGlobs, - normalize, - relative, -} from "../path/mod.ts"; -import { - expandGlob, - ExpandGlobOptions, - expandGlobSync, -} from "./expand_glob.ts"; - -async function expandGlobArray( - globString: string, - options: ExpandGlobOptions, -): Promise { - const paths: string[] = []; - for await (const { path } of expandGlob(globString, options)) { - paths.push(path); - } - paths.sort(); - const pathsSync = [...expandGlobSync(globString, options)].map( - ({ path }): string => path, - ); - pathsSync.sort(); - assertEquals(paths, pathsSync); - const root = normalize(options.root || Deno.cwd()); - for (const path of paths) { - assert(path.startsWith(root)); - } - const relativePaths = paths.map( - (path: string): string => relative(root, path) || ".", - ); - relativePaths.sort(); - return relativePaths; -} - -const EG_OPTIONS: ExpandGlobOptions = { - root: fromFileUrl(new URL(join("testdata", "glob"), import.meta.url)), - includeDirs: true, - extended: false, - globstar: false, -}; - -Deno.test("expandGlobWildcard", async function (): Promise { - const options = EG_OPTIONS; - assertEquals(await expandGlobArray("*", options), [ - "abc", - "abcdef", - "abcdefghi", - "subdir", - ]); -}); - -Deno.test("expandGlobTrailingSeparator", async function (): Promise { - const options = EG_OPTIONS; - assertEquals(await expandGlobArray("*/", options), ["subdir"]); -}); - -Deno.test("expandGlobParent", async function (): Promise { - const options = EG_OPTIONS; - assertEquals(await expandGlobArray("subdir/../*", options), [ - "abc", - "abcdef", - "abcdefghi", - "subdir", - ]); -}); - -Deno.test("expandGlobExt", async function (): Promise { - const options = { ...EG_OPTIONS, extended: true }; - assertEquals(await expandGlobArray("abc?(def|ghi)", options), [ - "abc", - "abcdef", - ]); - assertEquals(await expandGlobArray("abc*(def|ghi)", options), [ - "abc", - "abcdef", - "abcdefghi", - ]); - assertEquals(await expandGlobArray("abc+(def|ghi)", options), [ - "abcdef", - "abcdefghi", - ]); - assertEquals(await expandGlobArray("abc@(def|ghi)", options), ["abcdef"]); - assertEquals(await expandGlobArray("abc{def,ghi}", options), ["abcdef"]); - assertEquals(await expandGlobArray("abc!(def|ghi)", options), ["abc"]); -}); - -Deno.test("expandGlobGlobstar", async function (): Promise { - const options = { ...EG_OPTIONS, globstar: true }; - assertEquals( - await expandGlobArray(joinGlobs(["**", "abc"], options), options), - ["abc", join("subdir", "abc")], - ); -}); - -Deno.test("expandGlobGlobstarParent", async function (): Promise { - const options = { ...EG_OPTIONS, globstar: true }; - assertEquals( - await expandGlobArray(joinGlobs(["subdir", "**", ".."], options), options), - ["."], - ); -}); - -Deno.test("expandGlobIncludeDirs", async function (): Promise { - const options = { ...EG_OPTIONS, includeDirs: false }; - assertEquals(await expandGlobArray("subdir", options), []); -}); - -Deno.test("expandGlobPermError", async function (): Promise { - const exampleUrl = new URL("testdata/expand_wildcard.js", import.meta.url); - const p = Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - "--unstable", - exampleUrl.toString(), - ], - stdin: "null", - stdout: "piped", - stderr: "piped", - }); - assertEquals(await p.status(), { code: 1, success: false }); - assertEquals(decode(await p.output()), ""); - assertStringIncludes( - decode(await p.stderrOutput()), - "Uncaught PermissionDenied", - ); - p.close(); -}); diff --git a/std/fs/mod.ts b/std/fs/mod.ts deleted file mode 100644 index 98dd3daa50..0000000000 --- a/std/fs/mod.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -export * from "./empty_dir.ts"; -export * from "./ensure_dir.ts"; -export * from "./ensure_file.ts"; -export * from "./ensure_link.ts"; -export * from "./ensure_symlink.ts"; -export * from "./exists.ts"; -export * from "./expand_glob.ts"; -export * from "./move.ts"; -export * from "./copy.ts"; -export * from "./walk.ts"; -export * from "./eol.ts"; diff --git a/std/fs/move.ts b/std/fs/move.ts deleted file mode 100644 index 2e34e73871..0000000000 --- a/std/fs/move.ts +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { exists, existsSync } from "./exists.ts"; -import { isSubdir } from "./_util.ts"; - -interface MoveOptions { - overwrite?: boolean; -} - -/** Moves a file or directory */ -export async function move( - src: string, - dest: string, - { overwrite = false }: MoveOptions = {}, -): Promise { - const srcStat = await Deno.stat(src); - - if (srcStat.isDirectory && isSubdir(src, dest)) { - throw new Error( - `Cannot move '${src}' to a subdirectory of itself, '${dest}'.`, - ); - } - - if (overwrite) { - if (await exists(dest)) { - await Deno.remove(dest, { recursive: true }); - } - } else { - if (await exists(dest)) { - throw new Error("dest already exists."); - } - } - - await Deno.rename(src, dest); - - return; -} - -/** Moves a file or directory synchronously */ -export function moveSync( - src: string, - dest: string, - { overwrite = false }: MoveOptions = {}, -): void { - const srcStat = Deno.statSync(src); - - if (srcStat.isDirectory && isSubdir(src, dest)) { - throw new Error( - `Cannot move '${src}' to a subdirectory of itself, '${dest}'.`, - ); - } - - if (overwrite) { - if (existsSync(dest)) { - Deno.removeSync(dest, { recursive: true }); - } - } else { - if (existsSync(dest)) { - throw new Error("dest already exists."); - } - } - - Deno.renameSync(src, dest); -} diff --git a/std/fs/move_test.ts b/std/fs/move_test.ts deleted file mode 100644 index 048193755a..0000000000 --- a/std/fs/move_test.ts +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assertEquals, - assertThrows, - assertThrowsAsync, -} from "../testing/asserts.ts"; -import * as path from "../path/mod.ts"; -import { move, moveSync } from "./move.ts"; -import { ensureFile, ensureFileSync } from "./ensure_file.ts"; -import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; -import { exists, existsSync } from "./exists.ts"; - -const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); -const testdataDir = path.resolve(moduleDir, "testdata"); - -Deno.test("moveDirectoryIfSrcNotExists", async function (): Promise { - const srcDir = path.join(testdataDir, "move_test_src_1"); - const destDir = path.join(testdataDir, "move_test_dest_1"); - // if src directory not exist - await assertThrowsAsync( - async (): Promise => { - await move(srcDir, destDir); - }, - ); -}); - -Deno.test("moveDirectoryIfDestNotExists", async function (): Promise { - const srcDir = path.join(testdataDir, "move_test_src_2"); - const destDir = path.join(testdataDir, "move_test_dest_2"); - - await Deno.mkdir(srcDir, { recursive: true }); - - // if dest directory not exist - await assertThrowsAsync( - async (): Promise => { - await move(srcDir, destDir); - throw new Error("should not throw error"); - }, - Error, - "should not throw error", - ); - - await Deno.remove(destDir); -}); - -Deno.test( - "moveDirectoryIfDestNotExistsAndOverwrite", - async function (): Promise { - const srcDir = path.join(testdataDir, "move_test_src_2"); - const destDir = path.join(testdataDir, "move_test_dest_2"); - - await Deno.mkdir(srcDir, { recursive: true }); - - // if dest directory not exist - await assertThrowsAsync( - async (): Promise => { - await move(srcDir, destDir, { overwrite: true }); - throw new Error("should not throw error"); - }, - Error, - "should not throw error", - ); - - await Deno.remove(destDir); - }, -); - -Deno.test("moveFileIfSrcNotExists", async function (): Promise { - const srcFile = path.join(testdataDir, "move_test_src_3", "test.txt"); - const destFile = path.join(testdataDir, "move_test_dest_3", "test.txt"); - - // if src directory not exist - await assertThrowsAsync( - async (): Promise => { - await move(srcFile, destFile); - }, - ); -}); - -Deno.test("moveFileIfDestExists", async function (): Promise { - const srcDir = path.join(testdataDir, "move_test_src_4"); - const destDir = path.join(testdataDir, "move_test_dest_4"); - const srcFile = path.join(srcDir, "test.txt"); - const destFile = path.join(destDir, "test.txt"); - const srcContent = new TextEncoder().encode("src"); - const destContent = new TextEncoder().encode("dest"); - - // make sure files exists - await Promise.all([ensureFile(srcFile), ensureFile(destFile)]); - - // write file content - await Promise.all([ - Deno.writeFile(srcFile, srcContent), - Deno.writeFile(destFile, destContent), - ]); - - // make sure the test file have been created - assertEquals(new TextDecoder().decode(await Deno.readFile(srcFile)), "src"); - assertEquals(new TextDecoder().decode(await Deno.readFile(destFile)), "dest"); - - // move it without override - await assertThrowsAsync( - async (): Promise => { - await move(srcFile, destFile); - }, - Error, - "dest already exists", - ); - - // move again with overwrite - await assertThrowsAsync( - async (): Promise => { - await move(srcFile, destFile, { overwrite: true }); - throw new Error("should not throw error"); - }, - Error, - "should not throw error", - ); - - assertEquals(await exists(srcFile), false); - assertEquals(new TextDecoder().decode(await Deno.readFile(destFile)), "src"); - - // clean up - await Promise.all([ - Deno.remove(srcDir, { recursive: true }), - Deno.remove(destDir, { recursive: true }), - ]); -}); - -Deno.test("moveDirectory", async function (): Promise { - const srcDir = path.join(testdataDir, "move_test_src_5"); - const destDir = path.join(testdataDir, "move_test_dest_5"); - const srcFile = path.join(srcDir, "test.txt"); - const destFile = path.join(destDir, "test.txt"); - const srcContent = new TextEncoder().encode("src"); - - await Deno.mkdir(srcDir, { recursive: true }); - assertEquals(await exists(srcDir), true); - await Deno.writeFile(srcFile, srcContent); - - await move(srcDir, destDir); - - assertEquals(await exists(srcDir), false); - assertEquals(await exists(destDir), true); - assertEquals(await exists(destFile), true); - - const destFileContent = new TextDecoder().decode( - await Deno.readFile(destFile), - ); - assertEquals(destFileContent, "src"); - - await Deno.remove(destDir, { recursive: true }); -}); - -Deno.test( - "moveIfSrcAndDestDirectoryExistsAndOverwrite", - async function (): Promise { - const srcDir = path.join(testdataDir, "move_test_src_6"); - const destDir = path.join(testdataDir, "move_test_dest_6"); - const srcFile = path.join(srcDir, "test.txt"); - const destFile = path.join(destDir, "test.txt"); - const srcContent = new TextEncoder().encode("src"); - const destContent = new TextEncoder().encode("dest"); - - await Promise.all([ - Deno.mkdir(srcDir, { recursive: true }), - Deno.mkdir(destDir, { recursive: true }), - ]); - assertEquals(await exists(srcDir), true); - assertEquals(await exists(destDir), true); - await Promise.all([ - Deno.writeFile(srcFile, srcContent), - Deno.writeFile(destFile, destContent), - ]); - - await move(srcDir, destDir, { overwrite: true }); - - assertEquals(await exists(srcDir), false); - assertEquals(await exists(destDir), true); - assertEquals(await exists(destFile), true); - - const destFileContent = new TextDecoder().decode( - await Deno.readFile(destFile), - ); - assertEquals(destFileContent, "src"); - - await Deno.remove(destDir, { recursive: true }); - }, -); - -Deno.test("moveIntoSubDir", async function (): Promise { - const srcDir = path.join(testdataDir, "move_test_src_7"); - const destDir = path.join(srcDir, "nest"); - - await ensureDir(destDir); - - await assertThrowsAsync( - async (): Promise => { - await move(srcDir, destDir); - }, - Error, - `Cannot move '${srcDir}' to a subdirectory of itself, '${destDir}'.`, - ); - await Deno.remove(srcDir, { recursive: true }); -}); - -Deno.test("moveSyncDirectoryIfSrcNotExists", function (): void { - const srcDir = path.join(testdataDir, "move_sync_test_src_1"); - const destDir = path.join(testdataDir, "move_sync_test_dest_1"); - // if src directory not exist - assertThrows((): void => { - moveSync(srcDir, destDir); - }); -}); - -Deno.test("moveSyncDirectoryIfDestNotExists", function (): void { - const srcDir = path.join(testdataDir, "move_sync_test_src_2"); - const destDir = path.join(testdataDir, "move_sync_test_dest_2"); - - Deno.mkdirSync(srcDir, { recursive: true }); - - // if dest directory not exist - assertThrows( - (): void => { - moveSync(srcDir, destDir); - throw new Error("should not throw error"); - }, - Error, - "should not throw error", - ); - - Deno.removeSync(destDir); -}); - -Deno.test("moveSyncDirectoryIfDestNotExistsAndOverwrite", function (): void { - const srcDir = path.join(testdataDir, "move_sync_test_src_2"); - const destDir = path.join(testdataDir, "move_sync_test_dest_2"); - - Deno.mkdirSync(srcDir, { recursive: true }); - - // if dest directory not exist width overwrite - assertThrows( - (): void => { - moveSync(srcDir, destDir, { overwrite: true }); - throw new Error("should not throw error"); - }, - Error, - "should not throw error", - ); - - Deno.removeSync(destDir); -}); - -Deno.test("moveSyncFileIfSrcNotExists", function (): void { - const srcFile = path.join(testdataDir, "move_sync_test_src_3", "test.txt"); - const destFile = path.join(testdataDir, "move_sync_test_dest_3", "test.txt"); - - // if src directory not exist - assertThrows((): void => { - moveSync(srcFile, destFile); - }); -}); - -Deno.test("moveSyncFileIfDestExists", function (): void { - const srcDir = path.join(testdataDir, "move_sync_test_src_4"); - const destDir = path.join(testdataDir, "move_sync_test_dest_4"); - const srcFile = path.join(srcDir, "test.txt"); - const destFile = path.join(destDir, "test.txt"); - const srcContent = new TextEncoder().encode("src"); - const destContent = new TextEncoder().encode("dest"); - - // make sure files exists - ensureFileSync(srcFile); - ensureFileSync(destFile); - - // write file content - Deno.writeFileSync(srcFile, srcContent); - Deno.writeFileSync(destFile, destContent); - - // make sure the test file have been created - assertEquals(new TextDecoder().decode(Deno.readFileSync(srcFile)), "src"); - assertEquals(new TextDecoder().decode(Deno.readFileSync(destFile)), "dest"); - - // move it without override - assertThrows( - (): void => { - moveSync(srcFile, destFile); - }, - Error, - "dest already exists", - ); - - // move again with overwrite - assertThrows( - (): void => { - moveSync(srcFile, destFile, { overwrite: true }); - throw new Error("should not throw error"); - }, - Error, - "should not throw error", - ); - - assertEquals(existsSync(srcFile), false); - assertEquals(new TextDecoder().decode(Deno.readFileSync(destFile)), "src"); - - // clean up - Deno.removeSync(srcDir, { recursive: true }); - Deno.removeSync(destDir, { recursive: true }); -}); - -Deno.test("moveSyncDirectory", function (): void { - const srcDir = path.join(testdataDir, "move_sync_test_src_5"); - const destDir = path.join(testdataDir, "move_sync_test_dest_5"); - const srcFile = path.join(srcDir, "test.txt"); - const destFile = path.join(destDir, "test.txt"); - const srcContent = new TextEncoder().encode("src"); - - Deno.mkdirSync(srcDir, { recursive: true }); - assertEquals(existsSync(srcDir), true); - Deno.writeFileSync(srcFile, srcContent); - - moveSync(srcDir, destDir); - - assertEquals(existsSync(srcDir), false); - assertEquals(existsSync(destDir), true); - assertEquals(existsSync(destFile), true); - - const destFileContent = new TextDecoder().decode(Deno.readFileSync(destFile)); - assertEquals(destFileContent, "src"); - - Deno.removeSync(destDir, { recursive: true }); -}); - -Deno.test("moveSyncIfSrcAndDestDirectoryExistsAndOverwrite", function (): void { - const srcDir = path.join(testdataDir, "move_sync_test_src_6"); - const destDir = path.join(testdataDir, "move_sync_test_dest_6"); - const srcFile = path.join(srcDir, "test.txt"); - const destFile = path.join(destDir, "test.txt"); - const srcContent = new TextEncoder().encode("src"); - const destContent = new TextEncoder().encode("dest"); - - Deno.mkdirSync(srcDir, { recursive: true }); - Deno.mkdirSync(destDir, { recursive: true }); - assertEquals(existsSync(srcDir), true); - assertEquals(existsSync(destDir), true); - Deno.writeFileSync(srcFile, srcContent); - Deno.writeFileSync(destFile, destContent); - - moveSync(srcDir, destDir, { overwrite: true }); - - assertEquals(existsSync(srcDir), false); - assertEquals(existsSync(destDir), true); - assertEquals(existsSync(destFile), true); - - const destFileContent = new TextDecoder().decode(Deno.readFileSync(destFile)); - assertEquals(destFileContent, "src"); - - Deno.removeSync(destDir, { recursive: true }); -}); - -Deno.test("moveSyncIntoSubDir", function (): void { - const srcDir = path.join(testdataDir, "move_sync_test_src_7"); - const destDir = path.join(srcDir, "nest"); - - ensureDirSync(destDir); - - assertThrows( - (): void => { - moveSync(srcDir, destDir); - }, - Error, - `Cannot move '${srcDir}' to a subdirectory of itself, '${destDir}'.`, - ); - Deno.removeSync(srcDir, { recursive: true }); -}); diff --git a/std/fs/test.ts b/std/fs/test.ts deleted file mode 100644 index 590417055f..0000000000 --- a/std/fs/test.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import "./mod.ts"; diff --git a/std/fs/testdata/0-link b/std/fs/testdata/0-link deleted file mode 120000 index 937f1e899f..0000000000 --- a/std/fs/testdata/0-link +++ /dev/null @@ -1 +0,0 @@ -0.ts \ No newline at end of file diff --git a/std/fs/testdata/0.ts b/std/fs/testdata/0.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/std/fs/testdata/copy_dir/0.txt b/std/fs/testdata/copy_dir/0.txt deleted file mode 100644 index f3a34851d4..0000000000 --- a/std/fs/testdata/copy_dir/0.txt +++ /dev/null @@ -1 +0,0 @@ -text \ No newline at end of file diff --git a/std/fs/testdata/copy_dir/nest/0.txt b/std/fs/testdata/copy_dir/nest/0.txt deleted file mode 100644 index cd1b98cb37..0000000000 --- a/std/fs/testdata/copy_dir/nest/0.txt +++ /dev/null @@ -1 +0,0 @@ -nest \ No newline at end of file diff --git a/std/fs/testdata/copy_dir_link_file/0.txt b/std/fs/testdata/copy_dir_link_file/0.txt deleted file mode 120000 index 63413ea1c1..0000000000 --- a/std/fs/testdata/copy_dir_link_file/0.txt +++ /dev/null @@ -1 +0,0 @@ -./fs/testdata/copy_dir/0.txt \ No newline at end of file diff --git a/std/fs/testdata/copy_file.txt b/std/fs/testdata/copy_file.txt deleted file mode 100644 index 84c22fd8a1..0000000000 --- a/std/fs/testdata/copy_file.txt +++ /dev/null @@ -1 +0,0 @@ -txt \ No newline at end of file diff --git a/std/fs/testdata/empty_dir.ts b/std/fs/testdata/empty_dir.ts deleted file mode 100644 index f8fcc1cebe..0000000000 --- a/std/fs/testdata/empty_dir.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { emptyDir } from "../empty_dir.ts"; - -emptyDir(Deno.args[0]) - .then(() => { - Deno.stdout.write(new TextEncoder().encode("success")); - }) - .catch((err) => { - Deno.stdout.write(new TextEncoder().encode(err.message)); - }); diff --git a/std/fs/testdata/empty_dir_sync.ts b/std/fs/testdata/empty_dir_sync.ts deleted file mode 100644 index 0bef81e910..0000000000 --- a/std/fs/testdata/empty_dir_sync.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { emptyDirSync } from "../empty_dir.ts"; - -try { - emptyDirSync(Deno.args[0]) - Deno.stdout.write(new TextEncoder().encode("success")) -} catch (err) { - Deno.stdout.write(new TextEncoder().encode(err.message)) -} \ No newline at end of file diff --git a/std/fs/testdata/exists.ts b/std/fs/testdata/exists.ts deleted file mode 100644 index 93812a7038..0000000000 --- a/std/fs/testdata/exists.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { exists } from "../exists.ts"; - -exists(Deno.args[0]) - .then(isExist => { - Deno.stdout.write(new TextEncoder().encode(isExist ? 'exist' :'not exist')) - }) - .catch((err) => { - Deno.stdout.write(new TextEncoder().encode(err.message)) - }) - diff --git a/std/fs/testdata/exists_sync.ts b/std/fs/testdata/exists_sync.ts deleted file mode 100644 index 8cc51e9f8a..0000000000 --- a/std/fs/testdata/exists_sync.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { existsSync } from "../exists.ts"; - -try { - const isExist = existsSync(Deno.args[0]); - Deno.stdout.write(new TextEncoder().encode(isExist ? "exist" : "not exist")); -} catch (err) { - Deno.stdout.write(new TextEncoder().encode(err.message)); -} diff --git a/std/fs/testdata/expand_wildcard.js b/std/fs/testdata/expand_wildcard.js deleted file mode 100644 index a30a175360..0000000000 --- a/std/fs/testdata/expand_wildcard.js +++ /dev/null @@ -1,6 +0,0 @@ -import { expandGlob } from "../expand_glob.ts"; - -const glob = new URL("*", import.meta.url).pathname; -for await (const { filename } of expandGlob(glob)) { - console.log(filename); -} diff --git a/std/fs/testdata/glob/abc b/std/fs/testdata/glob/abc deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/std/fs/testdata/glob/abcdef b/std/fs/testdata/glob/abcdef deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/std/fs/testdata/glob/abcdefghi b/std/fs/testdata/glob/abcdefghi deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/std/fs/testdata/glob/subdir/abc b/std/fs/testdata/glob/subdir/abc deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/std/fs/testdata/json_empty.json b/std/fs/testdata/json_empty.json deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/std/fs/testdata/json_invalid.json b/std/fs/testdata/json_invalid.json deleted file mode 100644 index dd9f98ff07..0000000000 --- a/std/fs/testdata/json_invalid.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - [ - "here is a invalid json file" - ] -} \ No newline at end of file diff --git a/std/fs/testdata/json_valid_array.json b/std/fs/testdata/json_valid_array.json deleted file mode 100644 index 904968e154..0000000000 --- a/std/fs/testdata/json_valid_array.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "1", - "2", - "3" -] \ No newline at end of file diff --git a/std/fs/testdata/json_valid_obj.json b/std/fs/testdata/json_valid_obj.json deleted file mode 100644 index 88b3d7123a..0000000000 --- a/std/fs/testdata/json_valid_obj.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "key1": "value1", - "key2": "value2" -} \ No newline at end of file diff --git a/std/fs/walk.ts b/std/fs/walk.ts deleted file mode 100644 index 4ce5644354..0000000000 --- a/std/fs/walk.ts +++ /dev/null @@ -1,188 +0,0 @@ -// Documentation and interface for walk were adapted from Go -// https://golang.org/pkg/path/filepath/#Walk -// Copyright 2009 The Go Authors. All rights reserved. BSD license. -import { assert } from "../_util/assert.ts"; -import { basename, join, normalize } from "../path/mod.ts"; - -/** Create WalkEntry for the `path` synchronously */ -export function _createWalkEntrySync(path: string): WalkEntry { - path = normalize(path); - const name = basename(path); - const info = Deno.statSync(path); - return { - path, - name, - isFile: info.isFile, - isDirectory: info.isDirectory, - isSymlink: info.isSymlink, - }; -} - -/** Create WalkEntry for the `path` asynchronously */ -export async function _createWalkEntry(path: string): Promise { - path = normalize(path); - const name = basename(path); - const info = await Deno.stat(path); - return { - path, - name, - isFile: info.isFile, - isDirectory: info.isDirectory, - isSymlink: info.isSymlink, - }; -} - -export interface WalkOptions { - maxDepth?: number; - includeFiles?: boolean; - includeDirs?: boolean; - followSymlinks?: boolean; - exts?: string[]; - match?: RegExp[]; - skip?: RegExp[]; -} - -function include( - path: string, - exts?: string[], - match?: RegExp[], - skip?: RegExp[], -): boolean { - if (exts && !exts.some((ext): boolean => path.endsWith(ext))) { - return false; - } - if (match && !match.some((pattern): boolean => !!path.match(pattern))) { - return false; - } - if (skip && skip.some((pattern): boolean => !!path.match(pattern))) { - return false; - } - return true; -} - -export interface WalkEntry extends Deno.DirEntry { - path: string; -} - -/** Walks the file tree rooted at root, yielding each file or directory in the - * tree filtered according to the given options. The files are walked in lexical - * order, which makes the output deterministic but means that for very large - * directories walk() can be inefficient. - * - * Options: - * - maxDepth?: number = Infinity; - * - includeFiles?: boolean = true; - * - includeDirs?: boolean = true; - * - followSymlinks?: boolean = false; - * - exts?: string[]; - * - match?: RegExp[]; - * - skip?: RegExp[]; - * - * - * for await (const entry of walk(".")) { - * console.log(entry.path); - * assert(entry.isFile); - * } - */ -export async function* walk( - root: string, - { - maxDepth = Infinity, - includeFiles = true, - includeDirs = true, - followSymlinks = false, - exts = undefined, - match = undefined, - skip = undefined, - }: WalkOptions = {}, -): AsyncIterableIterator { - if (maxDepth < 0) { - return; - } - if (includeDirs && include(root, exts, match, skip)) { - yield await _createWalkEntry(root); - } - if (maxDepth < 1 || !include(root, undefined, undefined, skip)) { - return; - } - for await (const entry of Deno.readDir(root)) { - assert(entry.name != null); - let path = join(root, entry.name); - - if (entry.isSymlink) { - if (followSymlinks) { - path = await Deno.realPath(path); - } else { - continue; - } - } - - if (entry.isFile) { - if (includeFiles && include(path, exts, match, skip)) { - yield { path, ...entry }; - } - } else { - yield* walk(path, { - maxDepth: maxDepth - 1, - includeFiles, - includeDirs, - followSymlinks, - exts, - match, - skip, - }); - } - } -} - -/** Same as walk() but uses synchronous ops */ -export function* walkSync( - root: string, - { - maxDepth = Infinity, - includeFiles = true, - includeDirs = true, - followSymlinks = false, - exts = undefined, - match = undefined, - skip = undefined, - }: WalkOptions = {}, -): IterableIterator { - if (maxDepth < 0) { - return; - } - if (includeDirs && include(root, exts, match, skip)) { - yield _createWalkEntrySync(root); - } - if (maxDepth < 1 || !include(root, undefined, undefined, skip)) { - return; - } - for (const entry of Deno.readDirSync(root)) { - assert(entry.name != null); - let path = join(root, entry.name); - - if (entry.isSymlink) { - if (followSymlinks) { - path = Deno.realPathSync(path); - } else { - continue; - } - } - - if (entry.isFile) { - if (includeFiles && include(path, exts, match, skip)) { - yield { path, ...entry }; - } - } else { - yield* walkSync(path, { - maxDepth: maxDepth - 1, - includeFiles, - includeDirs, - followSymlinks, - exts, - match, - skip, - }); - } - } -} diff --git a/std/fs/walk_test.ts b/std/fs/walk_test.ts deleted file mode 100644 index 5eb6ebd693..0000000000 --- a/std/fs/walk_test.ts +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { walk, WalkEntry, WalkOptions, walkSync } from "./walk.ts"; -import { assert, assertEquals, assertThrowsAsync } from "../testing/asserts.ts"; - -export function testWalk( - setup: (arg0: string) => void | Promise, - t: () => void | Promise, - ignore = false, -): void { - const name = t.name; - async function fn(): Promise { - const origCwd = Deno.cwd(); - const d = await Deno.makeTempDir(); - Deno.chdir(d); - try { - await setup(d); - await t(); - } finally { - Deno.chdir(origCwd); - await Deno.remove(d, { recursive: true }); - } - } - Deno.test({ ignore, name: `[walk] ${name}`, fn }); -} - -function normalize({ path }: WalkEntry): string { - return path.replace(/\\/g, "/"); -} - -export async function walkArray( - root: string, - options: WalkOptions = {}, -): Promise { - const arr: string[] = []; - for await (const w of walk(root, { ...options })) { - arr.push(normalize(w)); - } - arr.sort(); // TODO(ry) Remove sort. The order should be deterministic. - const arrSync = Array.from(walkSync(root, options), normalize); - arrSync.sort(); // TODO(ry) Remove sort. The order should be deterministic. - assertEquals(arr, arrSync); - return arr; -} - -export async function touch(path: string): Promise { - const f = await Deno.create(path); - f.close(); -} - -function assertReady(expectedLength: number): void { - const arr = Array.from(walkSync("."), normalize); - - assertEquals(arr.length, expectedLength); -} - -testWalk( - async (d: string): Promise => { - await Deno.mkdir(d + "/empty"); - }, - async function emptyDir(): Promise { - const arr = await walkArray("."); - assertEquals(arr, [".", "empty"]); - }, -); - -testWalk( - async (d: string): Promise => { - await touch(d + "/x"); - }, - async function singleFile(): Promise { - const arr = await walkArray("."); - assertEquals(arr, [".", "x"]); - }, -); - -testWalk( - async (d: string): Promise => { - await touch(d + "/x"); - }, - async function iteratable(): Promise { - let count = 0; - for (const _ of walkSync(".")) { - count += 1; - } - assertEquals(count, 2); - for await (const _ of walk(".")) { - count += 1; - } - assertEquals(count, 4); - }, -); - -testWalk( - async (d: string): Promise => { - await Deno.mkdir(d + "/a"); - await touch(d + "/a/x"); - }, - async function nestedSingleFile(): Promise { - const arr = await walkArray("."); - assertEquals(arr, [".", "a", "a/x"]); - }, -); - -testWalk( - async (d: string): Promise => { - await Deno.mkdir(d + "/a/b/c/d", { recursive: true }); - await touch(d + "/a/b/c/d/x"); - }, - async function depth(): Promise { - assertReady(6); - const arr3 = await walkArray(".", { maxDepth: 3 }); - assertEquals(arr3, [".", "a", "a/b", "a/b/c"]); - const arr5 = await walkArray(".", { maxDepth: 5 }); - assertEquals(arr5, [".", "a", "a/b", "a/b/c", "a/b/c/d", "a/b/c/d/x"]); - }, -); - -testWalk( - async (d: string): Promise => { - await touch(d + "/a"); - await Deno.mkdir(d + "/b"); - await touch(d + "/b/c"); - }, - async function includeDirs(): Promise { - assertReady(4); - const arr = await walkArray(".", { includeDirs: false }); - assertEquals(arr, ["a", "b/c"]); - }, -); - -testWalk( - async (d: string): Promise => { - await touch(d + "/a"); - await Deno.mkdir(d + "/b"); - await touch(d + "/b/c"); - }, - async function includeFiles(): Promise { - assertReady(4); - const arr = await walkArray(".", { includeFiles: false }); - assertEquals(arr, [".", "b"]); - }, -); - -testWalk( - async (d: string): Promise => { - await touch(d + "/x.ts"); - await touch(d + "/y.rs"); - }, - async function ext(): Promise { - assertReady(3); - const arr = await walkArray(".", { exts: [".ts"] }); - assertEquals(arr, ["x.ts"]); - }, -); - -testWalk( - async (d: string): Promise => { - await touch(d + "/x.ts"); - await touch(d + "/y.rs"); - await touch(d + "/z.py"); - }, - async function extAny(): Promise { - assertReady(4); - const arr = await walkArray(".", { exts: [".rs", ".ts"] }); - assertEquals(arr, ["x.ts", "y.rs"]); - }, -); - -testWalk( - async (d: string): Promise => { - await touch(d + "/x"); - await touch(d + "/y"); - }, - async function match(): Promise { - assertReady(3); - const arr = await walkArray(".", { match: [/x/] }); - assertEquals(arr, ["x"]); - }, -); - -testWalk( - async (d: string): Promise => { - await touch(d + "/x"); - await touch(d + "/y"); - await touch(d + "/z"); - }, - async function matchAny(): Promise { - assertReady(4); - const arr = await walkArray(".", { match: [/x/, /y/] }); - assertEquals(arr, ["x", "y"]); - }, -); - -testWalk( - async (d: string): Promise => { - await touch(d + "/x"); - await touch(d + "/y"); - }, - async function skip(): Promise { - assertReady(3); - const arr = await walkArray(".", { skip: [/x/] }); - assertEquals(arr, [".", "y"]); - }, -); - -testWalk( - async (d: string): Promise => { - await touch(d + "/x"); - await touch(d + "/y"); - await touch(d + "/z"); - }, - async function skipAny(): Promise { - assertReady(4); - const arr = await walkArray(".", { skip: [/x/, /y/] }); - assertEquals(arr, [".", "z"]); - }, -); - -testWalk( - async (d: string): Promise => { - await Deno.mkdir(d + "/a"); - await Deno.mkdir(d + "/b"); - await touch(d + "/a/x"); - await touch(d + "/a/y"); - await touch(d + "/b/z"); - }, - async function subDir(): Promise { - assertReady(6); - const arr = await walkArray("b"); - assertEquals(arr, ["b", "b/z"]); - }, -); - -testWalk( - async (_d: string): Promise => {}, - async function nonexistentRoot(): Promise { - await assertThrowsAsync(async () => { - await walkArray("nonexistent"); - }, Deno.errors.NotFound); - }, -); - -testWalk( - async (d: string): Promise => { - await Deno.mkdir(d + "/a"); - await Deno.mkdir(d + "/b"); - await touch(d + "/a/x"); - await touch(d + "/a/y"); - await touch(d + "/b/z"); - await Deno.symlink(d + "/b", d + "/a/bb"); - }, - async function symlink(): Promise { - assertReady(6); - const files = await walkArray("a"); - assertEquals(files.length, 3); - assert(!files.includes("a/bb/z")); - - const arr = await walkArray("a", { followSymlinks: true }); - assertEquals(arr.length, 5); - assert(arr.some((f): boolean => f.endsWith("/b/z"))); - }, -); diff --git a/std/hash/README.md b/std/hash/README.md deleted file mode 100644 index ec5d068e98..0000000000 --- a/std/hash/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# std/hash - -## Usage - -### Creating new hash instance - -You can create a new Hasher instance by calling `createHash` defined in mod.ts. - -```ts -import { createHash } from "https://deno.land/std@$STD_VERSION/hash/mod.ts"; - -const hash = createHash("md5"); -// ... -``` - -### Using hash instance - -You can use `update` method to feed data into your hash instance. Call `digest` -method to retrive final hash value in ArrayBuffer. - -```ts -import { createHash } from "https://deno.land/std@$STD_VERSION/hash/mod.ts"; - -const hash = createHash("md5"); -hash.update("Your data here"); -const final = hash.digest(); // returns ArrayBuffer. -``` - -Please note that `digest` invalidates the hash instance's internal state. -Calling `digest` more than once will throw an Error. - -```ts -import { createHash } from "https://deno.land/std@$STD_VERSION/hash/mod.ts"; - -const hash = createHash("md5"); -hash.update("Your data here"); -const final1 = hash.digest(); // returns ArrayBuffer. -const final2 = hash.digest(); // throws Error. -``` - -If you need final hash in string formats, call `toString` method with output -format. - -Supported formats are `hex` and `base64` and default format is `hex`. - -```ts -import { createHash } from "https://deno.land/std@$STD_VERSION/hash/mod.ts"; - -const hash = createHash("md5"); -hash.update("Your data here"); -const hashInHex = hash.toString(); // returns 5fe084ee423ff7e0c7709e9437cee89d -``` - -```ts -import { createHash } from "https://deno.land/std@$STD_VERSION/hash/mod.ts"; - -const hash = createHash("md5"); -hash.update("Your data here"); -const hashInBase64 = hash.toString("base64"); // returns X+CE7kI/9+DHcJ6UN87onQ== -``` - -### Supported algorithms - -Following algorithms are supported. - -- md2 -- md4 -- md5 -- ripemd160 -- ripemd320 -- sha1 -- sha224 -- sha256 -- sha384 -- sha512 -- sha3-224 -- sha3-256 -- sha3-384 -- sha3-512 -- keccak224 -- keccak256 -- keccak384 -- keccak512 diff --git a/std/hash/_fnv/fnv32.ts b/std/hash/_fnv/fnv32.ts deleted file mode 100644 index 679d44a62b..0000000000 --- a/std/hash/_fnv/fnv32.ts +++ /dev/null @@ -1,79 +0,0 @@ -// Ported from Go: -// https://github.com/golang/go/tree/go1.13.10/src/hash/fnv/fnv.go -// Copyright 2011 The Go Authors. All rights reserved. BSD license. -// https://github.com/golang/go/blob/master/LICENSE -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { mul32 } from "./util.ts"; - -const offset32 = 2166136261; -const prime32 = 16777619; - -abstract class Fnv32Base { - #state: number; - - constructor() { - this.#state = offset32; - } - - protected _updateState(newState: number): void { - this.#state = newState; - } - - reset(): void { - this.#state = offset32; - } - - abstract write(data: Uint8Array): T; - - size(): number { - return 4; - } - - blockSize(): number { - return 1; - } - - sum32(): number { - return this.#state; - } - - sum(): Uint8Array { - return Uint8Array.from([ - (this.#state >> 24) & 0xff, - (this.#state >> 16) & 0xff, - (this.#state >> 8) & 0xff, - this.#state & 0xff, - ]); - } -} - -/** Fnv32 hash */ -export class Fnv32 extends Fnv32Base { - write(data: Uint8Array): Fnv32 { - let hash = this.sum32(); - - data.forEach((c) => { - hash = mul32(hash, prime32); - hash ^= c; - }); - - this._updateState(hash); - return this; - } -} - -/** Fnv32a hash */ -export class Fnv32a extends Fnv32Base { - write(data: Uint8Array): Fnv32a { - let hash = this.sum32(); - - data.forEach((c) => { - hash ^= c; - hash = mul32(hash, prime32); - }); - - this._updateState(hash); - return this; - } -} diff --git a/std/hash/_fnv/fnv64.ts b/std/hash/_fnv/fnv64.ts deleted file mode 100644 index 882636d0e3..0000000000 --- a/std/hash/_fnv/fnv64.ts +++ /dev/null @@ -1,89 +0,0 @@ -// Ported from Go: -// https://github.com/golang/go/tree/go1.13.10/src/hash/fnv/fnv.go -// Copyright 2011 The Go Authors. All rights reserved. BSD license. -// https://github.com/golang/go/blob/master/LICENSE -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { mul64 } from "./util.ts"; - -const offset64Lo = 2216829733; -const offset64Hi = 3421674724; -const prime64Lo = 435; -const prime64Hi = 256; - -abstract class Fnv64Base { - #stateHi: number; - #stateLo: number; - - constructor() { - this.#stateHi = offset64Hi; - this.#stateLo = offset64Lo; - } - - protected _updateState([newStateHi, newStateLo]: [number, number]): void { - this.#stateHi = newStateHi; - this.#stateLo = newStateLo; - } - - reset(): void { - this.#stateHi = offset64Hi; - this.#stateLo = offset64Lo; - } - - abstract write(data: Uint8Array): T; - - size(): number { - return 8; - } - - blockSize(): number { - return 1; - } - - sum64(): [number, number] { - return [this.#stateHi, this.#stateLo]; - } - - sum(): Uint8Array { - return Uint8Array.from([ - (this.#stateHi >> 24) & 0xff, - (this.#stateHi >> 16) & 0xff, - (this.#stateHi >> 8) & 0xff, - this.#stateHi & 0xff, - (this.#stateLo >> 24) & 0xff, - (this.#stateLo >> 16) & 0xff, - (this.#stateLo >> 8) & 0xff, - this.#stateLo & 0xff, - ]); - } -} - -/** Fnv64 hash */ -export class Fnv64 extends Fnv64Base { - write(data: Uint8Array): Fnv64 { - let [hashHi, hashLo] = this.sum64(); - - data.forEach((c) => { - [hashHi, hashLo] = mul64([hashHi, hashLo], [prime64Hi, prime64Lo]); - hashLo ^= c; - }); - - this._updateState([hashHi, hashLo]); - return this; - } -} - -/** Fnv64a hash */ -export class Fnv64a extends Fnv64Base { - write(data: Uint8Array): Fnv64 { - let [hashHi, hashLo] = this.sum64(); - - data.forEach((c) => { - hashLo ^= c; - [hashHi, hashLo] = mul64([hashHi, hashLo], [prime64Hi, prime64Lo]); - }); - - this._updateState([hashHi, hashLo]); - return this; - } -} diff --git a/std/hash/_fnv/util.ts b/std/hash/_fnv/util.ts deleted file mode 100644 index 8afed8e821..0000000000 --- a/std/hash/_fnv/util.ts +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -function n16(n: number): number { - return n & 0xffff; -} - -function n32(n: number): number { - return n >>> 0; -} - -function add32WithCarry(a: number, b: number): [number, number] { - const added = n32(a) + n32(b); - return [n32(added), added > 0xffffffff ? 1 : 0]; -} - -function mul32WithCarry(a: number, b: number): [number, number] { - const al = n16(a); - const ah = n16(a >>> 16); - const bl = n16(b); - const bh = n16(b >>> 16); - - const [t, tc] = add32WithCarry(al * bh, ah * bl); - const [n, nc] = add32WithCarry(al * bl, n32(t << 16)); - const carry = nc + (tc << 16) + n16(t >>> 16) + ah * bh; - - return [n, carry]; -} - -/** - * mul32 performs 32-bit multiplication, a * b - * @param a - * @param b - */ -export function mul32(a: number, b: number): number { - // https://stackoverflow.com/a/28151933 - const al = n16(a); - const ah = a - al; - return n32(n32(ah * b) + al * b); -} - -/** - * mul64 performs 64-bit multiplication with two 32-bit words - * @param [ah, al] - * @param [bh, bl] - */ -export function mul64( - [ah, al]: [number, number], - [bh, bl]: [number, number], -): [number, number] { - const [n, c] = mul32WithCarry(al, bl); - return [n32(mul32(al, bh) + mul32(ah, bl) + c), n]; -} diff --git a/std/hash/_fnv/util_test.ts b/std/hash/_fnv/util_test.ts deleted file mode 100644 index fb9dd32087..0000000000 --- a/std/hash/_fnv/util_test.ts +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../../testing/asserts.ts"; -import { mul32, mul64 } from "./util.ts"; - -Deno.test("[hash/fnv/util] mul32", () => { - assertEquals(mul32(0xffffffff, 0xffffffff), 1); - assertEquals(mul32(0x12345678, 0xdeadbeef), 0x5621ca08); - assertEquals(mul32(0xf626f430, 0xff7469f1), 0x2a939130); - assertEquals(mul32(0x543f9412, 0x8a4aa84f), 0x39fe818e); - assertEquals(mul32(0x8ee170d1, 0x2fbbb9ec), 0x6a0609ac); - assertEquals(mul32(0xea3b3a14, 0xa397bd0a), 0xddfd08c8); - assertEquals(mul32(0x93f8536b, 0xa79e3c04), 0xcc7861ac); - assertEquals(mul32(0xf97dab98, 0xed526241), 0x2348c198); - assertEquals(mul32(0x35500191, 0xd5012447), 0xaff9d337); - assertEquals(mul32(0x471dde47, 0xaaa4950c), 0x4341be54); - assertEquals(mul32(0xd633970d, 0xa9bc2bcd), 0xb43b2469); - assertEquals(mul32(0xc60898cc, 0xbfe7dcc4), 0x15f84c30); -}); - -Deno.test("[hash/fnv/util] mul64", () => { - assertEquals(mul64([0xffffffff, 0xffffffff], [0xffffffff, 0xffffffff]), [ - 0, - 1, - ]); - assertEquals(mul64([0x12345678, 0xdeadbeef], [0xcafebabe, 0xbaadf00d]), [ - 0xc801c86b, - 0xdf55c223, - ]); - assertEquals(mul64([0xdc479aed, 0x24bc71a3], [0x543717c1, 0x4b6056b9]), [ - 0x56c7ec8f, - 0x387ae0cb, - ]); - assertEquals(mul64([0xb84936ae, 0xb84becd2], [0x2864edd1, 0x14ee13cc]), [ - 0xd87e9171, - 0x12504d58, - ]); - assertEquals(mul64([0xb0b73e95, 0x3f5cc701], [0x6c7b30b8, 0xcd7f0f9e]), [ - 0x570551ee, - 0x116ae19e, - ]); - assertEquals(mul64([0xc237b433, 0x160b50bf], [0x3f937c23, 0xf26175f7]), [ - 0x48a1d118, - 0x97313349, - ]); - assertEquals(mul64([0x386242fd, 0x6baa0fc0], [0xf81f7e23, 0xbe172381]), [ - 0x4799f2a3, - 0x6b192fc0, - ]); - assertEquals(mul64([0x5afc8714, 0x902180d1], [0xa7068c96, 0xb859bb4d]), [ - 0xb4589d29, - 0xd3d569dd, - ]); - assertEquals(mul64([0xb4e86a68, 0x619bee92], [0xd67560fa, 0x736982a7]), [ - 0x72c73b5d, - 0x4bc0c53e, - ]); - assertEquals(mul64([0xfc8b5561, 0xbf91d6d5], [0x2bcb029a, 0xa144ead3]), [ - 0x2da439a7, - 0x3926c38f, - ]); - assertEquals(mul64([0x47b62fae, 0xffe8cb4c], [0xbda77111, 0x6cad4968]), [ - 0x9d9b7832, - 0xcae742e0, - ]); - assertEquals(mul64([0xc9160fc1, 0xd96e085b], [0x3adfd031, 0x3f75e557]), [ - 0xe4d0bf23, - 0x88753ded, - ]); -}); diff --git a/std/hash/_sha3/keccak.ts b/std/hash/_sha3/keccak.ts deleted file mode 100644 index 403a2dcb03..0000000000 --- a/std/hash/_sha3/keccak.ts +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Sponge } from "./sponge.ts"; -import { keccakf } from "./keccakf.ts"; - -/** Keccak-224 hash */ -export class Keccak224 extends Sponge { - constructor() { - super({ - bitsize: 224, - rate: 144, - dsbyte: 1, - permutator: keccakf, - }); - } -} - -/** Keccak-256 hash */ -export class Keccak256 extends Sponge { - constructor() { - super({ - bitsize: 256, - rate: 136, - dsbyte: 1, - permutator: keccakf, - }); - } -} - -/** Keccak-384 hash */ -export class Keccak384 extends Sponge { - constructor() { - super({ - bitsize: 384, - rate: 104, - dsbyte: 1, - permutator: keccakf, - }); - } -} - -/** Keccak-512 hash */ -export class Keccak512 extends Sponge { - constructor() { - super({ - bitsize: 512, - rate: 72, - dsbyte: 1, - permutator: keccakf, - }); - } -} diff --git a/std/hash/_sha3/keccakf.ts b/std/hash/_sha3/keccakf.ts deleted file mode 100644 index 0ba2b870c4..0000000000 --- a/std/hash/_sha3/keccakf.ts +++ /dev/null @@ -1,790 +0,0 @@ -// Ported from Go: -// https://github.com/golang/crypto/blob/master/sha3/keccakf.go -// Copyright 2011 The Go Authors. All rights reserved. BSD license. -// https://github.com/golang/go/blob/master/LICENSE -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -const KECCAK_ROUNDS = 24; -const KECCAK_RC: number[] = [ - 0x1, - 0x0, - 0x8082, - 0x0, - 0x808a, - 0x80000000, - 0x80008000, - 0x80000000, - 0x808b, - 0x0, - 0x80000001, - 0x0, - 0x80008081, - 0x80000000, - 0x8009, - 0x80000000, - 0x8a, - 0x0, - 0x88, - 0x0, - 0x80008009, - 0x0, - 0x8000000a, - 0x0, - 0x8000808b, - 0x0, - 0x8b, - 0x80000000, - 0x8089, - 0x80000000, - 0x8003, - 0x80000000, - 0x8002, - 0x80000000, - 0x80, - 0x80000000, - 0x800a, - 0x0, - 0x8000000a, - 0x80000000, - 0x80008081, - 0x80000000, - 0x8080, - 0x80000000, - 0x80000001, - 0x0, - 0x80008008, - 0x80000000, -]; - -/** keccak1600 permutation function */ -export function keccakf(state: Uint8Array): void { - const s = new Uint32Array(state.buffer); - let bc0 = 0; - let bc1 = 0; - let bc2 = 0; - let bc3 = 0; - let bc4 = 0; - let bc5 = 0; - let bc6 = 0; - let bc7 = 0; - let bc8 = 0; - let bc9 = 0; - let d0 = 0; - let d1 = 0; - let d2 = 0; - let d3 = 0; - let d4 = 0; - let d5 = 0; - let d6 = 0; - let d7 = 0; - let d8 = 0; - let d9 = 0; - let t0 = 0; - let t1 = 0; - - for (let n = 0; n < KECCAK_ROUNDS * 2; n += 8) { - // Round 1 - bc0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40]; - bc1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41]; - bc2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42]; - bc3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43]; - bc4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44]; - bc5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45]; - bc6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46]; - bc7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47]; - bc8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48]; - bc9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49]; - - d0 = bc8 ^ ((bc2 << 1) | (bc3 >>> 31)); - d1 = bc9 ^ ((bc3 << 1) | (bc2 >>> 31)); - d2 = bc0 ^ ((bc4 << 1) | (bc5 >>> 31)); - d3 = bc1 ^ ((bc5 << 1) | (bc4 >>> 31)); - d4 = bc2 ^ ((bc6 << 1) | (bc7 >>> 31)); - d5 = bc3 ^ ((bc7 << 1) | (bc6 >>> 31)); - d6 = bc4 ^ ((bc8 << 1) | (bc9 >>> 31)); - d7 = bc5 ^ ((bc9 << 1) | (bc8 >>> 31)); - d8 = bc6 ^ ((bc0 << 1) | (bc1 >>> 31)); - d9 = bc7 ^ ((bc1 << 1) | (bc0 >>> 31)); - - bc0 = s[0] ^ d0; - bc1 = s[1] ^ d1; - t0 = s[12] ^ d2; - t1 = s[13] ^ d3; - bc2 = (t1 << 12) | (t0 >>> 20); - bc3 = (t0 << 12) | (t1 >>> 20); - t0 = s[24] ^ d4; - t1 = s[25] ^ d5; - bc4 = (t1 << 11) | (t0 >>> 21); - bc5 = (t0 << 11) | (t1 >>> 21); - t0 = s[36] ^ d6; - t1 = s[37] ^ d7; - bc6 = (t0 << 21) | (t1 >>> 11); - bc7 = (t1 << 21) | (t0 >>> 11); - t0 = s[48] ^ d8; - t1 = s[49] ^ d9; - bc8 = (t0 << 14) | (t1 >>> 18); - bc9 = (t1 << 14) | (t0 >>> 18); - s[0] = bc0 ^ (bc4 & ~bc2) ^ KECCAK_RC[n]; - s[1] = bc1 ^ (bc5 & ~bc3) ^ KECCAK_RC[n + 1]; - s[12] = bc2 ^ (bc6 & ~bc4); - s[13] = bc3 ^ (bc7 & ~bc5); - s[24] = bc4 ^ (bc8 & ~bc6); - s[25] = bc5 ^ (bc9 & ~bc7); - s[36] = bc6 ^ (bc0 & ~bc8); - s[37] = bc7 ^ (bc1 & ~bc9); - s[48] = bc8 ^ (bc2 & ~bc0); - s[49] = bc9 ^ (bc3 & ~bc1); - - t0 = s[20] ^ d0; - t1 = s[21] ^ d1; - bc4 = (t0 << 3) | (t1 >>> 29); - bc5 = (t1 << 3) | (t0 >>> 29); - t0 = s[32] ^ d2; - t1 = s[33] ^ d3; - bc6 = (t1 << 13) | (t0 >>> 19); - bc7 = (t0 << 13) | (t1 >>> 19); - t0 = s[44] ^ d4; - t1 = s[45] ^ d5; - bc8 = (t1 << 29) | (t0 >>> 3); - bc9 = (t0 << 29) | (t1 >>> 3); - t0 = s[6] ^ d6; - t1 = s[7] ^ d7; - bc0 = (t0 << 28) | (t1 >>> 4); - bc1 = (t1 << 28) | (t0 >>> 4); - t0 = s[18] ^ d8; - t1 = s[19] ^ d9; - bc2 = (t0 << 20) | (t1 >>> 12); - bc3 = (t1 << 20) | (t0 >>> 12); - s[20] = bc0 ^ (bc4 & ~bc2); - s[21] = bc1 ^ (bc5 & ~bc3); - s[32] = bc2 ^ (bc6 & ~bc4); - s[33] = bc3 ^ (bc7 & ~bc5); - s[44] = bc4 ^ (bc8 & ~bc6); - s[45] = bc5 ^ (bc9 & ~bc7); - s[6] = bc6 ^ (bc0 & ~bc8); - s[7] = bc7 ^ (bc1 & ~bc9); - s[18] = bc8 ^ (bc2 & ~bc0); - s[19] = bc9 ^ (bc3 & ~bc1); - - t0 = s[40] ^ d0; - t1 = s[41] ^ d1; - bc8 = (t0 << 18) | (t1 >>> 14); - bc9 = (t1 << 18) | (t0 >>> 14); - t0 = s[2] ^ d2; - t1 = s[3] ^ d3; - bc0 = (t0 << 1) | (t1 >>> 31); - bc1 = (t1 << 1) | (t0 >>> 31); - t0 = s[14] ^ d4; - t1 = s[15] ^ d5; - bc2 = (t0 << 6) | (t1 >>> 26); - bc3 = (t1 << 6) | (t0 >>> 26); - t0 = s[26] ^ d6; - t1 = s[27] ^ d7; - bc4 = (t0 << 25) | (t1 >>> 7); - bc5 = (t1 << 25) | (t0 >>> 7); - t0 = s[38] ^ d8; - t1 = s[39] ^ d9; - bc6 = (t0 << 8) | (t1 >>> 24); - bc7 = (t1 << 8) | (t0 >>> 24); - s[40] = bc0 ^ (bc4 & ~bc2); - s[41] = bc1 ^ (bc5 & ~bc3); - s[2] = bc2 ^ (bc6 & ~bc4); - s[3] = bc3 ^ (bc7 & ~bc5); - s[14] = bc4 ^ (bc8 & ~bc6); - s[15] = bc5 ^ (bc9 & ~bc7); - s[26] = bc6 ^ (bc0 & ~bc8); - s[27] = bc7 ^ (bc1 & ~bc9); - s[38] = bc8 ^ (bc2 & ~bc0); - s[39] = bc9 ^ (bc3 & ~bc1); - - t0 = s[10] ^ d0; - t1 = s[11] ^ d1; - bc2 = (t1 << 4) | (t0 >>> 28); - bc3 = (t0 << 4) | (t1 >>> 28); - t0 = s[22] ^ d2; - t1 = s[23] ^ d3; - bc4 = (t0 << 10) | (t1 >>> 22); - bc5 = (t1 << 10) | (t0 >>> 22); - t0 = s[34] ^ d4; - t1 = s[35] ^ d5; - bc6 = (t0 << 15) | (t1 >>> 17); - bc7 = (t1 << 15) | (t0 >>> 17); - t0 = s[46] ^ d6; - t1 = s[47] ^ d7; - bc8 = (t1 << 24) | (t0 >>> 8); - bc9 = (t0 << 24) | (t1 >>> 8); - t0 = s[8] ^ d8; - t1 = s[9] ^ d9; - bc0 = (t0 << 27) | (t1 >>> 5); - bc1 = (t1 << 27) | (t0 >>> 5); - s[10] = bc0 ^ (bc4 & ~bc2); - s[11] = bc1 ^ (bc5 & ~bc3); - s[22] = bc2 ^ (bc6 & ~bc4); - s[23] = bc3 ^ (bc7 & ~bc5); - s[34] = bc4 ^ (bc8 & ~bc6); - s[35] = bc5 ^ (bc9 & ~bc7); - s[46] = bc6 ^ (bc0 & ~bc8); - s[47] = bc7 ^ (bc1 & ~bc9); - s[8] = bc8 ^ (bc2 & ~bc0); - s[9] = bc9 ^ (bc3 & ~bc1); - - t0 = s[30] ^ d0; - t1 = s[31] ^ d1; - bc6 = (t1 << 9) | (t0 >>> 23); - bc7 = (t0 << 9) | (t1 >>> 23); - t0 = s[42] ^ d2; - t1 = s[43] ^ d3; - bc8 = (t0 << 2) | (t1 >>> 30); - bc9 = (t1 << 2) | (t0 >>> 30); - t0 = s[4] ^ d4; - t1 = s[5] ^ d5; - bc0 = (t1 << 30) | (t0 >>> 2); - bc1 = (t0 << 30) | (t1 >>> 2); - t0 = s[16] ^ d6; - t1 = s[17] ^ d7; - bc2 = (t1 << 23) | (t0 >>> 9); - bc3 = (t0 << 23) | (t1 >>> 9); - t0 = s[28] ^ d8; - t1 = s[29] ^ d9; - bc4 = (t1 << 7) | (t0 >>> 25); - bc5 = (t0 << 7) | (t1 >>> 25); - s[30] = bc0 ^ (bc4 & ~bc2); - s[31] = bc1 ^ (bc5 & ~bc3); - s[42] = bc2 ^ (bc6 & ~bc4); - s[43] = bc3 ^ (bc7 & ~bc5); - s[4] = bc4 ^ (bc8 & ~bc6); - s[5] = bc5 ^ (bc9 & ~bc7); - s[16] = bc6 ^ (bc0 & ~bc8); - s[17] = bc7 ^ (bc1 & ~bc9); - s[28] = bc8 ^ (bc2 & ~bc0); - s[29] = bc9 ^ (bc3 & ~bc1); - - // Round 2 - bc0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40]; - bc1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41]; - bc2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42]; - bc3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43]; - bc4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44]; - bc5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45]; - bc6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46]; - bc7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47]; - bc8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48]; - bc9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49]; - - d0 = bc8 ^ ((bc2 << 1) | (bc3 >>> 31)); - d1 = bc9 ^ ((bc3 << 1) | (bc2 >>> 31)); - d2 = bc0 ^ ((bc4 << 1) | (bc5 >>> 31)); - d3 = bc1 ^ ((bc5 << 1) | (bc4 >>> 31)); - d4 = bc2 ^ ((bc6 << 1) | (bc7 >>> 31)); - d5 = bc3 ^ ((bc7 << 1) | (bc6 >>> 31)); - d6 = bc4 ^ ((bc8 << 1) | (bc9 >>> 31)); - d7 = bc5 ^ ((bc9 << 1) | (bc8 >>> 31)); - d8 = bc6 ^ ((bc0 << 1) | (bc1 >>> 31)); - d9 = bc7 ^ ((bc1 << 1) | (bc0 >>> 31)); - - bc0 = s[0] ^ d0; - bc1 = s[1] ^ d1; - t0 = s[32] ^ d2; - t1 = s[33] ^ d3; - bc2 = (t1 << 12) | (t0 >>> 20); - bc3 = (t0 << 12) | (t1 >>> 20); - t0 = s[14] ^ d4; - t1 = s[15] ^ d5; - bc4 = (t1 << 11) | (t0 >>> 21); - bc5 = (t0 << 11) | (t1 >>> 21); - t0 = s[46] ^ d6; - t1 = s[47] ^ d7; - bc6 = (t0 << 21) | (t1 >>> 11); - bc7 = (t1 << 21) | (t0 >>> 11); - t0 = s[28] ^ d8; - t1 = s[29] ^ d9; - bc8 = (t0 << 14) | (t1 >>> 18); - bc9 = (t1 << 14) | (t0 >>> 18); - s[0] = bc0 ^ (bc4 & ~bc2) ^ KECCAK_RC[n + 2]; - s[1] = bc1 ^ (bc5 & ~bc3) ^ KECCAK_RC[n + 3]; - s[32] = bc2 ^ (bc6 & ~bc4); - s[33] = bc3 ^ (bc7 & ~bc5); - s[14] = bc4 ^ (bc8 & ~bc6); - s[15] = bc5 ^ (bc9 & ~bc7); - s[46] = bc6 ^ (bc0 & ~bc8); - s[47] = bc7 ^ (bc1 & ~bc9); - s[28] = bc8 ^ (bc2 & ~bc0); - s[29] = bc9 ^ (bc3 & ~bc1); - - t0 = s[40] ^ d0; - t1 = s[41] ^ d1; - bc4 = (t0 << 3) | (t1 >>> 29); - bc5 = (t1 << 3) | (t0 >>> 29); - t0 = s[22] ^ d2; - t1 = s[23] ^ d3; - bc6 = (t1 << 13) | (t0 >>> 19); - bc7 = (t0 << 13) | (t1 >>> 19); - t0 = s[4] ^ d4; - t1 = s[5] ^ d5; - bc8 = (t1 << 29) | (t0 >>> 3); - bc9 = (t0 << 29) | (t1 >>> 3); - t0 = s[36] ^ d6; - t1 = s[37] ^ d7; - bc0 = (t0 << 28) | (t1 >>> 4); - bc1 = (t1 << 28) | (t0 >>> 4); - t0 = s[18] ^ d8; - t1 = s[19] ^ d9; - bc2 = (t0 << 20) | (t1 >>> 12); - bc3 = (t1 << 20) | (t0 >>> 12); - s[40] = bc0 ^ (bc4 & ~bc2); - s[41] = bc1 ^ (bc5 & ~bc3); - s[22] = bc2 ^ (bc6 & ~bc4); - s[23] = bc3 ^ (bc7 & ~bc5); - s[4] = bc4 ^ (bc8 & ~bc6); - s[5] = bc5 ^ (bc9 & ~bc7); - s[36] = bc6 ^ (bc0 & ~bc8); - s[37] = bc7 ^ (bc1 & ~bc9); - s[18] = bc8 ^ (bc2 & ~bc0); - s[19] = bc9 ^ (bc3 & ~bc1); - - t0 = s[30] ^ d0; - t1 = s[31] ^ d1; - bc8 = (t0 << 18) | (t1 >>> 14); - bc9 = (t1 << 18) | (t0 >>> 14); - t0 = s[12] ^ d2; - t1 = s[13] ^ d3; - bc0 = (t0 << 1) | (t1 >>> 31); - bc1 = (t1 << 1) | (t0 >>> 31); - t0 = s[44] ^ d4; - t1 = s[45] ^ d5; - bc2 = (t0 << 6) | (t1 >>> 26); - bc3 = (t1 << 6) | (t0 >>> 26); - t0 = s[26] ^ d6; - t1 = s[27] ^ d7; - bc4 = (t0 << 25) | (t1 >>> 7); - bc5 = (t1 << 25) | (t0 >>> 7); - t0 = s[8] ^ d8; - t1 = s[9] ^ d9; - bc6 = (t0 << 8) | (t1 >>> 24); - bc7 = (t1 << 8) | (t0 >>> 24); - s[30] = bc0 ^ (bc4 & ~bc2); - s[31] = bc1 ^ (bc5 & ~bc3); - s[12] = bc2 ^ (bc6 & ~bc4); - s[13] = bc3 ^ (bc7 & ~bc5); - s[44] = bc4 ^ (bc8 & ~bc6); - s[45] = bc5 ^ (bc9 & ~bc7); - s[26] = bc6 ^ (bc0 & ~bc8); - s[27] = bc7 ^ (bc1 & ~bc9); - s[8] = bc8 ^ (bc2 & ~bc0); - s[9] = bc9 ^ (bc3 & ~bc1); - - t0 = s[20] ^ d0; - t1 = s[21] ^ d1; - bc2 = (t1 << 4) | (t0 >>> 28); - bc3 = (t0 << 4) | (t1 >>> 28); - t0 = s[2] ^ d2; - t1 = s[3] ^ d3; - bc4 = (t0 << 10) | (t1 >>> 22); - bc5 = (t1 << 10) | (t0 >>> 22); - t0 = s[34] ^ d4; - t1 = s[35] ^ d5; - bc6 = (t0 << 15) | (t1 >>> 17); - bc7 = (t1 << 15) | (t0 >>> 17); - t0 = s[16] ^ d6; - t1 = s[17] ^ d7; - bc8 = (t1 << 24) | (t0 >>> 8); - bc9 = (t0 << 24) | (t1 >>> 8); - t0 = s[48] ^ d8; - t1 = s[49] ^ d9; - bc0 = (t0 << 27) | (t1 >>> 5); - bc1 = (t1 << 27) | (t0 >>> 5); - s[20] = bc0 ^ (bc4 & ~bc2); - s[21] = bc1 ^ (bc5 & ~bc3); - s[2] = bc2 ^ (bc6 & ~bc4); - s[3] = bc3 ^ (bc7 & ~bc5); - s[34] = bc4 ^ (bc8 & ~bc6); - s[35] = bc5 ^ (bc9 & ~bc7); - s[16] = bc6 ^ (bc0 & ~bc8); - s[17] = bc7 ^ (bc1 & ~bc9); - s[48] = bc8 ^ (bc2 & ~bc0); - s[49] = bc9 ^ (bc3 & ~bc1); - - t0 = s[10] ^ d0; - t1 = s[11] ^ d1; - bc6 = (t1 << 9) | (t0 >>> 23); - bc7 = (t0 << 9) | (t1 >>> 23); - t0 = s[42] ^ d2; - t1 = s[43] ^ d3; - bc8 = (t0 << 2) | (t1 >>> 30); - bc9 = (t1 << 2) | (t0 >>> 30); - t0 = s[24] ^ d4; - t1 = s[25] ^ d5; - bc0 = (t1 << 30) | (t0 >>> 2); - bc1 = (t0 << 30) | (t1 >>> 2); - t0 = s[6] ^ d6; - t1 = s[7] ^ d7; - bc2 = (t1 << 23) | (t0 >>> 9); - bc3 = (t0 << 23) | (t1 >>> 9); - t0 = s[38] ^ d8; - t1 = s[39] ^ d9; - bc4 = (t1 << 7) | (t0 >>> 25); - bc5 = (t0 << 7) | (t1 >>> 25); - s[10] = bc0 ^ (bc4 & ~bc2); - s[11] = bc1 ^ (bc5 & ~bc3); - s[42] = bc2 ^ (bc6 & ~bc4); - s[43] = bc3 ^ (bc7 & ~bc5); - s[24] = bc4 ^ (bc8 & ~bc6); - s[25] = bc5 ^ (bc9 & ~bc7); - s[6] = bc6 ^ (bc0 & ~bc8); - s[7] = bc7 ^ (bc1 & ~bc9); - s[38] = bc8 ^ (bc2 & ~bc0); - s[39] = bc9 ^ (bc3 & ~bc1); - - // Round 3 - bc0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40]; - bc1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41]; - bc2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42]; - bc3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43]; - bc4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44]; - bc5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45]; - bc6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46]; - bc7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47]; - bc8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48]; - bc9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49]; - - d0 = bc8 ^ ((bc2 << 1) | (bc3 >>> 31)); - d1 = bc9 ^ ((bc3 << 1) | (bc2 >>> 31)); - d2 = bc0 ^ ((bc4 << 1) | (bc5 >>> 31)); - d3 = bc1 ^ ((bc5 << 1) | (bc4 >>> 31)); - d4 = bc2 ^ ((bc6 << 1) | (bc7 >>> 31)); - d5 = bc3 ^ ((bc7 << 1) | (bc6 >>> 31)); - d6 = bc4 ^ ((bc8 << 1) | (bc9 >>> 31)); - d7 = bc5 ^ ((bc9 << 1) | (bc8 >>> 31)); - d8 = bc6 ^ ((bc0 << 1) | (bc1 >>> 31)); - d9 = bc7 ^ ((bc1 << 1) | (bc0 >>> 31)); - - bc0 = s[0] ^ d0; - bc1 = s[1] ^ d1; - t0 = s[22] ^ d2; - t1 = s[23] ^ d3; - bc2 = (t1 << 12) | (t0 >>> 20); - bc3 = (t0 << 12) | (t1 >>> 20); - t0 = s[44] ^ d4; - t1 = s[45] ^ d5; - bc4 = (t1 << 11) | (t0 >>> 21); - bc5 = (t0 << 11) | (t1 >>> 21); - t0 = s[16] ^ d6; - t1 = s[17] ^ d7; - bc6 = (t0 << 21) | (t1 >>> 11); - bc7 = (t1 << 21) | (t0 >>> 11); - t0 = s[38] ^ d8; - t1 = s[39] ^ d9; - bc8 = (t0 << 14) | (t1 >>> 18); - bc9 = (t1 << 14) | (t0 >>> 18); - s[0] = bc0 ^ (bc4 & ~bc2) ^ KECCAK_RC[n + 4]; - s[1] = bc1 ^ (bc5 & ~bc3) ^ KECCAK_RC[n + 5]; - s[22] = bc2 ^ (bc6 & ~bc4); - s[23] = bc3 ^ (bc7 & ~bc5); - s[44] = bc4 ^ (bc8 & ~bc6); - s[45] = bc5 ^ (bc9 & ~bc7); - s[16] = bc6 ^ (bc0 & ~bc8); - s[17] = bc7 ^ (bc1 & ~bc9); - s[38] = bc8 ^ (bc2 & ~bc0); - s[39] = bc9 ^ (bc3 & ~bc1); - - t0 = s[30] ^ d0; - t1 = s[31] ^ d1; - bc4 = (t0 << 3) | (t1 >>> 29); - bc5 = (t1 << 3) | (t0 >>> 29); - t0 = s[2] ^ d2; - t1 = s[3] ^ d3; - bc6 = (t1 << 13) | (t0 >>> 19); - bc7 = (t0 << 13) | (t1 >>> 19); - t0 = s[24] ^ d4; - t1 = s[25] ^ d5; - bc8 = (t1 << 29) | (t0 >>> 3); - bc9 = (t0 << 29) | (t1 >>> 3); - t0 = s[46] ^ d6; - t1 = s[47] ^ d7; - bc0 = (t0 << 28) | (t1 >>> 4); - bc1 = (t1 << 28) | (t0 >>> 4); - t0 = s[18] ^ d8; - t1 = s[19] ^ d9; - bc2 = (t0 << 20) | (t1 >>> 12); - bc3 = (t1 << 20) | (t0 >>> 12); - s[30] = bc0 ^ (bc4 & ~bc2); - s[31] = bc1 ^ (bc5 & ~bc3); - s[2] = bc2 ^ (bc6 & ~bc4); - s[3] = bc3 ^ (bc7 & ~bc5); - s[24] = bc4 ^ (bc8 & ~bc6); - s[25] = bc5 ^ (bc9 & ~bc7); - s[46] = bc6 ^ (bc0 & ~bc8); - s[47] = bc7 ^ (bc1 & ~bc9); - s[18] = bc8 ^ (bc2 & ~bc0); - s[19] = bc9 ^ (bc3 & ~bc1); - - t0 = s[10] ^ d0; - t1 = s[11] ^ d1; - bc8 = (t0 << 18) | (t1 >>> 14); - bc9 = (t1 << 18) | (t0 >>> 14); - t0 = s[32] ^ d2; - t1 = s[33] ^ d3; - bc0 = (t0 << 1) | (t1 >>> 31); - bc1 = (t1 << 1) | (t0 >>> 31); - t0 = s[4] ^ d4; - t1 = s[5] ^ d5; - bc2 = (t0 << 6) | (t1 >>> 26); - bc3 = (t1 << 6) | (t0 >>> 26); - t0 = s[26] ^ d6; - t1 = s[27] ^ d7; - bc4 = (t0 << 25) | (t1 >>> 7); - bc5 = (t1 << 25) | (t0 >>> 7); - t0 = s[48] ^ d8; - t1 = s[49] ^ d9; - bc6 = (t0 << 8) | (t1 >>> 24); - bc7 = (t1 << 8) | (t0 >>> 24); - s[10] = bc0 ^ (bc4 & ~bc2); - s[11] = bc1 ^ (bc5 & ~bc3); - s[32] = bc2 ^ (bc6 & ~bc4); - s[33] = bc3 ^ (bc7 & ~bc5); - s[4] = bc4 ^ (bc8 & ~bc6); - s[5] = bc5 ^ (bc9 & ~bc7); - s[26] = bc6 ^ (bc0 & ~bc8); - s[27] = bc7 ^ (bc1 & ~bc9); - s[48] = bc8 ^ (bc2 & ~bc0); - s[49] = bc9 ^ (bc3 & ~bc1); - - t0 = s[40] ^ d0; - t1 = s[41] ^ d1; - bc2 = (t1 << 4) | (t0 >>> 28); - bc3 = (t0 << 4) | (t1 >>> 28); - t0 = s[12] ^ d2; - t1 = s[13] ^ d3; - bc4 = (t0 << 10) | (t1 >>> 22); - bc5 = (t1 << 10) | (t0 >>> 22); - t0 = s[34] ^ d4; - t1 = s[35] ^ d5; - bc6 = (t0 << 15) | (t1 >>> 17); - bc7 = (t1 << 15) | (t0 >>> 17); - t0 = s[6] ^ d6; - t1 = s[7] ^ d7; - bc8 = (t1 << 24) | (t0 >>> 8); - bc9 = (t0 << 24) | (t1 >>> 8); - t0 = s[28] ^ d8; - t1 = s[29] ^ d9; - bc0 = (t0 << 27) | (t1 >>> 5); - bc1 = (t1 << 27) | (t0 >>> 5); - s[40] = bc0 ^ (bc4 & ~bc2); - s[41] = bc1 ^ (bc5 & ~bc3); - s[12] = bc2 ^ (bc6 & ~bc4); - s[13] = bc3 ^ (bc7 & ~bc5); - s[34] = bc4 ^ (bc8 & ~bc6); - s[35] = bc5 ^ (bc9 & ~bc7); - s[6] = bc6 ^ (bc0 & ~bc8); - s[7] = bc7 ^ (bc1 & ~bc9); - s[28] = bc8 ^ (bc2 & ~bc0); - s[29] = bc9 ^ (bc3 & ~bc1); - - t0 = s[20] ^ d0; - t1 = s[21] ^ d1; - bc6 = (t1 << 9) | (t0 >>> 23); - bc7 = (t0 << 9) | (t1 >>> 23); - t0 = s[42] ^ d2; - t1 = s[43] ^ d3; - bc8 = (t0 << 2) | (t1 >>> 30); - bc9 = (t1 << 2) | (t0 >>> 30); - t0 = s[14] ^ d4; - t1 = s[15] ^ d5; - bc0 = (t1 << 30) | (t0 >>> 2); - bc1 = (t0 << 30) | (t1 >>> 2); - t0 = s[36] ^ d6; - t1 = s[37] ^ d7; - bc2 = (t1 << 23) | (t0 >>> 9); - bc3 = (t0 << 23) | (t1 >>> 9); - t0 = s[8] ^ d8; - t1 = s[9] ^ d9; - bc4 = (t1 << 7) | (t0 >>> 25); - bc5 = (t0 << 7) | (t1 >>> 25); - s[20] = bc0 ^ (bc4 & ~bc2); - s[21] = bc1 ^ (bc5 & ~bc3); - s[42] = bc2 ^ (bc6 & ~bc4); - s[43] = bc3 ^ (bc7 & ~bc5); - s[14] = bc4 ^ (bc8 & ~bc6); - s[15] = bc5 ^ (bc9 & ~bc7); - s[36] = bc6 ^ (bc0 & ~bc8); - s[37] = bc7 ^ (bc1 & ~bc9); - s[8] = bc8 ^ (bc2 & ~bc0); - s[9] = bc9 ^ (bc3 & ~bc1); - - // Round 4 - bc0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40]; - bc1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41]; - bc2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42]; - bc3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43]; - bc4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44]; - bc5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45]; - bc6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46]; - bc7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47]; - bc8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48]; - bc9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49]; - - d0 = bc8 ^ ((bc2 << 1) | (bc3 >>> 31)); - d1 = bc9 ^ ((bc3 << 1) | (bc2 >>> 31)); - d2 = bc0 ^ ((bc4 << 1) | (bc5 >>> 31)); - d3 = bc1 ^ ((bc5 << 1) | (bc4 >>> 31)); - d4 = bc2 ^ ((bc6 << 1) | (bc7 >>> 31)); - d5 = bc3 ^ ((bc7 << 1) | (bc6 >>> 31)); - d6 = bc4 ^ ((bc8 << 1) | (bc9 >>> 31)); - d7 = bc5 ^ ((bc9 << 1) | (bc8 >>> 31)); - d8 = bc6 ^ ((bc0 << 1) | (bc1 >>> 31)); - d9 = bc7 ^ ((bc1 << 1) | (bc0 >>> 31)); - - bc0 = s[0] ^ d0; - bc1 = s[1] ^ d1; - t0 = s[2] ^ d2; - t1 = s[3] ^ d3; - bc2 = (t1 << 12) | (t0 >>> 20); - bc3 = (t0 << 12) | (t1 >>> 20); - t0 = s[4] ^ d4; - t1 = s[5] ^ d5; - bc4 = (t1 << 11) | (t0 >>> 21); - bc5 = (t0 << 11) | (t1 >>> 21); - t0 = s[6] ^ d6; - t1 = s[7] ^ d7; - bc6 = (t0 << 21) | (t1 >>> 11); - bc7 = (t1 << 21) | (t0 >>> 11); - t0 = s[8] ^ d8; - t1 = s[9] ^ d9; - bc8 = (t0 << 14) | (t1 >>> 18); - bc9 = (t1 << 14) | (t0 >>> 18); - s[0] = bc0 ^ (bc4 & ~bc2) ^ KECCAK_RC[n + 6]; - s[1] = bc1 ^ (bc5 & ~bc3) ^ KECCAK_RC[n + 7]; - s[2] = bc2 ^ (bc6 & ~bc4); - s[3] = bc3 ^ (bc7 & ~bc5); - s[4] = bc4 ^ (bc8 & ~bc6); - s[5] = bc5 ^ (bc9 & ~bc7); - s[6] = bc6 ^ (bc0 & ~bc8); - s[7] = bc7 ^ (bc1 & ~bc9); - s[8] = bc8 ^ (bc2 & ~bc0); - s[9] = bc9 ^ (bc3 & ~bc1); - - t0 = s[10] ^ d0; - t1 = s[11] ^ d1; - bc4 = (t0 << 3) | (t1 >>> 29); - bc5 = (t1 << 3) | (t0 >>> 29); - t0 = s[12] ^ d2; - t1 = s[13] ^ d3; - bc6 = (t1 << 13) | (t0 >>> 19); - bc7 = (t0 << 13) | (t1 >>> 19); - t0 = s[14] ^ d4; - t1 = s[15] ^ d5; - bc8 = (t1 << 29) | (t0 >>> 3); - bc9 = (t0 << 29) | (t1 >>> 3); - t0 = s[16] ^ d6; - t1 = s[17] ^ d7; - bc0 = (t0 << 28) | (t1 >>> 4); - bc1 = (t1 << 28) | (t0 >>> 4); - t0 = s[18] ^ d8; - t1 = s[19] ^ d9; - bc2 = (t0 << 20) | (t1 >>> 12); - bc3 = (t1 << 20) | (t0 >>> 12); - s[10] = bc0 ^ (bc4 & ~bc2); - s[11] = bc1 ^ (bc5 & ~bc3); - s[12] = bc2 ^ (bc6 & ~bc4); - s[13] = bc3 ^ (bc7 & ~bc5); - s[14] = bc4 ^ (bc8 & ~bc6); - s[15] = bc5 ^ (bc9 & ~bc7); - s[16] = bc6 ^ (bc0 & ~bc8); - s[17] = bc7 ^ (bc1 & ~bc9); - s[18] = bc8 ^ (bc2 & ~bc0); - s[19] = bc9 ^ (bc3 & ~bc1); - - t0 = s[20] ^ d0; - t1 = s[21] ^ d1; - bc8 = (t0 << 18) | (t1 >>> 14); - bc9 = (t1 << 18) | (t0 >>> 14); - t0 = s[22] ^ d2; - t1 = s[23] ^ d3; - bc0 = (t0 << 1) | (t1 >>> 31); - bc1 = (t1 << 1) | (t0 >>> 31); - t0 = s[24] ^ d4; - t1 = s[25] ^ d5; - bc2 = (t0 << 6) | (t1 >>> 26); - bc3 = (t1 << 6) | (t0 >>> 26); - t0 = s[26] ^ d6; - t1 = s[27] ^ d7; - bc4 = (t0 << 25) | (t1 >>> 7); - bc5 = (t1 << 25) | (t0 >>> 7); - t0 = s[28] ^ d8; - t1 = s[29] ^ d9; - bc6 = (t0 << 8) | (t1 >>> 24); - bc7 = (t1 << 8) | (t0 >>> 24); - s[20] = bc0 ^ (bc4 & ~bc2); - s[21] = bc1 ^ (bc5 & ~bc3); - s[22] = bc2 ^ (bc6 & ~bc4); - s[23] = bc3 ^ (bc7 & ~bc5); - s[24] = bc4 ^ (bc8 & ~bc6); - s[25] = bc5 ^ (bc9 & ~bc7); - s[26] = bc6 ^ (bc0 & ~bc8); - s[27] = bc7 ^ (bc1 & ~bc9); - s[28] = bc8 ^ (bc2 & ~bc0); - s[29] = bc9 ^ (bc3 & ~bc1); - - t0 = s[30] ^ d0; - t1 = s[31] ^ d1; - bc2 = (t1 << 4) | (t0 >>> 28); - bc3 = (t0 << 4) | (t1 >>> 28); - t0 = s[32] ^ d2; - t1 = s[33] ^ d3; - bc4 = (t0 << 10) | (t1 >>> 22); - bc5 = (t1 << 10) | (t0 >>> 22); - t0 = s[34] ^ d4; - t1 = s[35] ^ d5; - bc6 = (t0 << 15) | (t1 >>> 17); - bc7 = (t1 << 15) | (t0 >>> 17); - t0 = s[36] ^ d6; - t1 = s[37] ^ d7; - bc8 = (t1 << 24) | (t0 >>> 8); - bc9 = (t0 << 24) | (t1 >>> 8); - t0 = s[38] ^ d8; - t1 = s[39] ^ d9; - bc0 = (t0 << 27) | (t1 >>> 5); - bc1 = (t1 << 27) | (t0 >>> 5); - s[30] = bc0 ^ (bc4 & ~bc2); - s[31] = bc1 ^ (bc5 & ~bc3); - s[32] = bc2 ^ (bc6 & ~bc4); - s[33] = bc3 ^ (bc7 & ~bc5); - s[34] = bc4 ^ (bc8 & ~bc6); - s[35] = bc5 ^ (bc9 & ~bc7); - s[36] = bc6 ^ (bc0 & ~bc8); - s[37] = bc7 ^ (bc1 & ~bc9); - s[38] = bc8 ^ (bc2 & ~bc0); - s[39] = bc9 ^ (bc3 & ~bc1); - - t0 = s[40] ^ d0; - t1 = s[41] ^ d1; - bc6 = (t1 << 9) | (t0 >>> 23); - bc7 = (t0 << 9) | (t1 >>> 23); - t0 = s[42] ^ d2; - t1 = s[43] ^ d3; - bc8 = (t0 << 2) | (t1 >>> 30); - bc9 = (t1 << 2) | (t0 >>> 30); - t0 = s[44] ^ d4; - t1 = s[45] ^ d5; - bc0 = (t1 << 30) | (t0 >>> 2); - bc1 = (t0 << 30) | (t1 >>> 2); - t0 = s[46] ^ d6; - t1 = s[47] ^ d7; - bc2 = (t1 << 23) | (t0 >>> 9); - bc3 = (t0 << 23) | (t1 >>> 9); - t0 = s[48] ^ d8; - t1 = s[49] ^ d9; - bc4 = (t1 << 7) | (t0 >>> 25); - bc5 = (t0 << 7) | (t1 >>> 25); - s[40] = bc0 ^ (bc4 & ~bc2); - s[41] = bc1 ^ (bc5 & ~bc3); - s[42] = bc2 ^ (bc6 & ~bc4); - s[43] = bc3 ^ (bc7 & ~bc5); - s[44] = bc4 ^ (bc8 & ~bc6); - s[45] = bc5 ^ (bc9 & ~bc7); - s[46] = bc6 ^ (bc0 & ~bc8); - s[47] = bc7 ^ (bc1 & ~bc9); - s[48] = bc8 ^ (bc2 & ~bc0); - s[49] = bc9 ^ (bc3 & ~bc1); - } -} diff --git a/std/hash/_sha3/sha3.ts b/std/hash/_sha3/sha3.ts deleted file mode 100644 index 2966ce2a4c..0000000000 --- a/std/hash/_sha3/sha3.ts +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Sponge } from "./sponge.ts"; -import { keccakf } from "./keccakf.ts"; - -/** Sha3-224 hash */ -// deno-lint-ignore camelcase -export class Sha3_224 extends Sponge { - constructor() { - super({ - bitsize: 224, - rate: 144, - dsbyte: 6, - permutator: keccakf, - }); - } -} - -/** Sha3-256 hash */ -// deno-lint-ignore camelcase -export class Sha3_256 extends Sponge { - constructor() { - super({ - bitsize: 256, - rate: 136, - dsbyte: 6, - permutator: keccakf, - }); - } -} - -/** Sha3-384 hash */ -// deno-lint-ignore camelcase -export class Sha3_384 extends Sponge { - constructor() { - super({ - bitsize: 384, - rate: 104, - dsbyte: 6, - permutator: keccakf, - }); - } -} - -/** Sha3-512 hash */ -// deno-lint-ignore camelcase -export class Sha3_512 extends Sponge { - constructor() { - super({ - bitsize: 512, - rate: 72, - dsbyte: 6, - permutator: keccakf, - }); - } -} diff --git a/std/hash/_sha3/shake.ts b/std/hash/_sha3/shake.ts deleted file mode 100644 index 4fe24d7cfb..0000000000 --- a/std/hash/_sha3/shake.ts +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Sponge } from "./sponge.ts"; -import { keccakf } from "./keccakf.ts"; - -/** Shake128 hash */ -export class Shake128 extends Sponge { - /** - * Instantiates a new Shake128 hash - * @param bitsize length of hash in bits - */ - constructor(bitsize: number) { - if (bitsize < 8) { - throw new Error("shake128: `bitsize` too small"); - } - - if (bitsize % 8 !== 0) { - throw new Error("shake128: `bitsize` must be multiple of 8"); - } - - super({ - bitsize: bitsize, - rate: 168, - dsbyte: 0x1f, - permutator: keccakf, - }); - } -} - -/** - * Instantiates a new Shake256 hash - * @param bitsize length of hash in bits - */ -export class Shake256 extends Sponge { - constructor(bitsize: number) { - if (bitsize < 8) { - throw new Error("shake256: `bitsize` too small"); - } - - if (bitsize % 8 !== 0) { - throw new Error("shake256: `bitsize` must be multiple of 8"); - } - - super({ - bitsize: bitsize, - rate: 136, - dsbyte: 0x1f, - permutator: keccakf, - }); - } -} diff --git a/std/hash/_sha3/sponge.ts b/std/hash/_sha3/sponge.ts deleted file mode 100644 index 9ad67ca079..0000000000 --- a/std/hash/_sha3/sponge.ts +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import * as hex from "../../encoding/hex.ts"; - -type SpongePermutator = (data: Uint8Array) => void; - -/** Sponge construction option */ -export interface SpongeOption { - bitsize: number; - rate: number; - dsbyte: number; - permutator: SpongePermutator; -} - -export type Message = string | ArrayBuffer; - -const STATE_SIZE = 200; -const TYPE_ERROR_MSG = "sha3: `data` is invalid type"; - -/** Sponge construction */ -export class Sponge { - #option: SpongeOption; - #state: Uint8Array; - #rp: number; - #absorbing: boolean; - - constructor(option: SpongeOption) { - this.#option = option; - this.#state = new Uint8Array(STATE_SIZE); - this.#rp = 0; - this.#absorbing = true; - } - - /** Applies padding to internal state */ - private pad(): void { - this.#state[this.#rp] ^= this.#option.dsbyte; - this.#state[this.#option.rate - 1] ^= 0x80; - } - - /** Squeezes internal state */ - protected squeeze(length: number): Uint8Array { - if (length < 0) { - throw new Error("sha3: length cannot be negative"); - } - - this.pad(); - - const hash = new Uint8Array(length); - let pos = 0; - while (length > 0) { - const r = length > this.#option.rate ? this.#option.rate : length; - this.#option.permutator(this.#state); - hash.set(this.#state.slice(0, r), pos); - length -= r; - pos += r; - } - - this.#absorbing = false; - return hash; - } - - /** Updates internal state by absorbing */ - update(data: Message): this { - if (!this.#absorbing) { - throw new Error("sha3: cannot update already finalized hash"); - } - - let msg: Uint8Array; - - if (typeof data === "string") { - msg = new TextEncoder().encode(data as string); - } else if (typeof data === "object") { - if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) { - msg = new Uint8Array(data); - } else { - throw new Error(TYPE_ERROR_MSG); - } - } else { - throw new Error(TYPE_ERROR_MSG); - } - - let rp = this.#rp; - - for (let i = 0; i < msg.length; ++i) { - this.#state[rp++] ^= msg[i]; - if (rp >= this.#option.rate) { - this.#option.permutator(this.#state); - rp = 0; - } - } - - this.#rp = rp; - return this; - } - - /** Returns the hash in ArrayBuffer */ - digest(): ArrayBuffer { - return this.squeeze(this.#option.bitsize >> 3); - } - - /** Returns the hash in given format */ - toString(format: "hex" = "hex"): string { - const rawOutput = this.squeeze(this.#option.bitsize >> 3); - switch (format) { - case "hex": - return hex.encodeToString(rawOutput); - default: - throw new Error("sha3: invalid output format"); - } - } -} diff --git a/std/hash/_wasm/Cargo.lock b/std/hash/_wasm/Cargo.lock deleted file mode 100644 index 5ff47d7566..0000000000 --- a/std/hash/_wasm/Cargo.lock +++ /dev/null @@ -1,291 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - -[[package]] -name = "bumpalo" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cpuid-bool" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" - -[[package]] -name = "deno-hash" -version = "0.1.0" -dependencies = [ - "digest", - "md-5", - "md2", - "md4", - "ripemd160", - "ripemd320", - "sha-1", - "sha2", - "sha3", - "wasm-bindgen", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "generic-array" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "keccak" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "log" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "md-5" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" -dependencies = [ - "block-buffer", - "digest", - "opaque-debug", -] - -[[package]] -name = "md2" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd23bb613adebff88451d962b449f93e2ca5d97400021ad5895740778d95f78" -dependencies = [ - "block-buffer", - "digest", - "opaque-debug", -] - -[[package]] -name = "md4" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd1a7931601ee6a560262a1dc9a8369949f5b7ae20b2bbf029c74fbd6d1b09e2" -dependencies = [ - "block-buffer", - "digest", - "opaque-debug", -] - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "proc-macro2" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "ripemd160" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" -dependencies = [ - "block-buffer", - "digest", - "opaque-debug", -] - -[[package]] -name = "ripemd320" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ff9fb4227f99b2078691fbd1bf16edd94041f15b02a3c7b4a155d0074954191" -dependencies = [ - "block-buffer", - "digest", - "opaque-debug", -] - -[[package]] -name = "sha-1" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770" -dependencies = [ - "block-buffer", - "cfg-if", - "cpuid-bool", - "digest", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" -dependencies = [ - "block-buffer", - "cfg-if", - "cpuid-bool", - "digest", - "opaque-debug", -] - -[[package]] -name = "sha3" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" -dependencies = [ - "block-buffer", - "digest", - "keccak", - "opaque-debug", -] - -[[package]] -name = "syn" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "typenum" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" - -[[package]] -name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" - -[[package]] -name = "version_check" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" - -[[package]] -name = "wasm-bindgen" -version = "0.2.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307" diff --git a/std/hash/_wasm/Cargo.toml b/std/hash/_wasm/Cargo.toml deleted file mode 100644 index 7a9119552f..0000000000 --- a/std/hash/_wasm/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "deno-hash" -version = "0.1.0" -authors = ["the Deno authors"] -edition = "2018" -license = "MIT" -repository = "https://github.com/denoland/deno" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -digest = "0.9.0" -md2 = "0.9.0" -md4 = "0.9.0" -md-5 = "0.9.1" -ripemd160 = "0.9.1" -ripemd320 = "0.9.0" -sha-1 = "0.9.1" -sha2 = "0.9.1" -sha3 = "0.9.1" -wasm-bindgen = "0.2.68" - -[profile.release] -lto = true -opt-level = 3 - -[package.metadata.wasm-pack.profile.release] -wasm-opt = ["-O", "--enable-mutable-globals"] diff --git a/std/hash/_wasm/README.md b/std/hash/_wasm/README.md deleted file mode 100644 index 721e496d25..0000000000 --- a/std/hash/_wasm/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# How to build - -## Prerequisite - -`wasm-pack` is required. - -```sh -cargo install wasm-pack -``` - -## Build - -```sh -deno run --allow-read --allow-write --allow-run ./build.ts -``` - -`wasm.js` will be generated. diff --git a/std/hash/_wasm/build.ts b/std/hash/_wasm/build.ts deleted file mode 100644 index a328c26158..0000000000 --- a/std/hash/_wasm/build.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { encode as base64Encode } from "../../encoding/base64.ts"; - -// 1. build wasm -async function buildWasm(path: string): Promise { - const cmd = [ - "wasm-pack", - "build", - "--target", - "web", - "--release", - "-d", - path, - ]; - const builder = Deno.run({ cmd }); - const status = await builder.status(); - - if (!status.success) { - console.error(`Failed to build wasm: ${status.code}`); - Deno.exit(1); - } -} - -// 2. encode wasm -async function encodeWasm(wasmPath: string): Promise { - const wasm = await Deno.readFile(`${wasmPath}/deno_hash_bg.wasm`); - return base64Encode(wasm); -} - -// 3. generate script -async function generate(wasm: string, output: string): Promise { - const initScript = await Deno.readTextFile(`${output}/deno_hash.js`); - const denoHashScript = - "// deno-lint-ignore-file\n" + - "//deno-fmt-ignore-file\n" + - "//deno-lint-ignore-file\n" + - `import * as base64 from "../../encoding/base64.ts";` + - `export const source = base64.decode("${wasm}");` + - initScript; - - await Deno.writeFile("wasm.js", new TextEncoder().encode(denoHashScript)); -} - -const OUTPUT_DIR = "./out"; - -await buildWasm(OUTPUT_DIR); -const wasm = await encodeWasm(OUTPUT_DIR); -await generate(wasm, OUTPUT_DIR); diff --git a/std/hash/_wasm/hash.ts b/std/hash/_wasm/hash.ts deleted file mode 100644 index eb6636dab3..0000000000 --- a/std/hash/_wasm/hash.ts +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import init, { - source, - create_hash as createHash, - update_hash as updateHash, - digest_hash as digestHash, - DenoHash, -} from "./wasm.js"; - -import * as hex from "../../encoding/hex.ts"; -import * as base64 from "../../encoding/base64.ts"; -import type { Hasher, Message, OutputFormat } from "../hasher.ts"; - -await init(source); - -const TYPE_ERROR_MSG = "hash: `data` is invalid type"; - -export class Hash implements Hasher { - #hash: DenoHash; - #digested: boolean; - - constructor(algorithm: string) { - this.#hash = createHash(algorithm); - this.#digested = false; - } - - /** - * Update internal state - * @param data data to update - */ - update(data: Message): this { - let msg: Uint8Array; - - if (typeof data === "string") { - msg = new TextEncoder().encode(data as string); - } else if (typeof data === "object") { - if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) { - msg = new Uint8Array(data); - } else { - throw new Error(TYPE_ERROR_MSG); - } - } else { - throw new Error(TYPE_ERROR_MSG); - } - - updateHash(this.#hash, msg); - - return this; - } - - /** Returns final hash */ - digest(): ArrayBuffer { - if (this.#digested) throw new Error("hash: already digested"); - - this.#digested = true; - return digestHash(this.#hash); - } - - /** - * Returns hash as a string of given format - * @param format format of output string (hex or base64). Default is hex - */ - toString(format: OutputFormat = "hex"): string { - const finalized = new Uint8Array(this.digest()); - - switch (format) { - case "hex": - return hex.encodeToString(finalized); - case "base64": - return base64.encode(finalized); - default: - throw new Error("hash: invalid format"); - } - } -} diff --git a/std/hash/_wasm/src/lib.rs b/std/hash/_wasm/src/lib.rs deleted file mode 100644 index 7e72a7471b..0000000000 --- a/std/hash/_wasm/src/lib.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -use digest::{Digest, DynDigest}; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -pub struct DenoHash { - inner: Box, -} - -#[wasm_bindgen] -pub fn create_hash(algorithm: &str) -> Result { - let hash: Option> = match algorithm { - "md2" => Some(Box::new(md2::Md2::new())), - "md4" => Some(Box::new(md4::Md4::new())), - "md5" => Some(Box::new(md5::Md5::new())), - "ripemd160" => Some(Box::new(ripemd160::Ripemd160::new())), - "ripemd320" => Some(Box::new(ripemd320::Ripemd320::new())), - "sha1" => Some(Box::new(sha1::Sha1::new())), - "sha224" => Some(Box::new(sha2::Sha224::new())), - "sha256" => Some(Box::new(sha2::Sha256::new())), - "sha384" => Some(Box::new(sha2::Sha384::new())), - "sha512" => Some(Box::new(sha2::Sha512::new())), - "sha3-224" => Some(Box::new(sha3::Sha3_224::new())), - "sha3-256" => Some(Box::new(sha3::Sha3_256::new())), - "sha3-384" => Some(Box::new(sha3::Sha3_384::new())), - "sha3-512" => Some(Box::new(sha3::Sha3_512::new())), - "keccak224" => Some(Box::new(sha3::Keccak224::new())), - "keccak256" => Some(Box::new(sha3::Keccak256::new())), - "keccak384" => Some(Box::new(sha3::Keccak384::new())), - "keccak512" => Some(Box::new(sha3::Keccak512::new())), - _ => None, - }; - - if let Some(h) = hash { - Ok(DenoHash { inner: h }) - } else { - let err_msg = format!("unsupported hash algorithm: {}", algorithm); - Err(JsValue::from_str(&err_msg)) - } -} - -#[wasm_bindgen] -pub fn update_hash(hash: &mut DenoHash, data: &[u8]) { - hash.inner.update(data) -} - -#[wasm_bindgen] -pub fn digest_hash(hash: &mut DenoHash) -> Box<[u8]> { - hash.inner.finalize_reset() -} diff --git a/std/hash/_wasm/wasm.js b/std/hash/_wasm/wasm.js deleted file mode 100644 index 9f4adbd021..0000000000 --- a/std/hash/_wasm/wasm.js +++ /dev/null @@ -1,254 +0,0 @@ -// deno-lint-ignore-file -//deno-fmt-ignore-file -//deno-lint-ignore-file -import * as base64 from "../../encoding/base64.ts";export const source = base64.decode("AGFzbQEAAAABSQxgAn9/AGACf38Bf2ADf39/AGADf39/AX9gAX8AYAF/AX9gAABgBH9/f38Bf2AFf39/f38AYAV/f39/fwF/YAJ+fwF/YAF/AX4CTQMDd2JnFV9fd2JpbmRnZW5fc3RyaW5nX25ldwABA3diZxBfX3diaW5kZ2VuX3Rocm93AAADd2JnEl9fd2JpbmRnZW5fcmV0aHJvdwAEA6sBqQEAAgEAAAIFAAACAAQABAADAAAAAQcJAAAAAAAAAAAAAAAAAAAAAAICAgIAAAAAAAAAAAAAAAAAAAACAgICBAAAAgAAAQAAAAAAAAAAAAAAAAAECgEEAQIAAAAAAgIAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAQEAgICAAEGAAMEAgcEAgQEAwMFBAQAAwQDAQEBAQQABwYBBgYBAAELBQUFBQUFBQAEBAUBcAFpaQUDAQARBgkBfwFBgIDAAAsHoQEJBm1lbW9yeQIAE19fd2JnX2Rlbm9oYXNoX2ZyZWUAhAELY3JlYXRlX2hhc2gABQt1cGRhdGVfaGFzaACFAQtkaWdlc3RfaGFzaACCARFfX3diaW5kZ2VuX21hbGxvYwCNARJfX3diaW5kZ2VuX3JlYWxsb2MAkwETX193YmluZGdlbl9leHBvcnRfMgMAD19fd2JpbmRnZW5fZnJlZQCZAQmPAQEAQQELaJcBqgGcAZYBnwFYqwFDDy5XowE3PEFIkgGjAWA/QkliPi9EjgGlAVI9GSiHAaQBR2EwRY8BU18nOooBqAFQIS2JAakBUVkTHnunAUsVJnqmAUoqNjiYAagBcSkyNJgBqQF1LBocmAGnAXQrIiSYAaYBdzU5cDEzeBsddiMlc4wBVoABlQGiAZQBCsixBqkBjEwBVn4gACABKQN4IgIgASkDSCIaIAEpAwAiFyABKQMIIgtCOIkgC0IHiIUgC0I/iYV8fCABKQNwIgNCA4kgA0IGiIUgA0ItiYV8IgRCOIkgBEIHiIUgBEI/iYV8IAEpA1AiPiABKQMQIglCOIkgCUIHiIUgCUI/iYUgC3x8IAJCBoggAkIDiYUgAkItiYV8IgcgASkDQCITIBpCB4ggGkI4iYUgGkI/iYV8fCABKQMwIhQgASkDOCJCQjiJIEJCB4iFIEJCP4mFfCACfCABKQNoIkQgASkDICIVIAEpAygiQ0I4iSBDQgeIhSBDQj+JhXx8IAEpA1giPyABKQMYIgpCOIkgCkIHiIUgCkI/iYUgCXx8IARCBoggBEIDiYUgBEItiYV8IgZCA4kgBkIGiIUgBkItiYV8IgVCA4kgBUIGiIUgBUItiYV8IghCA4kgCEIGiIUgCEItiYV8Igx8IANCB4ggA0I4iYUgA0I/iYUgRHwgCHwgASkDYCJAQjiJIEBCB4iFIEBCP4mFID98IAV8ID5CB4ggPkI4iYUgPkI/iYUgGnwgBnwgE0IHiCATQjiJhSATQj+JhSBCfCAEfCAUQgeIIBRCOImFIBRCP4mFIEN8IAN8IBVCB4ggFUI4iYUgFUI/iYUgCnwgQHwgB0IGiCAHQgOJhSAHQi2JhXwiDUIDiSANQgaIhSANQi2JhXwiDkIDiSAOQgaIhSAOQi2JhXwiEEIDiSAQQgaIhSAQQi2JhXwiEUIDiSARQgaIhSARQi2JhXwiFkIDiSAWQgaIhSAWQi2JhXwiGEIDiSAYQgaIhSAYQi2JhXwiGUI4iSAZQgeIhSAZQj+JhSACQgeIIAJCOImFIAJCP4mFIAN8IBB8IERCB4ggREI4iYUgREI/iYUgQHwgDnwgP0IHiCA/QjiJhSA/Qj+JhSA+fCANfCAMQgaIIAxCA4mFIAxCLYmFfCIbQgOJIBtCBoiFIBtCLYmFfCIcQgOJIBxCBoiFIBxCLYmFfCIdfCAHQgeIIAdCOImFIAdCP4mFIAR8IBF8IB1CBoggHUIDiYUgHUItiYV8Ih4gDEIHiCAMQjiJhSAMQj+JhSAQfHwgCEIHiCAIQjiJhSAIQj+JhSAOfCAdfCAFQgeIIAVCOImFIAVCP4mFIA18IBx8IAZCB4ggBkI4iYUgBkI/iYUgB3wgG3wgGUIGiCAZQgOJhSAZQi2JhXwiH0IDiSAfQgaIhSAfQi2JhXwiIEIDiSAgQgaIhSAgQi2JhXwiIUIDiSAhQgaIhSAhQi2JhXwiInwgGEIHiCAYQjiJhSAYQj+JhSAcfCAhfCAWQgeIIBZCOImFIBZCP4mFIBt8ICB8IBFCB4ggEUI4iYUgEUI/iYUgDHwgH3wgEEIHiCAQQjiJhSAQQj+JhSAIfCAZfCAOQgeIIA5COImFIA5CP4mFIAV8IBh8IA1CB4ggDUI4iYUgDUI/iYUgBnwgFnwgHkIGiCAeQgOJhSAeQi2JhXwiI0IDiSAjQgaIhSAjQi2JhXwiJEIDiSAkQgaIhSAkQi2JhXwiJUIDiSAlQgaIhSAlQi2JhXwiJkIDiSAmQgaIhSAmQi2JhXwiJ0IDiSAnQgaIhSAnQi2JhXwiKEIDiSAoQgaIhSAoQi2JhXwiKUI4iSApQgeIhSApQj+JhSAdQgeIIB1COImFIB1CP4mFIBh8ICV8IBxCB4ggHEI4iYUgHEI/iYUgFnwgJHwgG0IHiCAbQjiJhSAbQj+JhSARfCAjfCAiQgaIICJCA4mFICJCLYmFfCIqQgOJICpCBoiFICpCLYmFfCIrQgOJICtCBoiFICtCLYmFfCIsfCAeQgeIIB5COImFIB5CP4mFIBl8ICZ8ICxCBoggLEIDiYUgLEItiYV8Ii0gIkIHiCAiQjiJhSAiQj+JhSAlfHwgIUIHiCAhQjiJhSAhQj+JhSAkfCAsfCAgQgeIICBCOImFICBCP4mFICN8ICt8IB9CB4ggH0I4iYUgH0I/iYUgHnwgKnwgKUIGiCApQgOJhSApQi2JhXwiLkIDiSAuQgaIhSAuQi2JhXwiL0IDiSAvQgaIhSAvQi2JhXwiMEIDiSAwQgaIhSAwQi2JhXwiMXwgKEIHiCAoQjiJhSAoQj+JhSArfCAwfCAnQgeIICdCOImFICdCP4mFICp8IC98ICZCB4ggJkI4iYUgJkI/iYUgInwgLnwgJUIHiCAlQjiJhSAlQj+JhSAhfCApfCAkQgeIICRCOImFICRCP4mFICB8ICh8ICNCB4ggI0I4iYUgI0I/iYUgH3wgJ3wgLUIGiCAtQgOJhSAtQi2JhXwiMkIDiSAyQgaIhSAyQi2JhXwiM0IDiSAzQgaIhSAzQi2JhXwiNEIDiSA0QgaIhSA0Qi2JhXwiNUIDiSA1QgaIhSA1Qi2JhXwiNkIDiSA2QgaIhSA2Qi2JhXwiN0IDiSA3QgaIhSA3Qi2JhXwiOEI4iSA4QgeIhSA4Qj+JhSAsQgeIICxCOImFICxCP4mFICh8IDR8ICtCB4ggK0I4iYUgK0I/iYUgJ3wgM3wgKkIHiCAqQjiJhSAqQj+JhSAmfCAyfCAxQgaIIDFCA4mFIDFCLYmFfCI5QgOJIDlCBoiFIDlCLYmFfCI6QgOJIDpCBoiFIDpCLYmFfCI7fCAtQgeIIC1COImFIC1CP4mFICl8IDV8IDtCBoggO0IDiYUgO0ItiYV8IjwgMUIHiCAxQjiJhSAxQj+JhSA0fHwgMEIHiCAwQjiJhSAwQj+JhSAzfCA7fCAvQgeIIC9COImFIC9CP4mFIDJ8IDp8IC5CB4ggLkI4iYUgLkI/iYUgLXwgOXwgOEIGiCA4QgOJhSA4Qi2JhXwiPUIDiSA9QgaIhSA9Qi2JhXwiRkIDiSBGQgaIhSBGQi2JhXwiR0IDiSBHQgaIhSBHQi2JhXwiSHwgN0IHiCA3QjiJhSA3Qj+JhSA6fCBHfCA2QgeIIDZCOImFIDZCP4mFIDl8IEZ8IDVCB4ggNUI4iYUgNUI/iYUgMXwgPXwgNEIHiCA0QjiJhSA0Qj+JhSAwfCA4fCAzQgeIIDNCOImFIDNCP4mFIC98IDd8IDJCB4ggMkI4iYUgMkI/iYUgLnwgNnwgPEIGiCA8QgOJhSA8Qi2JhXwiQUIDiSBBQgaIhSBBQi2JhXwiSUIDiSBJQgaIhSBJQi2JhXwiSkIDiSBKQgaIhSBKQi2JhXwiS0IDiSBLQgaIhSBLQi2JhXwiTEIDiSBMQgaIhSBMQi2JhXwiTkIDiSBOQgaIhSBOQi2JhXwiTyBMIEogQSA7IDkgMCAuICggJiAkIB4gHCAMIAUgBCBAIBMgFSAXIAApAzgiVCAAKQMgIhdCMokgF0IuiYUgF0IXiYV8IAApAzAiUCAAKQMoIk2FIBeDIFCFfHxCotyiuY3zi8XCAHwiEiAAKQMYIlV8IhV8IAogF3wgCSBNfCALIFB8IBUgFyBNhYMgTYV8IBVCMokgFUIuiYUgFUIXiYV8Qs3LvZ+SktGb8QB8IlEgACkDECJSfCIJIBUgF4WDIBeFfCAJQjKJIAlCLomFIAlCF4mFfEKv9rTi/vm+4LV/fCJTIAApAwgiRXwiCiAJIBWFgyAVhXwgCkIyiSAKQi6JhSAKQheJhXxCvLenjNj09tppfCJWIAApAwAiFXwiDyAJIAqFgyAJhXwgD0IyiSAPQi6JhSAPQheJhXxCuOqimr/LsKs5fCJXIEUgUoUgFYMgRSBSg4UgFUIkiSAVQh6JhSAVQhmJhXwgEnwiC3wiEnwgDyBCfCAKIBR8IAkgQ3wgEiAKIA+FgyAKhXwgEkIyiSASQi6JhSASQheJhXxCmaCXsJu+xPjZAHwiQiALQiSJIAtCHomFIAtCGYmFIAsgFSBFhYMgFSBFg4V8IFF8Igl8IhMgDyAShYMgD4V8IBNCMokgE0IuiYUgE0IXiYV8Qpuf5fjK1OCfkn98IkMgCUIkiSAJQh6JhSAJQhmJhSAJIAsgFYWDIAsgFYOFfCBTfCIKfCIPIBIgE4WDIBKFfCAPQjKJIA9CLomFIA9CF4mFfEKYgrbT3dqXjqt/fCJRIApCJIkgCkIeiYUgCkIZiYUgCiAJIAuFgyAJIAuDhXwgVnwiC3wiEiAPIBOFgyAThXwgEkIyiSASQi6JhSASQheJhXxCwoSMmIrT6oNYfCJTIAtCJIkgC0IeiYUgC0IZiYUgCyAJIAqFgyAJIAqDhXwgV3wiCXwiFHwgEiA/fCAPID58IBMgGnwgFCAPIBKFgyAPhXwgFEIyiSAUQi6JhSAUQheJhXxCvt/Bq5Tg1sESfCIaIAlCJIkgCUIeiYUgCUIZiYUgCSAKIAuFgyAKIAuDhXwgQnwiCnwiDyASIBSFgyAShXwgD0IyiSAPQi6JhSAPQheJhXxCjOWS9+S34ZgkfCI+IApCJIkgCkIeiYUgCkIZiYUgCiAJIAuFgyAJIAuDhXwgQ3wiC3wiEiAPIBSFgyAUhXwgEkIyiSASQi6JhSASQheJhXxC4un+r724n4bVAHwiPyALQiSJIAtCHomFIAtCGYmFIAsgCSAKhYMgCSAKg4V8IFF8Igl8IhMgDyAShYMgD4V8IBNCMokgE0IuiYUgE0IXiYV8Qu+S7pPPrpff8gB8IkAgCUIkiSAJQh6JhSAJQhmJhSAJIAogC4WDIAogC4OFfCBTfCIKfCIUfCACIBN8IAMgEnwgDyBEfCAUIBIgE4WDIBKFfCAUQjKJIBRCLomFIBRCF4mFfEKxrdrY47+s74B/fCISIApCJIkgCkIeiYUgCkIZiYUgCiAJIAuFgyAJIAuDhXwgGnwiAnwiCyATIBSFgyAThXwgC0IyiSALQi6JhSALQheJhXxCtaScrvLUge6bf3wiEyACQiSJIAJCHomFIAJCGYmFIAIgCSAKhYMgCSAKg4V8ID58IgN8IgkgCyAUhYMgFIV8IAlCMokgCUIuiYUgCUIXiYV8QpTNpPvMrvzNQXwiFCADQiSJIANCHomFIANCGYmFIAMgAiAKhYMgAiAKg4V8ID98IgR8IgogCSALhYMgC4V8IApCMokgCkIuiYUgCkIXiYV8QtKVxfeZuNrNZHwiGiAEQiSJIARCHomFIARCGYmFIAQgAiADhYMgAiADg4V8IEB8IgJ8Ig98IAogDXwgBiAJfCAHIAt8IA8gCSAKhYMgCYV8IA9CMokgD0IuiYUgD0IXiYV8QuPLvMLj8JHfb3wiCyACQiSJIAJCHomFIAJCGYmFIAIgAyAEhYMgAyAEg4V8IBJ8IgN8IgcgCiAPhYMgCoV8IAdCMokgB0IuiYUgB0IXiYV8QrWrs9zouOfgD3wiCSADQiSJIANCHomFIANCGYmFIAMgAiAEhYMgAiAEg4V8IBN8IgR8IgYgByAPhYMgD4V8IAZCMokgBkIuiYUgBkIXiYV8QuW4sr3HuaiGJHwiCiAEQiSJIARCHomFIARCGYmFIAQgAiADhYMgAiADg4V8IBR8IgJ8IgUgBiAHhYMgB4V8IAVCMokgBUIuiYUgBUIXiYV8QvWErMn1jcv0LXwiDyACQiSJIAJCHomFIAJCGYmFIAIgAyAEhYMgAyAEg4V8IBp8IgN8Ig18IAUgEHwgBiAIfCAHIA58IA0gBSAGhYMgBoV8IA1CMokgDUIuiYUgDUIXiYV8QoPJm/WmlaG6ygB8IgwgA0IkiSADQh6JhSADQhmJhSADIAIgBIWDIAIgBIOFfCALfCIEfCIHIAUgDYWDIAWFfCAHQjKJIAdCLomFIAdCF4mFfELU94fqy7uq2NwAfCIOIARCJIkgBEIeiYUgBEIZiYUgBCACIAOFgyACIAODhXwgCXwiAnwiBiAHIA2FgyANhXwgBkIyiSAGQi6JhSAGQheJhXxCtafFmKib4vz2AHwiDSACQiSJIAJCHomFIAJCGYmFIAIgAyAEhYMgAyAEg4V8IAp8IgN8IgUgBiAHhYMgB4V8IAVCMokgBUIuiYUgBUIXiYV8Qqu/m/OuqpSfmH98IhAgA0IkiSADQh6JhSADQhmJhSADIAIgBIWDIAIgBIOFfCAPfCIEfCIIfCAFIBZ8IAYgG3wgByARfCAIIAUgBoWDIAaFfCAIQjKJIAhCLomFIAhCF4mFfEKQ5NDt0s3xmKh/fCIRIARCJIkgBEIeiYUgBEIZiYUgBCACIAOFgyACIAODhXwgDHwiAnwiByAFIAiFgyAFhXwgB0IyiSAHQi6JhSAHQheJhXxCv8Lsx4n5yYGwf3wiDCACQiSJIAJCHomFIAJCGYmFIAIgAyAEhYMgAyAEg4V8IA58IgN8IgYgByAIhYMgCIV8IAZCMokgBkIuiYUgBkIXiYV8QuSdvPf7+N+sv398Ig4gA0IkiSADQh6JhSADQhmJhSADIAIgBIWDIAIgBIOFfCANfCIEfCIFIAYgB4WDIAeFfCAFQjKJIAVCLomFIAVCF4mFfELCn6Lts/6C8EZ8Ig0gBEIkiSAEQh6JhSAEQhmJhSAEIAIgA4WDIAIgA4OFfCAQfCICfCIIfCAFIBl8IAYgHXwgByAYfCAIIAUgBoWDIAaFfCAIQjKJIAhCLomFIAhCF4mFfEKlzqqY+ajk01V8IhAgAkIkiSACQh6JhSACQhmJhSACIAMgBIWDIAMgBIOFfCARfCIDfCIHIAUgCIWDIAWFfCAHQjKJIAdCLomFIAdCF4mFfELvhI6AnuqY5QZ8IhEgA0IkiSADQh6JhSADQhmJhSADIAIgBIWDIAIgBIOFfCAMfCIEfCIGIAcgCIWDIAiFfCAGQjKJIAZCLomFIAZCF4mFfELw3LnQ8KzKlBR8IgwgBEIkiSAEQh6JhSAEQhmJhSAEIAIgA4WDIAIgA4OFfCAOfCICfCIFIAYgB4WDIAeFfCAFQjKJIAVCLomFIAVCF4mFfEL838i21NDC2yd8Ig4gAkIkiSACQh6JhSACQhmJhSACIAMgBIWDIAMgBIOFfCANfCIDfCIIfCAFICB8IAYgI3wgByAffCAIIAUgBoWDIAaFfCAIQjKJIAhCLomFIAhCF4mFfEKmkpvhhafIjS58Ig0gA0IkiSADQh6JhSADQhmJhSADIAIgBIWDIAIgBIOFfCAQfCIEfCIHIAUgCIWDIAWFfCAHQjKJIAdCLomFIAdCF4mFfELt1ZDWxb+bls0AfCIQIARCJIkgBEIeiYUgBEIZiYUgBCACIAOFgyACIAODhXwgEXwiAnwiBiAHIAiFgyAIhXwgBkIyiSAGQi6JhSAGQheJhXxC3+fW7Lmig5zTAHwiESACQiSJIAJCHomFIAJCGYmFIAIgAyAEhYMgAyAEg4V8IAx8IgN8IgUgBiAHhYMgB4V8IAVCMokgBUIuiYUgBUIXiYV8Qt7Hvd3I6pyF5QB8IgwgA0IkiSADQh6JhSADQhmJhSADIAIgBIWDIAIgBIOFfCAOfCIEfCIIfCAFICJ8IAYgJXwgByAhfCAIIAUgBoWDIAaFfCAIQjKJIAhCLomFIAhCF4mFfEKo5d7js9eCtfYAfCIOIARCJIkgBEIeiYUgBEIZiYUgBCACIAOFgyACIAODhXwgDXwiAnwiByAFIAiFgyAFhXwgB0IyiSAHQi6JhSAHQheJhXxC5t22v+SlsuGBf3wiDSACQiSJIAJCHomFIAJCGYmFIAIgAyAEhYMgAyAEg4V8IBB8IgN8IgYgByAIhYMgCIV8IAZCMokgBkIuiYUgBkIXiYV8QrvqiKTRkIu5kn98IhAgA0IkiSADQh6JhSADQhmJhSADIAIgBIWDIAIgBIOFfCARfCIEfCIFIAYgB4WDIAeFfCAFQjKJIAVCLomFIAVCF4mFfELkhsTnlJT636J/fCIRIARCJIkgBEIeiYUgBEIZiYUgBCACIAOFgyACIAODhXwgDHwiAnwiCHwgBSArfCAGICd8IAcgKnwgCCAFIAaFgyAGhXwgCEIyiSAIQi6JhSAIQheJhXxCgeCI4rvJmY2of3wiDCACQiSJIAJCHomFIAJCGYmFIAIgAyAEhYMgAyAEg4V8IA58IgN8IgcgBSAIhYMgBYV8IAdCMokgB0IuiYUgB0IXiYV8QpGv4oeN7uKlQnwiDiADQiSJIANCHomFIANCGYmFIAMgAiAEhYMgAiAEg4V8IA18IgR8IgYgByAIhYMgCIV8IAZCMokgBkIuiYUgBkIXiYV8QrD80rKwtJS2R3wiDSAEQiSJIARCHomFIARCGYmFIAQgAiADhYMgAiADg4V8IBB8IgJ8IgUgBiAHhYMgB4V8IAVCMokgBUIuiYUgBUIXiYV8Qpikvbedg7rJUXwiECACQiSJIAJCHomFIAJCGYmFIAIgAyAEhYMgAyAEg4V8IBF8IgN8Igh8IAUgLXwgBiApfCAHICx8IAggBSAGhYMgBoV8IAhCMokgCEIuiYUgCEIXiYV8QpDSlqvFxMHMVnwiESADQiSJIANCHomFIANCGYmFIAMgAiAEhYMgAiAEg4V8IAx8IgR8IgcgBSAIhYMgBYV8IAdCMokgB0IuiYUgB0IXiYV8QqrAxLvVsI2HdHwiDCAEQiSJIARCHomFIARCGYmFIAQgAiADhYMgAiADg4V8IA58IgJ8IgYgByAIhYMgCIV8IAZCMokgBkIuiYUgBkIXiYV8Qrij75WDjqi1EHwiDiACQiSJIAJCHomFIAJCGYmFIAIgAyAEhYMgAyAEg4V8IA18IgN8IgUgBiAHhYMgB4V8IAVCMokgBUIuiYUgBUIXiYV8Qsihy8brorDSGXwiDSADQiSJIANCHomFIANCGYmFIAMgAiAEhYMgAiAEg4V8IBB8IgR8Igh8IAUgM3wgBiAvfCAHIDJ8IAggBSAGhYMgBoV8IAhCMokgCEIuiYUgCEIXiYV8QtPWhoqFgdubHnwiECAEQiSJIARCHomFIARCGYmFIAQgAiADhYMgAiADg4V8IBF8IgJ8IgcgBSAIhYMgBYV8IAdCMokgB0IuiYUgB0IXiYV8QpnXu/zN6Z2kJ3wiESACQiSJIAJCHomFIAJCGYmFIAIgAyAEhYMgAyAEg4V8IAx8IgN8IgYgByAIhYMgCIV8IAZCMokgBkIuiYUgBkIXiYV8QqiR7Yzelq/YNHwiDCADQiSJIANCHomFIANCGYmFIAMgAiAEhYMgAiAEg4V8IA58IgR8IgUgBiAHhYMgB4V8IAVCMokgBUIuiYUgBUIXiYV8QuO0pa68loOOOXwiDiAEQiSJIARCHomFIARCGYmFIAQgAiADhYMgAiADg4V8IA18IgJ8Igh8IAUgNXwgBiAxfCAHIDR8IAggBSAGhYMgBoV8IAhCMokgCEIuiYUgCEIXiYV8QsuVhpquyarszgB8Ig0gAkIkiSACQh6JhSACQhmJhSACIAMgBIWDIAMgBIOFfCAQfCIDfCIHIAUgCIWDIAWFfCAHQjKJIAdCLomFIAdCF4mFfELzxo+798myztsAfCIQIANCJIkgA0IeiYUgA0IZiYUgAyACIASFgyACIASDhXwgEXwiBHwiBiAHIAiFgyAIhXwgBkIyiSAGQi6JhSAGQheJhXxCo/HKtb3+m5foAHwiESAEQiSJIARCHomFIARCGYmFIAQgAiADhYMgAiADg4V8IAx8IgJ8IgUgBiAHhYMgB4V8IAVCMokgBUIuiYUgBUIXiYV8Qvzlvu/l3eDH9AB8IgwgAkIkiSACQh6JhSACQhmJhSACIAMgBIWDIAMgBIOFfCAOfCIDfCIIfCAFIDd8IAYgOnwgByA2fCAIIAUgBoWDIAaFfCAIQjKJIAhCLomFIAhCF4mFfELg3tyY9O3Y0vgAfCIOIANCJIkgA0IeiYUgA0IZiYUgAyACIASFgyACIASDhXwgDXwiBHwiByAFIAiFgyAFhXwgB0IyiSAHQi6JhSAHQheJhXxC8tbCj8qCnuSEf3wiDSAEQiSJIARCHomFIARCGYmFIAQgAiADhYMgAiADg4V8IBB8IgJ8IgYgByAIhYMgCIV8IAZCMokgBkIuiYUgBkIXiYV8QuzzkNOBwcDjjH98IhAgAkIkiSACQh6JhSACQhmJhSACIAMgBIWDIAMgBIOFfCARfCIDfCIFIAYgB4WDIAeFfCAFQjKJIAVCLomFIAVCF4mFfEKovIybov+/35B/fCIRIANCJIkgA0IeiYUgA0IZiYUgAyACIASFgyACIASDhXwgDHwiBHwiCHwgBSA9fCAGIDx8IAcgOHwgCCAFIAaFgyAGhXwgCEIyiSAIQi6JhSAIQheJhXxC6fuK9L2dm6ikf3wiDCAEQiSJIARCHomFIARCGYmFIAQgAiADhYMgAiADg4V8IA58IgJ8IgcgBSAIhYMgBYV8IAdCMokgB0IuiYUgB0IXiYV8QpXymZb7/uj8vn98Ig4gAkIkiSACQh6JhSACQhmJhSACIAMgBIWDIAMgBIOFfCANfCIDfCIGIAcgCIWDIAiFfCAGQjKJIAZCLomFIAZCF4mFfEKrpsmbrp7euEZ8Ig0gA0IkiSADQh6JhSADQhmJhSADIAIgBIWDIAIgBIOFfCAQfCIEfCIFIAYgB4WDIAeFfCAFQjKJIAVCLomFIAVCF4mFfEKcw5nR7tnPk0p8IhAgBEIkiSAEQh6JhSAEQhmJhSAEIAIgA4WDIAIgA4OFfCARfCICfCIIfCAFIEd8IAYgSXwgByBGfCAIIAUgBoWDIAaFfCAIQjKJIAhCLomFIAhCF4mFfEKHhIOO8piuw1F8IhEgAkIkiSACQh6JhSACQhmJhSACIAMgBIWDIAMgBIOFfCAMfCIDfCIHIAUgCIWDIAWFfCAHQjKJIAdCLomFIAdCF4mFfEKe1oPv7Lqf7Wp8IgwgA0IkiSADQh6JhSADQhmJhSADIAIgBIWDIAIgBIOFfCAOfCIEfCIGIAcgCIWDIAiFfCAGQjKJIAZCLomFIAZCF4mFfEL4orvz/u/TvnV8Ig4gBEIkiSAEQh6JhSAEQhmJhSAEIAIgA4WDIAIgA4OFfCANfCICfCIFIAYgB4WDIAeFfCAFQjKJIAVCLomFIAVCF4mFfEK6392Qp/WZ+AZ8IhYgAkIkiSACQh6JhSACQhmJhSACIAMgBIWDIAMgBIOFfCAQfCIDfCIIfCA5QgeIIDlCOImFIDlCP4mFIDV8IEF8IEhCBoggSEIDiYUgSEItiYV8Ig0gBXwgBiBLfCAHIEh8IAggBSAGhYMgBoV8IAhCMokgCEIuiYUgCEIXiYV8QqaxopbauN+xCnwiECADQiSJIANCHomFIANCGYmFIAMgAiAEhYMgAiAEg4V8IBF8IgR8IgcgBSAIhYMgBYV8IAdCMokgB0IuiYUgB0IXiYV8Qq6b5PfLgOafEXwiESAEQiSJIARCHomFIARCGYmFIAQgAiADhYMgAiADg4V8IAx8IgJ8IgYgByAIhYMgCIV8IAZCMokgBkIuiYUgBkIXiYV8QpuO8ZjR5sK4G3wiGCACQiSJIAJCHomFIAJCGYmFIAIgAyAEhYMgAyAEg4V8IA58IgN8IgUgBiAHhYMgB4V8IAVCMokgBUIuiYUgBUIXiYV8QoT7kZjS/t3tKHwiGSADQiSJIANCHomFIANCGYmFIAMgAiAEhYMgAiAEg4V8IBZ8IgR8Igh8IDtCB4ggO0I4iYUgO0I/iYUgN3wgSnwgOkIHiCA6QjiJhSA6Qj+JhSA2fCBJfCANQgaIIA1CA4mFIA1CLYmFfCIMQgOJIAxCBoiFIAxCLYmFfCIOIAV8IAYgTnwgByAMfCAIIAUgBoWDIAaFfCAIQjKJIAhCLomFIAhCF4mFfEKTyZyGtO+q5TJ8IgcgBEIkiSAEQh6JhSAEQhmJhSAEIAIgA4WDIAIgA4OFfCAQfCICfCIGIAUgCIWDIAWFfCAGQjKJIAZCLomFIAZCF4mFfEK8/aauocGvzzx8IhAgAkIkiSACQh6JhSACQhmJhSACIAMgBIWDIAMgBIOFfCARfCIDfCIFIAYgCIWDIAiFfCAFQjKJIAVCLomFIAVCF4mFfELMmsDgyfjZjsMAfCIRIANCJIkgA0IeiYUgA0IZiYUgAyACIASFgyACIASDhXwgGHwiBHwiCCAFIAaFgyAGhXwgCEIyiSAIQi6JhSAIQheJhXxCtoX52eyX9eLMAHwiFiAEQiSJIARCHomFIARCGYmFIAQgAiADhYMgAiADg4V8IBl8IgJ8IgwgVHw3AzggACBVIAJCJIkgAkIeiYUgAkIZiYUgAiADIASFgyADIASDhXwgB3wiA0IkiSADQh6JhSADQhmJhSADIAIgBIWDIAIgBIOFfCAQfCIEQiSJIARCHomFIARCGYmFIAQgAiADhYMgAiADg4V8IBF8IgJCJIkgAkIeiYUgAkIZiYUgAiADIASFgyADIASDhXwgFnwiB3w3AxggACBQIAMgPEIHiCA8QjiJhSA8Qj+JhSA4fCBLfCAOQgaIIA5CA4mFIA5CLYmFfCIOIAZ8IAwgBSAIhYMgBYV8IAxCMokgDEIuiYUgDEIXiYV8Qqr8lePPs8q/2QB8IgN8IgZ8NwMwIAAgUiAHQiSJIAdCHomFIAdCGYmFIAcgAiAEhYMgAiAEg4V8IAN8IgN8NwMQIAAgTSA8ID1CB4ggPUI4iYUgPUI/iYV8IA18IE9CBoggT0IDiYUgT0ItiYV8IAV8IAYgCCAMhYMgCIV8IAZCMokgBkIuiYUgBkIXiYV8Quz129az9dvl3wB8IgUgBHwiBHw3AyggACBFIANCJIkgA0IeiYUgA0IZiYUgAyACIAeFgyACIAeDhXwgBXwiBXw3AwggACA9IEFCB4ggQUI4iYUgQUI/iYV8IEx8IA5CBoggDkIDiYUgDkItiYV8IAh8IAQgBiAMhYMgDIV8IARCMokgBEIuiYUgBEIXiYV8QpewndLEsYai7AB8IgQgAiAXfHw3AyAgACAVIAUgAyAHhYMgAyAHg4V8IAVCJIkgBUIeiYUgBUIZiYV8IAR8NwMAC6JBASN/IwBBQGoiHEE4akIANwMAIBxBMGpCADcDACAcQShqQgA3AwAgHEEgakIANwMAIBxBGGpCADcDACAcQRBqQgA3AwAgHEEIakIANwMAIBxCADcDACAAKAIcISMgACgCGCEhIAAoAhQhHyAAKAIQIR4gACgCDCEkIAAoAgghIiAAKAIEISAgACgCACEHIAIEQCABIAJBBnRqISUDQCAcIAEoAAAiAkEYdCACQQh0QYCA/AdxciACQQh2QYD+A3EgAkEYdnJyNgIAIBwgAUEEaigAACICQRh0IAJBCHRBgID8B3FyIAJBCHZBgP4DcSACQRh2cnI2AgQgHCABQQhqKAAAIgJBGHQgAkEIdEGAgPwHcXIgAkEIdkGA/gNxIAJBGHZycjYCCCAcIAFBDGooAAAiAkEYdCACQQh0QYCA/AdxciACQQh2QYD+A3EgAkEYdnJyNgIMIBwgAUEQaigAACICQRh0IAJBCHRBgID8B3FyIAJBCHZBgP4DcSACQRh2cnI2AhAgHCABQRRqKAAAIgJBGHQgAkEIdEGAgPwHcXIgAkEIdkGA/gNxIAJBGHZycjYCFCAcIAFBGGooAAAiAkEYdCACQQh0QYCA/AdxciACQQh2QYD+A3EgAkEYdnJyIhk2AhggHCABQRxqKAAAIgJBGHQgAkEIdEGAgPwHcXIgAkEIdkGA/gNxIAJBGHZyciIGNgIcIBwgAUEgaigAACICQRh0IAJBCHRBgID8B3FyIAJBCHZBgP4DcSACQRh2cnIiCjYCICAcIAFBJGooAAAiAkEYdCACQQh0QYCA/AdxciACQQh2QYD+A3EgAkEYdnJyIhE2AiQgHCABQShqKAAAIgJBGHQgAkEIdEGAgPwHcXIgAkEIdkGA/gNxIAJBGHZyciIQNgIoIBwgAUEsaigAACICQRh0IAJBCHRBgID8B3FyIAJBCHZBgP4DcSACQRh2cnIiFDYCLCAcIAFBMGooAAAiAkEYdCACQQh0QYCA/AdxciACQQh2QYD+A3EgAkEYdnJyIhU2AjAgHCABQTRqKAAAIgJBGHQgAkEIdEGAgPwHcXIgAkEIdkGA/gNxIAJBGHZyciIaNgI0IBwgAUE4aigAACICQRh0IAJBCHRBgID8B3FyIAJBCHZBgP4DcSACQRh2cnIiAjYCOCAcIAFBPGooAAAiG0EYdCAbQQh0QYCA/AdxciAbQQh2QYD+A3EgG0EYdnJyIhs2AjwgByAcKAIAIhggIyAfICFzIB5xICFzaiAeQRp3IB5BFXdzIB5BB3dzampBmN+olARqIgkgByAicSAHICBxIgsgICAicXNzIAdBHncgB0ETd3MgB0EKd3NqaiITQR53IBNBE3dzIBNBCndzIBMgByAgc3EgC3NqICEgHCgCBCIXaiAJICRqIgQgHiAfc3EgH3NqIARBGncgBEEVd3MgBEEHd3NqQZGJ3YkHaiILaiIJIBNxIgggByATcXMgByAJcXMgCUEedyAJQRN3cyAJQQp3c2ogHyAcKAIIIgVqIAsgImoiAyAEIB5zcSAec2ogA0EadyADQRV3cyADQQd3c2pBz/eDrntqIgtqIgxBHncgDEETd3MgDEEKd3MgDCAJIBNzcSAIc2ogHiAcKAIMIhZqIAsgIGoiCCADIARzcSAEc2ogCEEadyAIQRV3cyAIQQd3c2pBpbfXzX5qIg9qIgsgDHEiEiAJIAxxcyAJIAtxcyALQR53IAtBE3dzIAtBCndzaiAEIBwoAhAiDWogByAPaiIEIAMgCHNxIANzaiAEQRp3IARBFXdzIARBB3dzakHbhNvKA2oiB2oiD0EedyAPQRN3cyAPQQp3cyAPIAsgDHNxIBJzaiAcKAIUIg4gA2ogByATaiITIAQgCHNxIAhzaiATQRp3IBNBFXdzIBNBB3dzakHxo8TPBWoiA2oiByAPcSISIAsgD3FzIAcgC3FzIAdBHncgB0ETd3MgB0EKd3NqIAggGWogAyAJaiIDIAQgE3NxIARzaiADQRp3IANBFXdzIANBB3dzakGkhf6ReWoiCWoiCEEedyAIQRN3cyAIQQp3cyAIIAcgD3NxIBJzaiAEIAZqIAkgDGoiBCADIBNzcSATc2ogBEEadyAEQRV3cyAEQQd3c2pB1b3x2HpqIgxqIgkgCHEiEiAHIAhxcyAHIAlxcyAJQR53IAlBE3dzIAlBCndzaiAKIBNqIAsgDGoiEyADIARzcSADc2ogE0EadyATQRV3cyATQQd3c2pBmNWewH1qIgtqIgxBHncgDEETd3MgDEEKd3MgDCAIIAlzcSASc2ogAyARaiALIA9qIgMgBCATc3EgBHNqIANBGncgA0EVd3MgA0EHd3NqQYG2jZQBaiIPaiILIAxxIhIgCSAMcXMgCSALcXMgC0EedyALQRN3cyALQQp3c2ogBCAQaiAHIA9qIgQgAyATc3EgE3NqIARBGncgBEEVd3MgBEEHd3NqQb6LxqECaiIHaiIPQR53IA9BE3dzIA9BCndzIA8gCyAMc3EgEnNqIBMgFGogByAIaiITIAMgBHNxIANzaiATQRp3IBNBFXdzIBNBB3dzakHD+7GoBWoiCGoiByAPcSISIAsgD3FzIAcgC3FzIAdBHncgB0ETd3MgB0EKd3NqIAMgFWogCCAJaiIDIAQgE3NxIARzaiADQRp3IANBFXdzIANBB3dzakH0uvmVB2oiCWoiCEEedyAIQRN3cyAIQQp3cyAIIAcgD3NxIBJzaiAEIBpqIAkgDGoiBCADIBNzcSATc2ogBEEadyAEQRV3cyAEQQd3c2pB/uP6hnhqIgxqIgkgCHEiHSAHIAhxcyAHIAlxcyAJQR53IAlBE3dzIAlBCndzaiACIBNqIAsgDGoiDCADIARzcSADc2ogDEEadyAMQRV3cyAMQQd3c2pBp43w3nlqIgtqIhJBHncgEkETd3MgEkEKd3MgEiAIIAlzcSAdc2ogAyAbaiALIA9qIgMgBCAMc3EgBHNqIANBGncgA0EVd3MgA0EHd3NqQfTi74x8aiIPaiILIBJxIh0gCSAScXMgCSALcXMgC0EedyALQRN3cyALQQp3c2ogF0EDdiAXQRl3cyAXQQ53cyAYaiARaiACQQ93IAJBDXdzIAJBCnZzaiITIARqIAcgD2oiDyADIAxzcSAMc2ogD0EadyAPQRV3cyAPQQd3c2pBwdPtpH5qIgRqIhhBHncgGEETd3MgGEEKd3MgGCALIBJzcSAdc2ogBUEDdiAFQRl3cyAFQQ53cyAXaiAQaiAbQQ93IBtBDXdzIBtBCnZzaiIHIAxqIAQgCGoiCCADIA9zcSADc2ogCEEadyAIQRV3cyAIQQd3c2pBho/5/X5qIgxqIgQgGHEiHSALIBhxcyAEIAtxcyAEQR53IARBE3dzIARBCndzaiADIBZBA3YgFkEZd3MgFkEOd3MgBWogFGogE0EPdyATQQ13cyATQQp2c2oiA2ogCSAMaiIXIAggD3NxIA9zaiAXQRp3IBdBFXdzIBdBB3dzakHGu4b+AGoiDGoiBUEedyAFQRN3cyAFQQp3cyAFIAQgGHNxIB1zaiANQQN2IA1BGXdzIA1BDndzIBZqIBVqIAdBD3cgB0ENd3MgB0EKdnNqIgkgD2ogDCASaiISIAggF3NxIAhzaiASQRp3IBJBFXdzIBJBB3dzakHMw7KgAmoiD2oiDCAFcSIdIAQgBXFzIAQgDHFzIAxBHncgDEETd3MgDEEKd3NqIAggDkEDdiAOQRl3cyAOQQ53cyANaiAaaiADQQ93IANBDXdzIANBCnZzaiIIaiALIA9qIhYgEiAXc3EgF3NqIBZBGncgFkEVd3MgFkEHd3NqQe/YpO8CaiIPaiINQR53IA1BE3dzIA1BCndzIA0gBSAMc3EgHXNqIBlBA3YgGUEZd3MgGUEOd3MgDmogAmogCUEPdyAJQQ13cyAJQQp2c2oiCyAXaiAPIBhqIhcgEiAWc3EgEnNqIBdBGncgF0EVd3MgF0EHd3NqQaqJ0tMEaiIYaiIPIA1xIh0gDCANcXMgDCAPcXMgD0EedyAPQRN3cyAPQQp3c2ogEiAGQQN2IAZBGXdzIAZBDndzIBlqIBtqIAhBD3cgCEENd3MgCEEKdnNqIhJqIAQgGGoiGSAWIBdzcSAWc2ogGUEadyAZQRV3cyAZQQd3c2pB3NPC5QVqIhhqIg5BHncgDkETd3MgDkEKd3MgDiANIA9zcSAdc2ogCkEDdiAKQRl3cyAKQQ53cyAGaiATaiALQQ93IAtBDXdzIAtBCnZzaiIEIBZqIAUgGGoiFiAXIBlzcSAXc2ogFkEadyAWQRV3cyAWQQd3c2pB2pHmtwdqIgVqIhggDnEiHSAOIA9xcyAPIBhxcyAYQR53IBhBE3dzIBhBCndzaiAXIBFBA3YgEUEZd3MgEUEOd3MgCmogB2ogEkEPdyASQQ13cyASQQp2c2oiF2ogBSAMaiIGIBYgGXNxIBlzaiAGQRp3IAZBFXdzIAZBB3dzakHSovnBeWoiBWoiCkEedyAKQRN3cyAKQQp3cyAKIA4gGHNxIB1zaiAQQQN2IBBBGXdzIBBBDndzIBFqIANqIARBD3cgBEENd3MgBEEKdnNqIgwgGWogBSANaiIZIAYgFnNxIBZzaiAZQRp3IBlBFXdzIBlBB3dzakHtjMfBemoiDWoiBSAKcSIdIAogGHFzIAUgGHFzIAVBHncgBUETd3MgBUEKd3NqIBYgFEEDdiAUQRl3cyAUQQ53cyAQaiAJaiAXQQ93IBdBDXdzIBdBCnZzaiIWaiANIA9qIhEgBiAZc3EgBnNqIBFBGncgEUEVd3MgEUEHd3NqQcjPjIB7aiINaiIQQR53IBBBE3dzIBBBCndzIBAgBSAKc3EgHXNqIBVBA3YgFUEZd3MgFUEOd3MgFGogCGogDEEPdyAMQQ13cyAMQQp2c2oiDyAGaiANIA5qIgYgESAZc3EgGXNqIAZBGncgBkEVd3MgBkEHd3NqQcf/5fp7aiIOaiINIBBxIh0gBSAQcXMgBSANcXMgDUEedyANQRN3cyANQQp3c2ogGSAaQQN2IBpBGXdzIBpBDndzIBVqIAtqIBZBD3cgFkENd3MgFkEKdnNqIhlqIA4gGGoiFCAGIBFzcSARc2ogFEEadyAUQRV3cyAUQQd3c2pB85eAt3xqIg5qIhVBHncgFUETd3MgFUEKd3MgFSANIBBzcSAdc2ogAkEDdiACQRl3cyACQQ53cyAaaiASaiAPQQ93IA9BDXdzIA9BCnZzaiIYIBFqIAogDmoiCiAGIBRzcSAGc2ogCkEadyAKQRV3cyAKQQd3c2pBx6KerX1qIhFqIg4gFXEiGiANIBVxcyANIA5xcyAOQR53IA5BE3dzIA5BCndzaiAbQQN2IBtBGXdzIBtBDndzIAJqIARqIBlBD3cgGUENd3MgGUEKdnNqIgIgBmogBSARaiIGIAogFHNxIBRzaiAGQRp3IAZBFXdzIAZBB3dzakHRxqk2aiIFaiIRQR53IBFBE3dzIBFBCndzIBEgDiAVc3EgGnNqIBNBA3YgE0EZd3MgE0EOd3MgG2ogF2ogGEEPdyAYQQ13cyAYQQp2c2oiGyAUaiAFIBBqIhAgBiAKc3EgCnNqIBBBGncgEEEVd3MgEEEHd3NqQefSpKEBaiIUaiIFIBFxIhogDiARcXMgBSAOcXMgBUEedyAFQRN3cyAFQQp3c2ogB0EDdiAHQRl3cyAHQQ53cyATaiAMaiACQQ93IAJBDXdzIAJBCnZzaiITIApqIA0gFGoiCiAGIBBzcSAGc2ogCkEadyAKQRV3cyAKQQd3c2pBhZXcvQJqIg1qIhRBHncgFEETd3MgFEEKd3MgFCAFIBFzcSAac2ogA0EDdiADQRl3cyADQQ53cyAHaiAWaiAbQQ93IBtBDXdzIBtBCnZzaiIHIAZqIA0gFWoiBiAKIBBzcSAQc2ogBkEadyAGQRV3cyAGQQd3c2pBuMLs8AJqIhVqIg0gFHEiGiAFIBRxcyAFIA1xcyANQR53IA1BE3dzIA1BCndzaiAJQQN2IAlBGXdzIAlBDndzIANqIA9qIBNBD3cgE0ENd3MgE0EKdnNqIgMgEGogDiAVaiIQIAYgCnNxIApzaiAQQRp3IBBBFXdzIBBBB3dzakH827HpBGoiDmoiFUEedyAVQRN3cyAVQQp3cyAVIA0gFHNxIBpzaiAIQQN2IAhBGXdzIAhBDndzIAlqIBlqIAdBD3cgB0ENd3MgB0EKdnNqIgkgCmogDiARaiIKIAYgEHNxIAZzaiAKQRp3IApBFXdzIApBB3dzakGTmuCZBWoiEWoiDiAVcSIaIA0gFXFzIA0gDnFzIA5BHncgDkETd3MgDkEKd3NqIAtBA3YgC0EZd3MgC0EOd3MgCGogGGogA0EPdyADQQ13cyADQQp2c2oiCCAGaiAFIBFqIgYgCiAQc3EgEHNqIAZBGncgBkEVd3MgBkEHd3NqQdTmqagGaiIFaiIRQR53IBFBE3dzIBFBCndzIBEgDiAVc3EgGnNqIBJBA3YgEkEZd3MgEkEOd3MgC2ogAmogCUEPdyAJQQ13cyAJQQp2c2oiCyAQaiAFIBRqIhAgBiAKc3EgCnNqIBBBGncgEEEVd3MgEEEHd3NqQbuVqLMHaiIUaiIFIBFxIhogDiARcXMgBSAOcXMgBUEedyAFQRN3cyAFQQp3c2ogBEEDdiAEQRl3cyAEQQ53cyASaiAbaiAIQQ93IAhBDXdzIAhBCnZzaiISIApqIA0gFGoiCiAGIBBzcSAGc2ogCkEadyAKQRV3cyAKQQd3c2pBrpKLjnhqIg1qIhRBHncgFEETd3MgFEEKd3MgFCAFIBFzcSAac2ogF0EDdiAXQRl3cyAXQQ53cyAEaiATaiALQQ93IAtBDXdzIAtBCnZzaiIEIAZqIA0gFWoiBiAKIBBzcSAQc2ogBkEadyAGQRV3cyAGQQd3c2pBhdnIk3lqIhVqIg0gFHEiGiAFIBRxcyAFIA1xcyANQR53IA1BE3dzIA1BCndzaiAMQQN2IAxBGXdzIAxBDndzIBdqIAdqIBJBD3cgEkENd3MgEkEKdnNqIhcgEGogDiAVaiIQIAYgCnNxIApzaiAQQRp3IBBBFXdzIBBBB3dzakGh0f+VemoiDmoiFUEedyAVQRN3cyAVQQp3cyAVIA0gFHNxIBpzaiAWQQN2IBZBGXdzIBZBDndzIAxqIANqIARBD3cgBEENd3MgBEEKdnNqIgwgCmogDiARaiIKIAYgEHNxIAZzaiAKQRp3IApBFXdzIApBB3dzakHLzOnAemoiEWoiDiAVcSIaIA0gFXFzIA0gDnFzIA5BHncgDkETd3MgDkEKd3NqIA9BA3YgD0EZd3MgD0EOd3MgFmogCWogF0EPdyAXQQ13cyAXQQp2c2oiFiAGaiAFIBFqIgYgCiAQc3EgEHNqIAZBGncgBkEVd3MgBkEHd3NqQfCWrpJ8aiIFaiIRQR53IBFBE3dzIBFBCndzIBEgDiAVc3EgGnNqIBlBA3YgGUEZd3MgGUEOd3MgD2ogCGogDEEPdyAMQQ13cyAMQQp2c2oiDyAQaiAFIBRqIhAgBiAKc3EgCnNqIBBBGncgEEEVd3MgEEEHd3NqQaOjsbt8aiIUaiIFIBFxIhogDiARcXMgBSAOcXMgBUEedyAFQRN3cyAFQQp3c2ogGEEDdiAYQRl3cyAYQQ53cyAZaiALaiAWQQ93IBZBDXdzIBZBCnZzaiIZIApqIA0gFGoiCiAGIBBzcSAGc2ogCkEadyAKQRV3cyAKQQd3c2pBmdDLjH1qIg1qIhRBHncgFEETd3MgFEEKd3MgFCAFIBFzcSAac2ogAkEDdiACQRl3cyACQQ53cyAYaiASaiAPQQ93IA9BDXdzIA9BCnZzaiIYIAZqIA0gFWoiBiAKIBBzcSAQc2ogBkEadyAGQRV3cyAGQQd3c2pBpIzktH1qIhVqIg0gFHEiGiAFIBRxcyAFIA1xcyANQR53IA1BE3dzIA1BCndzaiAbQQN2IBtBGXdzIBtBDndzIAJqIARqIBlBD3cgGUENd3MgGUEKdnNqIgIgEGogDiAVaiIQIAYgCnNxIApzaiAQQRp3IBBBFXdzIBBBB3dzakGF67igf2oiDmoiFUEedyAVQRN3cyAVQQp3cyAVIA0gFHNxIBpzaiATQQN2IBNBGXdzIBNBDndzIBtqIBdqIBhBD3cgGEENd3MgGEEKdnNqIhsgCmogDiARaiIKIAYgEHNxIAZzaiAKQRp3IApBFXdzIApBB3dzakHwwKqDAWoiEWoiDiAVcSIaIA0gFXFzIA0gDnFzIA5BHncgDkETd3MgDkEKd3NqIAdBA3YgB0EZd3MgB0EOd3MgE2ogDGogAkEPdyACQQ13cyACQQp2c2oiEyAGaiAFIBFqIgUgCiAQc3EgEHNqIAVBGncgBUEVd3MgBUEHd3NqQZaCk80BaiIRaiIGQR53IAZBE3dzIAZBCndzIAYgDiAVc3EgGnNqIBAgA0EDdiADQRl3cyADQQ53cyAHaiAWaiAbQQ93IBtBDXdzIBtBCnZzaiIQaiARIBRqIhEgBSAKc3EgCnNqIBFBGncgEUEVd3MgEUEHd3NqQYjY3fEBaiIUaiIHIAZxIhogBiAOcXMgByAOcXMgB0EedyAHQRN3cyAHQQp3c2ogCiAJQQN2IAlBGXdzIAlBDndzIANqIA9qIBNBD3cgE0ENd3MgE0EKdnNqIgpqIA0gFGoiAyAFIBFzcSAFc2ogA0EadyADQRV3cyADQQd3c2pBzO6hugJqIh1qIg1BHncgDUETd3MgDUEKd3MgDSAGIAdzcSAac2ogCEEDdiAIQRl3cyAIQQ53cyAJaiAZaiAQQQ93IBBBDXdzIBBBCnZzaiIUIAVqIBUgHWoiBSADIBFzcSARc2ogBUEadyAFQRV3cyAFQQd3c2pBtfnCpQNqIhVqIgkgDXEiGiAHIA1xcyAHIAlxcyAJQR53IAlBE3dzIAlBCndzaiARIAtBA3YgC0EZd3MgC0EOd3MgCGogGGogCkEPdyAKQQ13cyAKQQp2c2oiEWogDiAVaiIIIAMgBXNxIANzaiAIQRp3IAhBFXdzIAhBB3dzakGzmfDIA2oiHWoiDkEedyAOQRN3cyAOQQp3cyAOIAkgDXNxIBpzaiASQQN2IBJBGXdzIBJBDndzIAtqIAJqIBRBD3cgFEENd3MgFEEKdnNqIhUgA2ogBiAdaiIDIAUgCHNxIAVzaiADQRp3IANBFXdzIANBB3dzakHK1OL2BGoiGmoiCyAOcSIdIAkgDnFzIAkgC3FzIAtBHncgC0ETd3MgC0EKd3NqIARBA3YgBEEZd3MgBEEOd3MgEmogG2ogEUEPdyARQQ13cyARQQp2c2oiBiAFaiAHIBpqIhIgAyAIc3EgCHNqIBJBGncgEkEVd3MgEkEHd3NqQc+U89wFaiIHaiIFQR53IAVBE3dzIAVBCndzIAUgCyAOc3EgHXNqIBdBA3YgF0EZd3MgF0EOd3MgBGogE2ogFUEPdyAVQQ13cyAVQQp2c2oiGiAIaiAHIA1qIgQgAyASc3EgA3NqIARBGncgBEEVd3MgBEEHd3NqQfPfucEGaiIIaiIHIAVxIg0gBSALcXMgByALcXMgB0EedyAHQRN3cyAHQQp3c2ogDEEDdiAMQRl3cyAMQQ53cyAXaiAQaiAGQQ93IAZBDXdzIAZBCnZzaiIXIANqIAggCWoiAyAEIBJzcSASc2ogA0EadyADQRV3cyADQQd3c2pB7oW+pAdqIglqIghBHncgCEETd3MgCEEKd3MgCCAFIAdzcSANc2ogFkEDdiAWQRl3cyAWQQ53cyAMaiAKaiAaQQ93IBpBDXdzIBpBCnZzaiINIBJqIAkgDmoiDCADIARzcSAEc2ogDEEadyAMQRV3cyAMQQd3c2pB78aVxQdqIhJqIgkgCHEiDiAHIAhxcyAHIAlxcyAJQR53IAlBE3dzIAlBCndzaiAPQQN2IA9BGXdzIA9BDndzIBZqIBRqIBdBD3cgF0ENd3MgF0EKdnNqIhYgBGogCyASaiIEIAMgDHNxIANzaiAEQRp3IARBFXdzIARBB3dzakGU8KGmeGoiC2oiEkEedyASQRN3cyASQQp3cyASIAggCXNxIA5zaiAZQQN2IBlBGXdzIBlBDndzIA9qIBFqIA1BD3cgDUENd3MgDUEKdnNqIg8gA2ogBSALaiIDIAQgDHNxIAxzaiADQRp3IANBFXdzIANBB3dzakGIhJzmeGoiDWoiCyAScSIOIAkgEnFzIAkgC3FzIAtBHncgC0ETd3MgC0EKd3NqIBhBA3YgGEEZd3MgGEEOd3MgGWogFWogFkEPdyAWQQ13cyAWQQp2c2oiBSAMaiAHIA1qIgcgAyAEc3EgBHNqIAdBGncgB0EVd3MgB0EHd3NqQfr/+4V5aiIWaiIMQR53IAxBE3dzIAxBCndzIAwgCyASc3EgDnNqIAJBA3YgAkEZd3MgAkEOd3MgGGogBmogD0EPdyAPQQ13cyAPQQp2c2oiDyAEaiAIIBZqIgQgAyAHc3EgA3NqIARBGncgBEEVd3MgBEEHd3NqQevZwaJ6aiIYaiIIIAxxIhYgCyAMcXMgCCALcXMgCEEedyAIQRN3cyAIQQp3c2ogAiAbQQN2IBtBGXdzIBtBDndzaiAaaiAFQQ93IAVBDXdzIAVBCnZzaiADaiAJIBhqIgIgBCAHc3EgB3NqIAJBGncgAkEVd3MgAkEHd3NqQffH5vd7aiIDaiIJIAggDHNxIBZzaiAJQR53IAlBE3dzIAlBCndzaiAbIBNBA3YgE0EZd3MgE0EOd3NqIBdqIA9BD3cgD0ENd3MgD0EKdnNqIAdqIAMgEmoiGyACIARzcSAEc2ogG0EadyAbQRV3cyAbQQd3c2pB8vHFs3xqIhNqIQcgCSAgaiEgIAggImohIiAMICRqISQgCyAeaiATaiEeIBsgH2ohHyACICFqISEgBCAjaiEjIAFBQGsiASAlRw0ACwsgACAjNgIcIAAgITYCGCAAIB82AhQgACAeNgIQIAAgJDYCDCAAICI2AgggACAgNgIEIAAgBzYCAAuXOgEMfyMAQaAFayICJAAgAiABNgIEIAIgADYCAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAn8CQCABQX1qIgNBBksNAAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCADQQFrDgYCEgMSBAEACyAAQYCAwABGDQQgAEGAgMAAQQMQgwFFDQQgAEGogMAARg0FIABBqIDAAEEDEIMBRQ0FIABB0IDAAEcEQCAAQdCAwABBAxCDAQ0SCyACQZIEakIANwEAIAJBmgRqQQA7AQAgAkGcBGpCADcCACACQaQEakIANwIAIAJBrARqQgA3AgAgAkG0BGpCADcCACACQbwEakIANwIAIAJBxARqQQA6AAAgAkHFBGpBADYAACACQckEakEAOwAAIAJBywRqQQA6AAAgAkHAADYCiAQgAkEAOwGMBCACQQA2AY4EIAJBmAFqIAJBiARqQcQAEIsBGiACQagDaiIEIAJB1AFqKQIANwMAIAJBoANqIgUgAkHMAWopAgA3AwAgAkGYA2oiCSACQcQBaikCADcDACACQZADaiIKIAJBvAFqKQIANwMAIAJBiANqIgYgAkG0AWopAgA3AwAgAkGAA2oiByACQawBaikCADcDACACQfgCaiIIIAJBpAFqKQIANwMAIAIgAikCnAE3A/ACQeAAQQgQoQEiA0UNGSADQQA2AgggA0IANwMAIAMgAikD8AI3AgwgA0EUaiAIKQMANwIAIANBHGogBykDADcCACADQSRqIAYpAwA3AgAgA0EsaiAKKQMANwIAIANBNGogCSkDADcCACADQTxqIAUpAwA3AgAgA0HEAGogBCkDADcCACADQdQAakHIl8AAKQIANwIAIANBwJfAACkCADcCTEHUgMAAIQRBAAwSCyAAQfiAwABGDQUgAEH4gMAAQQkQgwFFDQUgAEGogcAARg0GIABBqIHAAEEJEIMBRQ0GIABB4ITAAEYNDSAAQeCEwAAgARCDAUUNDSAAQZCFwABGDQ4gAEGQhcAAIAEQgwFFDQ4gAEHAhcAARg0PIABBwIXAACABEIMBRQ0PIABB8IXAAEcEQCAAQfCFwAAgARCDAQ0RCyACQZgBakEAQcgBEJEBGiACQf4CakIANwEAIAJBhgNqQgA3AQAgAkGOA2pCADcBACACQZYDakIANwEAIAJBngNqQgA3AQAgAkGmA2pCADcBACACQa4DakIANwEAIAJBtgNqQQA2AQAgAkG6A2pBADsBACACQQA7AfQCIAJCADcB9gIgAkHIADYC8AIgAkGIBGogAkHwAmpBzAAQiwEaIAJBCGogAkGIBGpBBHJByAAQiwEaQZgCQQgQoQEiA0UNHiADIAJBmAFqQcgBEIsBIgRBADYCyAEgBEHMAWogAkEIakHIABCLARpB/IXAACEEQQAMEQsgAEHYgcAARwRAIAAoAABB89CFiwNHDRALIAJBkgRqQgA3AQAgAkGaBGpBADsBACACQZwEakIANwIAIAJBpARqQgA3AgAgAkGsBGpCADcCACACQbQEakIANwIAIAJBvARqQgA3AgAgAkHEBGpBADoAACACQcUEakEANgAAIAJByQRqQQA7AAAgAkHLBGpBADoAACACQcAANgKIBCACQQA7AYwEIAJBADYBjgQgAkGYAWogAkGIBGpBxAAQiwEaIAJBqANqIgQgAkHUAWopAgA3AwAgAkGgA2oiBSACQcwBaikCADcDACACQZgDaiIJIAJBxAFqKQIANwMAIAJBkANqIgogAkG8AWopAgA3AwAgAkGIA2oiBiACQbQBaikCADcDACACQYADaiIHIAJBrAFqKQIANwMAIAJB+AJqIgggAkGkAWopAgA3AwAgAiACKQKcATcD8AJB4ABBCBChASIDRQ0XIANCADcDACADQQA2AhwgAyACKQPwAjcDICADQfiXwAApAwA3AwggA0EQakGAmMAAKQMANwMAIANBGGpBiJjAACgCADYCACADQShqIAgpAwA3AwAgA0EwaiAHKQMANwMAIANBOGogBikDADcDACADQUBrIAopAwA3AwAgA0HIAGogCSkDADcDACADQdAAaiAFKQMANwMAIANB2ABqIAQpAwA3AwBB3IHAACEEQQAMEAsgAEGAgsAARg0FIABBgILAAEEGEIMBRQ0FIABBrILAAEYNBiAAQayCwABBBhCDAUUNBiAAQdiCwABGDQcgAEHYgsAAQQYQgwFFDQcgAEGEg8AARwRAIABBhIPAAEEGEIMBDQ8LIAJBADYCiAQgAkGIBGpBBHIhBEEAIQMDQCADIARqQQA6AAAgAiACKAKIBEEBajYCiAQgA0EBaiIDQYABRw0ACyACQZgBaiACQYgEakGEARCLARogAkHwAmogAkGYAWpBBHJBgAEQiwEaQdgBQQgQoQEiA0UNGCADQgA3AwggA0IANwMAIANBADYCUCADQZCZwAApAwA3AxAgA0EYakGYmcAAKQMANwMAIANBIGpBoJnAACkDADcDACADQShqQaiZwAApAwA3AwAgA0EwakGwmcAAKQMANwMAIANBOGpBuJnAACkDADcDACADQUBrQcCZwAApAwA3AwAgA0HIAGpByJnAACkDADcDACADQdQAaiACQfACakGAARCLARpBjIPAACEEQQAMDwsgAEGwg8AARg0HIAApAABC89CFm9PFjJk0UQ0HIABB3IPAAEYNCCAAKQAAQvPQhZvTxcyaNlENCCAAQYiEwABGDQkgACkAAELz0IWb0+WMnDRRDQkgAEG0hMAARwRAIAApAABC89CFm9OlzZgyUg0OCyACQZgBakEAQcgBEJEBGiACQf4CakIANwEAIAJBhgNqQgA3AQAgAkGOA2pCADcBACACQZYDakIANwEAIAJBngNqQgA3AQAgAkGmA2pCADcBACACQa4DakIANwEAIAJBtgNqQQA2AQAgAkG6A2pBADsBACACQQA7AfQCIAJCADcB9gIgAkHIADYC8AIgAkGIBGogAkHwAmpBzAAQiwEaIAJBCGogAkGIBGpBBHJByAAQiwEaQZgCQQgQoQEiA0UNGyADIAJBmAFqQcgBEIsBIgRBADYCyAEgBEHMAWogAkEIakHIABCLARpBvITAACEEQQAMDgsgAkGSBGpCADcBACACQZoEakEAOwEAIAJBEDYCiAQgAkEAOwGMBCACQQA2AY4EIAJBqAFqIgMgAkGYBGoiBCgCADYCACACQaABaiIJIAJBkARqIgUpAwA3AwAgAkHoAmoiBiACQaQBaikCADcDACACIAIpA4gENwOYASACIAIpApwBNwPgAiACQcABaiIHQgA3AwAgAkG4AWoiCEIANwMAIAJBsAFqIg1CADcDACADQgA3AwAgCUIANwMAIAJCADcDmAEgAkH6AmpCADcBACACQYIDakEAOwEAIAJBEDYC8AIgAkEAOwH0AiACQQA2AfYCIAQgAkGAA2ooAgA2AgAgBSACQfgCaiIKKQMANwMAIAJBEGoiCyACQZQEaikCADcDACACIAIpA/ACNwOIBCACIAIpAowENwMIIAJB0AFqIgwgCykDADcDACACIAIpAwg3A8gBIAogBikDADcDACACIAIpA+ACNwPwAiACQcAEaiIGIAwpAwA3AwAgAkG4BGoiCyACKQPIATcDACACQbAEaiIMIAcpAwA3AwAgAkGoBGoiByAIKQMANwMAIAJBoARqIgggDSkDADcDACAEIAMpAwA3AwAgBSAJKQMANwMAIAIgAikDmAE3A4gEQdQAQQQQoQEiA0UNDiADQQA2AgAgAyACKQPwAjcCBCADIAIpA4gENwIUIANBDGogCikDADcCACADQRxqIAUpAwA3AgAgA0EkaiAEKQMANwIAIANBLGogCCkDADcCACADQTRqIAcpAwA3AgAgA0E8aiAMKQMANwIAIANBxABqIAspAwA3AgAgA0HMAGogBikDADcCAEGEgMAAIQRBAAwNCyACQZIEakIANwEAIAJBmgRqQQA7AQAgAkGcBGpCADcCACACQaQEakIANwIAIAJBrARqQgA3AgAgAkG0BGpCADcCACACQbwEakIANwIAIAJBxARqQQA6AAAgAkHFBGpBADYAACACQckEakEAOwAAIAJBywRqQQA6AAAgAkHAADYCiAQgAkEAOwGMBCACQQA2AY4EIAJBmAFqIAJBiARqQcQAEIsBGiACQagDaiIEIAJB1AFqKQIANwMAIAJBoANqIgUgAkHMAWopAgA3AwAgAkGYA2oiCSACQcQBaikCADcDACACQZADaiIKIAJBvAFqKQIANwMAIAJBiANqIgYgAkG0AWopAgA3AwAgAkGAA2oiByACQawBaikCADcDACACQfgCaiIIIAJBpAFqKQIANwMAIAIgAikCnAE3A/ACQeAAQQgQoQEiA0UNEyADQQA2AgggA0IANwMAIAMgAikD8AI3AgwgA0EUaiAIKQMANwIAIANBHGogBykDADcCACADQSRqIAYpAwA3AgAgA0EsaiAKKQMANwIAIANBNGogCSkDADcCACADQTxqIAUpAwA3AgAgA0HEAGogBCkDADcCACADQdQAakHIl8AAKQIANwIAIANBwJfAACkCADcCTEGsgMAAIQRBAAwMCyACQZIEakIANwEAIAJBmgRqQQA7AQAgAkGcBGpCADcCACACQaQEakIANwIAIAJBrARqQgA3AgAgAkG0BGpCADcCACACQbwEakIANwIAIAJBxARqQQA6AAAgAkHFBGpBADYAACACQckEakEAOwAAIAJBywRqQQA6AAAgAkHAADYCiAQgAkEAOwGMBCACQQA2AY4EIAJBmAFqIAJBiARqQcQAEIsBGiACQagDaiIEIAJB1AFqKQIANwMAIAJBoANqIgUgAkHMAWopAgA3AwAgAkGYA2oiCSACQcQBaikCADcDACACQZADaiIKIAJBvAFqKQIANwMAIAJBiANqIgYgAkG0AWopAgA3AwAgAkGAA2oiByACQawBaikCADcDACACQfgCaiIIIAJBpAFqKQIANwMAIAIgAikCnAE3A/ACQeAAQQgQoQEiA0UNEiADQgA3AwAgA0EANgIcIAMgAikD8AI3AyAgA0H4l8AAKQMANwMIIANBEGpBgJjAACkDADcDACADQRhqQYiYwAAoAgA2AgAgA0EoaiAIKQMANwMAIANBMGogBykDADcDACADQThqIAYpAwA3AwAgA0FAayAKKQMANwMAIANByABqIAkpAwA3AwAgA0HQAGogBSkDADcDACADQdgAaiAEKQMANwMAQYSBwAAhBEEADAsLIAJBkgRqQgA3AQAgAkGaBGpBADsBACACQZwEakIANwIAIAJBpARqQgA3AgAgAkGsBGpCADcCACACQbQEakIANwIAIAJBvARqQgA3AgAgAkHEBGpBADoAACACQcUEakEANgAAIAJByQRqQQA7AAAgAkHLBGpBADoAACACQcAANgKIBCACQQA7AYwEIAJBADYBjgQgAkGYAWogAkGIBGpBxAAQiwEaIAJBqANqIgQgAkHUAWopAgA3AwAgAkGgA2oiBSACQcwBaikCADcDACACQZgDaiIJIAJBxAFqKQIANwMAIAJBkANqIgogAkG8AWopAgA3AwAgAkGIA2oiBiACQbQBaikCADcDACACQYADaiIHIAJBrAFqKQIANwMAIAJB+AJqIgggAkGkAWopAgA3AwAgAiACKQKcATcD8AJB+ABBCBChASIDRQ0MIANCADcDACADQQA2AjAgAyACKQPwAjcCNCADQdCXwAApAwA3AwggA0EQakHYl8AAKQMANwMAIANBGGpB4JfAACkDADcDACADQSBqQeiXwAApAwA3AwAgA0EoakHwl8AAKQMANwMAIANBPGogCCkDADcCACADQcQAaiAHKQMANwIAIANBzABqIAYpAwA3AgAgA0HUAGogCikDADcCACADQdwAaiAJKQMANwIAIANB5ABqIAUpAwA3AgAgA0HsAGogBCkDADcCAEG0gcAAIQRBAAwKCyACQZIEakIANwEAIAJBmgRqQQA7AQAgAkGcBGpCADcCACACQaQEakIANwIAIAJBrARqQgA3AgAgAkG0BGpCADcCACACQbwEakIANwIAIAJBxARqQQA6AAAgAkHFBGpBADYAACACQckEakEAOwAAIAJBywRqQQA6AAAgAkHAADYCiAQgAkEAOwGMBCACQQA2AY4EIAJBmAFqIAJBiARqQcQAEIsBGiACQagDaiIEIAJB1AFqKQIANwMAIAJBoANqIgUgAkHMAWopAgA3AwAgAkGYA2oiCSACQcQBaikCADcDACACQZADaiIKIAJBvAFqKQIANwMAIAJBiANqIgYgAkG0AWopAgA3AwAgAkGAA2oiByACQawBaikCADcDACACQfgCaiIIIAJBpAFqKQIANwMAIAIgAikCnAE3A/ACQfAAQQgQoQEiA0UNESADQQA2AgggA0IANwMAIAMgAikD8AI3AgwgA0EUaiAIKQMANwIAIANBHGogBykDADcCACADQSRqIAYpAwA3AgAgA0EsaiAKKQMANwIAIANBNGogCSkDADcCACADQTxqIAUpAwA3AgAgA0HEAGogBCkDADcCACADQeQAakGkmMAAKQIANwIAIANB3ABqQZyYwAApAgA3AgAgA0HUAGpBlJjAACkCADcCACADQYyYwAApAgA3AkxBiILAACEEQQAMCQsgAkGSBGpCADcBACACQZoEakEAOwEAIAJBnARqQgA3AgAgAkGkBGpCADcCACACQawEakIANwIAIAJBtARqQgA3AgAgAkG8BGpCADcCACACQcQEakEAOgAAIAJBxQRqQQA2AAAgAkHJBGpBADsAACACQcsEakEAOgAAIAJBwAA2AogEIAJBADsBjAQgAkEANgGOBCACQZgBaiACQYgEakHEABCLARogAkGoA2oiBCACQdQBaikCADcDACACQaADaiIFIAJBzAFqKQIANwMAIAJBmANqIgkgAkHEAWopAgA3AwAgAkGQA2oiCiACQbwBaikCADcDACACQYgDaiIGIAJBtAFqKQIANwMAIAJBgANqIgcgAkGsAWopAgA3AwAgAkH4AmoiCCACQaQBaikCADcDACACIAIpApwBNwPwAkHwAEEIEKEBIgNFDRAgA0EANgIIIANCADcDACADIAIpA/ACNwIMIANBFGogCCkDADcCACADQRxqIAcpAwA3AgAgA0EkaiAGKQMANwIAIANBLGogCikDADcCACADQTRqIAkpAwA3AgAgA0E8aiAFKQMANwIAIANBxABqIAQpAwA3AgAgA0HkAGpBxJjAACkCADcCACADQdwAakG8mMAAKQIANwIAIANB1ABqQbSYwAApAgA3AgAgA0GsmMAAKQIANwJMQbSCwAAhBEEADAgLIAJBADYCiAQgAkGIBGpBBHIhBEEAIQMDQCADIARqQQA6AAAgAiACKAKIBEEBajYCiAQgA0EBaiIDQYABRw0ACyACQZgBaiACQYgEakGEARCLARogAkHwAmogAkGYAWpBBHJBgAEQiwEaQdgBQQgQoQEiA0UNECADQgA3AwggA0IANwMAIANBADYCUCADQdCYwAApAwA3AxAgA0EYakHYmMAAKQMANwMAIANBIGpB4JjAACkDADcDACADQShqQeiYwAApAwA3AwAgA0EwakHwmMAAKQMANwMAIANBOGpB+JjAACkDADcDACADQUBrQYCZwAApAwA3AwAgA0HIAGpBiJnAACkDADcDACADQdQAaiACQfACakGAARCLARpB4ILAACEEQQAMBwsgAkGYAWpBAEHIARCRARogAkEANgLwAkEEIQMDQCACQfACaiADakEAOgAAIAIgAigC8AJBAWo2AvACIANBAWoiA0GUAUcNAAsgAkGIBGogAkHwAmpBlAEQiwEaIAJBCGogAkGIBGpBBHJBkAEQiwEaQeACQQgQoQEiA0UNECADIAJBmAFqQcgBEIsBIgRBADYCyAEgBEHMAWogAkEIakGQARCLARpBuIPAACEEQQAMBgsgAkGYAWpBAEHIARCRARogAkEANgLwAkEEIQMDQCACQfACaiADakEAOgAAIAIgAigC8AJBAWo2AvACIANBAWoiA0GMAUcNAAsgAkGIBGogAkHwAmpBjAEQiwEaIAJBCGogAkGIBGpBBHJBiAEQiwEaQdgCQQgQoQEiA0UNECADIAJBmAFqQcgBEIsBIgRBADYCyAEgBEHMAWogAkEIakGIARCLARpB5IPAACEEQQAMBQsgAkGYAWpBAEHIARCRARogAkEANgLwAkEEIQMDQCACQfACaiADakEAOgAAIAIgAigC8AJBAWo2AvACIANBAWoiA0HsAEcNAAsgAkGIBGogAkHwAmpB7AAQiwEaIAJBCGogAkGIBGpBBHJB6AAQiwEaQbgCQQgQoQEiA0UNECADIAJBmAFqQcgBEIsBIgRBADYCyAEgBEHMAWogAkEIakHoABCLARpBkITAACEEQQAMBAsgAkGYAWpBAEHIARCRARogAkEA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aiIBIAIgBHNzakHglu21f2pBEHcgAWoiAyABcyACIApqIAEgBHMgA3NqQfD4/vV7akEXdyADaiICc2pBxv3txAJqQQR3IAJqIgRqIAMgC2ogASATaiACIANzIARzakH6z4TVfmpBC3cgBGoiASACIARzc2pBheG8p31qQRB3IAFqIgMgAXMgAiAGaiABIARzIANzakGFuqAkakEXdyADaiICc2pBuaDTzn1qQQR3IAJqIgRqIAIgDGogASAJaiACIANzIARzakHls+62fmpBC3cgBGoiASAEcyADIBJqIAIgBHMgAXNqQfj5if0BakEQdyABaiICc2pB5ayxpXxqQRd3IAJqIgMgAUF/c3IgAnNqQcTEpKF/akEGdyADaiIEaiADIBVqIAIgCGogASAOaiAEIAJBf3NyIANzakGX/6uZBGpBCncgBGoiASADQX9zciAEc2pBp8fQ3HpqQQ93IAFqIgIgBEF/c3IgAXNqQbnAzmRqQRV3IAJqIgMgAUF/c3IgAnNqQcOz7aoGakEGdyADaiIEaiADIBRqIAIgCmogASALaiAEIAJBf3NyIANzakGSmbP4eGpBCncgBGoiASADQX9zciAEc2pB/ei/f2pBD3cgAWoiAiAEQX9zciABc2pB0buRrHhqQRV3IAJqIgMgAUF/c3IgAnNqQc/8of0GakEGdyADaiIEaiADIBFqIAIgBmogASASaiAEIAJBf3NyIANzakHgzbNxakEKdyAEaiIBIANBf3NyIARzakGUhoWYempBD3cgAWoiAiAEQX9zciABc2pBoaOg8ARqQRV3IAJqIgMgAUF/c3IgAnNqQYL9zbp/akEGdyADaiIEIAAoAgBqNgIAIAAgASAQaiAEIAJBf3NyIANzakG15Ovpe2pBCncgBGoiASAAKAIMajYCDCAAIAIgDGogASADQX9zciAEc2pBu6Xf1gJqQQ93IAFqIgIgACgCCGo2AgggACACIAAoAgRqIAMgD2ogAiAEQX9zciABc2pBkaeb3H5qQRV3ajYCBAvcDwEFfyAAIAEtAAAiAzoAECAAIAEtAAEiAjoAESAAIAEtAAIiBDoAEiAAIAEtAAMiBToAEyAAIAEtAAQiBjoAFCAAIAMgAC0AAHM6ACAgACACIAAtAAFzOgAhIAAgBCAALQACczoAIiAAIAUgAC0AA3M6ACMgACAGIAAtAARzOgAkIAAgAS0ABSIDOgAVIAAgAS0ABiICOgAWIAAgAS0AByIEOgAXIAAgAS0ACCIFOgAYIAAgAS0ACSIGOgAZIAAgAyAALQAFczoAJSAAIAIgAC0ABnM6ACYgACAEIAAtAAdzOgAnIAAgBSAALQAIczoAKCAAIAEtAAoiAzoAGiAAIAEtAAsiAjoAGyAAIAEtAAwiBDoAHCAAIAEtAA0iBToAHSAAIAYgAC0ACXM6ACkgACADIAAtAApzOgAqIAAgAiAALQALczoAKyAAIAQgAC0ADHM6ACwgACAFIAAtAA1zOgAtIAAgAS0ADiIDOgAeIAAgAyAALQAOczoALiAAIAEtAA8iAzoAHyAAIAMgAC0AD3M6AC9BACECQQAhAwNAIAAgA2oiBCAELQAAIAJB/wFxQciUwABqLQAAcyICOgAAIANBAWoiA0EwRw0AC0EAIQMDQCAAIANqIgQgBC0AACACQf8BcUHIlMAAai0AAHMiAjoAACADQQFqIgNBMEcNAAsgAkEBaiEDQQAhAgNAIAAgAmoiBCAELQAAIANB/wFxQciUwABqLQAAcyIDOgAAIAJBAWoiAkEwRw0ACyADQQJqIQNBACECA0AgACACaiIEIAQtAAAgA0H/AXFByJTAAGotAABzIgM6AAAgAkEBaiICQTBHDQALIANBA2ohA0EAIQIDQCAAIAJqIgQgBC0AACADQf8BcUHIlMAAai0AAHMiAzoAACACQQFqIgJBMEcNAAsgA0EEaiEDQQAhAgNAIAAgAmoiBCAELQAAIANB/wFxQciUwABqLQAAcyIDOgAAIAJBAWoiAkEwRw0ACyADQQVqIQNBACECA0AgACACaiIEIAQtAAAgA0H/AXFByJTAAGotAABzIgM6AAAgAkEBaiICQTBHDQALIANBBmohA0EAIQIDQCAAIAJqIgQgBC0AACADQf8BcUHIlMAAai0AAHMiAzoAACACQQFqIgJBMEcNAAsgA0EHaiEDQQAhAgNAIAAgAmoiBCAELQAAIANB/wFxQciUwABqLQAAcyIDOgAAIAJBAWoiAkEwRw0ACyADQQhqIQNBACECA0AgACACaiIEIAQtAAAgA0H/AXFByJTAAGotAABzIgM6AAAgAkEBaiICQTBHDQALIANBCWohA0EAIQIDQCAAIAJqIgQgBC0AACADQf8BcUHIlMAAai0AAHMiAzoAACACQQFqIgJBMEcNAAsgA0EKaiEDQQAhAgNAIAAgAmoiBCAELQAAIANB/wFxQciUwABqLQAAcyIDOgAAIAJBAWoiAkEwRw0ACyADQQtqIQNBACECA0AgACACaiIEIAQtAAAgA0H/AXFByJTAAGotAABzIgM6AAAgAkEBaiICQTBHDQALIANBDGohA0EAIQIDQCAAIAJqIgQgBC0AACADQf8BcUHIlMAAai0AAHMiAzoAACACQQFqIgJBMEcNAAsgA0ENaiEDQQAhAgNAIAAgAmoiBCAELQAAIANB/wFxQciUwABqLQAAcyIDOgAAIAJBAWoiAkEwRw0ACyADQQ5qIQNBACECA0AgACACaiIEIAQtAAAgA0H/AXFByJTAAGotAABzIgM6AAAgAkEBaiICQTBHDQALIANBD2ohA0EAIQIDQCAAIAJqIgQgBC0AACADQf8BcUHIlMAAai0AAHMiAzoAACACQQFqIgJBMEcNAAsgA0EQaiEDQQAhAgNAIAAgAmoiBCAELQAAIANB/wFxQciUwABqLQAAcyIDOgAAIAJBAWoiAkEwRw0ACyAAIAAtADAgAS0AACAAQT9qIgMtAABzQciUwABqLQAAcyICOgAwIABBMWoiBCAELQAAIAIgAS0AAXNByJTAAGotAABzIgI6AAAgAEEyaiIEIAQtAAAgAiABLQACc0HIlMAAai0AAHMiAjoAACAAQTNqIgQgBC0AACACIAEtAANzQciUwABqLQAAcyICOgAAIABBNGoiBCAELQAAIAIgAS0ABHNByJTAAGotAABzIgI6AAAgAEE1aiIEIAQtAAAgAiABLQAFc0HIlMAAai0AAHMiAjoAACAAQTZqIgQgBC0AACACIAEtAAZzQciUwABqLQAAcyICOgAAIABBN2oiBCAELQAAIAIgAS0AB3NByJTAAGotAABzIgI6AAAgAEE4aiIEIAQtAAAgAiABLQAIc0HIlMAAai0AAHMiAjoAACAAQTlqIgQgBC0AACACIAEtAAlzQciUwABqLQAAcyICOgAAIABBOmoiBCAELQAAIAIgAS0ACnNByJTAAGotAABzIgI6AAAgAEE7aiIEIAQtAAAgAiABLQALc0HIlMAAai0AAHMiAjoAACAAQTxqIgQgBC0AACACIAEtAAxzQciUwABqLQAAcyICOgAAIABBPWoiBCAELQAAIAIgAS0ADXNByJTAAGotAABzIgI6AAAgAEE+aiIAIAAtAAAgAiABLQAOc0HIlMAAai0AAHMiADoAACADIAMtAAAgACABLQAPc0HIlMAAai0AAHM6AAAL3g8CD38BfiMAQcABayIDJAAgA0EAQYABEJEBIgNBuAFqIgQgAEE4aiIFKQMANwMAIANBsAFqIgYgAEEwaiIHKQMANwMAIANBqAFqIgggAEEoaiIJKQMANwMAIANBoAFqIgogAEEgaiILKQMANwMAIANBmAFqIgwgAEEYaiINKQMANwMAIANBkAFqIg4gAEEQaiIPKQMANwMAIANBiAFqIhAgAEEIaiIRKQMANwMAIAMgACkDADcDgAEgAgRAIAEgAkEHdGohAgNAIAMgASkAACISQjiGIBJCKIZCgICAgICAwP8Ag4QgEkIYhkKAgICAgOA/gyASQgiGQoCAgIDwH4OEhCASQgiIQoCAgPgPgyASQhiIQoCA/AeDhCASQiiIQoD+A4MgEkI4iISEhDcDACADIAFBCGopAAAiEkI4hiASQiiGQoCAgICAgMD/AIOEIBJCGIZCgICAgIDgP4MgEkIIhkKAgICA8B+DhIQgEkIIiEKAgID4D4MgEkIYiEKAgPwHg4QgEkIoiEKA/gODIBJCOIiEhIQ3AwggAyABQRBqKQAAIhJCOIYgEkIohkKAgICAgIDA/wCDhCASQhiGQoCAgICA4D+DIBJCCIZCgICAgPAfg4SEIBJCCIhCgICA+A+DIBJCGIhCgID8B4OEIBJCKIhCgP4DgyASQjiIhISENwMQIAMgAUEYaikAACISQjiGIBJCKIZCgICAgICAwP8Ag4QgEkIYhkKAgICAgOA/gyASQgiGQoCAgIDwH4OEhCASQgiIQoCAgPgPgyASQhiIQoCA/AeDhCASQiiIQoD+A4MgEkI4iISEhDcDGCADIAFBIGopAAAiEkI4hiASQiiGQoCAgICAgMD/AIOEIBJCGIZCgICAgIDgP4MgEkIIhkKAgICA8B+DhIQgEkIIiEKAgID4D4MgEkIYiEKAgPwHg4QgEkIoiEKA/gODIBJCOIiEhIQ3AyAgAyABQShqKQAAIhJCOIYgEkIohkKAgICAgIDA/wCDhCASQhiGQoCAgICA4D+DIBJCCIZCgICAgPAfg4SEIBJCCIhCgICA+A+DIBJCGIhCgID8B4OEIBJCKIhCgP4DgyASQjiIhISENwMoIAMgAUEwaikAACISQjiGIBJCKIZCgICAgICAwP8Ag4QgEkIYhkKAgICAgOA/gyASQgiGQoCAgIDwH4OEhCASQgiIQoCAgPgPgyASQhiIQoCA/AeDhCASQiiIQoD+A4MgEkI4iISEhDcDMCADIAFBOGopAAAiEkI4hiASQiiGQoCAgICAgMD/AIOEIBJCGIZCgICAgIDgP4MgEkIIhkKAgICA8B+DhIQgEkIIiEKAgID4D4MgEkIYiEKAgPwHg4QgEkIoiEKA/gODIBJCOIiEhIQ3AzggAyABQUBrKQAAIhJCOIYgEkIohkKAgICAgIDA/wCDhCASQhiGQoCAgICA4D+DIBJCCIZCgICAgPAfg4SEIBJCCIhCgICA+A+DIBJCGIhCgID8B4OEIBJCKIhCgP4DgyASQjiIhISENwNAIAMgAUHIAGopAAAiEkI4hiASQiiGQoCAgICAgMD/AIOEIBJCGIZCgICAgIDgP4MgEkIIhkKAgICA8B+DhIQgEkIIiEKAgID4D4MgEkIYiEKAgPwHg4QgEkIoiEKA/gODIBJCOIiEhIQ3A0ggAyABQdAAaikAACISQjiGIBJCKIZCgICAgICAwP8Ag4QgEkIYhkKAgICAgOA/gyASQgiGQoCAgIDwH4OEhCASQgiIQoCAgPgPgyASQhiIQoCA/AeDhCASQiiIQoD+A4MgEkI4iISEhDcDUCADIAFB2ABqKQAAIhJCOIYgEkIohkKAgICAgIDA/wCDhCASQhiGQoCAgICA4D+DIBJCCIZCgICAgPAfg4SEIBJCCIhCgICA+A+DIBJCGIhCgID8B4OEIBJCKIhCgP4DgyASQjiIhISENwNYIAMgAUHgAGopAAAiEkI4hiASQiiGQoCAgICAgMD/AIOEIBJCGIZCgICAgIDgP4MgEkIIhkKAgICA8B+DhIQgEkIIiEKAgID4D4MgEkIYiEKAgPwHg4QgEkIoiEKA/gODIBJCOIiEhIQ3A2AgAyABQegAaikAACISQjiGIBJCKIZCgICAgICAwP8Ag4QgEkIYhkKAgICAgOA/gyASQgiGQoCAgIDwH4OEhCASQgiIQoCAgPgPgyASQhiIQoCA/AeDhCASQiiIQoD+A4MgEkI4iISEhDcDaCADIAFB8ABqKQAAIhJCOIYgEkIohkKAgICAgIDA/wCDhCASQhiGQoCAgICA4D+DIBJCCIZCgICAgPAfg4SEIBJCCIhCgICA+A+DIBJCGIhCgID8B4OEIBJCKIhCgP4DgyASQjiIhISENwNwIAMgAUH4AGopAAAiEkI4hiASQiiGQoCAgICAgMD/AIOEIBJCGIZCgICAgIDgP4MgEkIIhkKAgICA8B+DhIQgEkIIiEKAgID4D4MgEkIYiEKAgPwHg4QgEkIoiEKA/gODIBJCOIiEhIQ3A3ggA0GAAWogAxADIAFBgAFqIgEgAkcNAAsLIAAgAykDgAE3AwAgBSAEKQMANwMAIAcgBikDADcDACAJIAgpAwA3AwAgCyAKKQMANwMAIA0gDCkDADcDACAPIA4pAwA3AwAgESAQKQMANwMAIANBwAFqJAALnAwBFH8gACgCACELIAAoAgwhBCAAKAIIIQUgACgCBCEDIwBBQGoiAkEYaiIGQgA3AwAgAkEgaiIHQgA3AwAgAkE4aiIIQgA3AwAgAkEwaiIJQgA3AwAgAkEoaiIKQgA3AwAgAkEIaiIMIAEpAAg3AwAgAkEQaiINIAEpABA3AwAgBiABKAAYIgY2AgAgByABKAAgIgc2AgAgAiABKQAANwMAIAIgASgAHCIONgIcIAIgASgAJCIPNgIkIAogASgAKCIKNgIAIAIgASgALCIQNgIsIAkgASgAMCIJNgIAIAIgASgANCIRNgI0IAggASgAOCIINgIAIAIgASgAPCISNgI8IAAgCSAPIAYgAigCDCITIAMgAigCACIUIAsgBCADQX9zcSADIAVxcmpqQQN3IgEgDCgCACILIAUgAyACKAIEIgwgBCAFIAFBf3NxIAEgA3FyampBB3ciBEF/c3EgASAEcXJqakELdyIFQX9zcSAEIAVxcmpqQRN3IgMgAigCFCIVIAUgDSgCACINIAQgA0F/c3EgAyAFcXIgAWpqQQN3IgFBf3NxIAEgA3FyIARqakEHdyICQX9zcSABIAJxciAFampBC3ciBCAHIAEgAiAOIAEgBEF/c3EgAiAEcXIgA2pqQRN3IgFBf3NxIAEgBHFyampBA3ciA0F/c3EgASADcXIgAmpqQQd3IgIgECABIAMgCiABIAJBf3NxIAIgA3FyIARqakELdyIBQX9zcSABIAJxcmpqQRN3IgRBf3NxIAEgBHFyIANqakEDdyIDIAggBCARIAEgA0F/c3EgAyAEcXIgAmpqQQd3IgVBf3NxIAMgBXFyIAFqakELdyIBIAVyIBIgBCABIAVxIgQgAyABQX9zcXJqakETdyICcSAEcmogFGpBmfOJ1AVqQQN3IgMgByABIAUgAyABIAJycSABIAJxcmogDWpBmfOJ1AVqQQV3IgQgAiADcnEgAiADcXJqakGZ84nUBWpBCXciASAEciAJIAIgASADIARycSADIARxcmpqQZnzidQFakENdyICcSABIARxcmogDGpBmfOJ1AVqQQN3IgMgDyABIAQgAyABIAJycSABIAJxcmogFWpBmfOJ1AVqQQV3IgQgAiADcnEgAiADcXJqakGZ84nUBWpBCXciASAEciARIAIgASADIARycSADIARxcmpqQZnzidQFakENdyICcSABIARxcmogC2pBmfOJ1AVqQQN3IgMgCiABIAYgBCADIAEgAnJxIAEgAnFyampBmfOJ1AVqQQV3IgQgAiADcnEgAiADcXJqakGZ84nUBWpBCXciASAEciAIIAIgASADIARycSADIARxcmpqQZnzidQFakENdyICcSABIARxcmogE2pBmfOJ1AVqQQN3IgMgEiACIBAgASAOIAQgAyABIAJycSABIAJxcmpqQZnzidQFakEFdyIEIAIgA3JxIAIgA3FyampBmfOJ1AVqQQl3IgUgAyAEcnEgAyAEcXJqakGZ84nUBWpBDXciAyAFcyICIARzaiAUakGh1+f2BmpBA3ciASAJIAMgASAHIAQgASACc2pqQaHX5/YGakEJdyICcyAFIA1qIAEgA3MgAnNqQaHX5/YGakELdyIEc2pqQaHX5/YGakEPdyIDIARzIgUgAnNqIAtqQaHX5/YGakEDdyIBIAggAyABIAogAiABIAVzampBodfn9gZqQQl3IgJzIAQgBmogASADcyACc2pBodfn9gZqQQt3IgRzampBodfn9gZqQQ93IgMgBHMiBSACc2ogDGpBodfn9gZqQQN3IgEgESADIAEgDyACIAEgBXNqakGh1+f2BmpBCXciAnMgBCAVaiABIANzIAJzakGh1+f2BmpBC3ciBHNqakGh1+f2BmpBD3ciAyAEcyIFIAJzaiATakGh1+f2BmpBA3ciASAAKAIAajYCACAAIBAgAiABIAVzampBodfn9gZqQQl3IgIgACgCDGo2Agwg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IgVFDQULIAIgBDYCFCACIAU2AhALIAIoAhAhBSACQThqIgZCADcDACACQTBqIgdCADcDACACQShqIghCADcDACACQSBqIglCADcDACACQRhqIgpCADcDACACQgA3AxAgAkHqAGpCADcBACACQfIAakEAOwEAIAJBEDYCYCACQQA7AWQgAkEANgFmIAJBiAFqIAJB8ABqKAIANgIAIAJBgAFqIAJB6ABqKQMANwMAIAJB2ABqIgsgAkGEAWopAgA3AwAgAiACKQNgNwN4IAIgAikCfDcDUCACQcgAaiIMIAspAwA3AwAgAiACKQNQNwNAIANBOGogDCkDADcAACADQTBqIAIpA0A3AAAgA0EoaiAGKQMANwAAIANBIGogBykDADcAACADQRhqIAgpAwA3AAAgA0EQaiAJKQMANwAAIANBCGogCikDADcAACADIAIpAxA3AAAgAUEANgIAIAAgBDYCBCAAIAU2AgAgAkGQAWokAA8LQbCawABBFyACQRBqQaCXwABBsJfAABB5AAtBEEEBQbSlwAAoAgAiAEECIAAbEQAAAAtBh4zAAEEkQayMwAAQiAEACyAEQQFBtKXAACgCACIAQQIgABsRAAAAC9gIAQV/IABBeGoiASAAQXxqKAIAIgNBeHEiAGohAgJAAkACQAJAIANBAXENACADQQNxRQ0BIAEoAgAiAyAAaiEAIAEgA2siAUH8pMAAKAIARgRAIAIoAgRBA3FBA0cNAUH0pMAAIAA2AgAgAiACKAIEQX5xNgIEIAEgAEEBcjYCBCAAIAFqIAA2AgAPCyABIAMQTAsCQCACQQRqIgQoAgAiA0ECcQRAIAQgA0F+cTYCACABIABBAXI2AgQgACABaiAANgIADAELAkAgAkGApcAAKAIARwRAQfykwAAoAgAgAkYNASACIANBeHEiAhBMIAEgACACaiIAQQFyNgIEIAAgAWogADYCACABQfykwAAoAgBHDQJB9KTAACAANgIADwtBgKXAACABNgIAQfikwABB+KTAACgCACAAaiIANgIAIAEgAEEBcjYCBEH8pMAAKAIAIAFGBEBB9KTAAEEANgIAQfykwABBADYCAAtBnKXAACgCACICIABPDQJBgKXAACgCACIARQ0CAkBB+KTAACgCACIDQSlJDQBBjKXAACEBA0AgASgCACIEIABNBEAgBCABKAIE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KQMwNwNoIAJBIGogAkGMAWopAgA3AwAgAkEYaiACQYQBaikCADcDACACQRBqIAJB/ABqKQIANwMAIAJBCGogAkH0AGopAgA3AwAgAkEoaiACQZQBaikCADcDACACIAIpAmw3AwAgASACEB8gAUIANwMIIAFCADcDACABQQA2AlAgAUHQmMAAKQMANwMQIAFBGGpB2JjAACkDADcDACABQSBqQeCYwAApAwA3AwAgAUEoakHomMAAKQMANwMAIAFBMGpB8JjAACkDADcDACABQThqQfiYwAApAwA3AwAgAUFAa0GAmcAAKQMANwMAIAFByABqQYiZwAApAwA3AwACQAJAQTBBARChASIDBEAgAkIwNwJsIAIgAzYCaCACQegAaiACQTAQXgJAIAIoAmwiBCACKAJwIgNGBEAgBCEDDAELIAQgA0kNAiAERQ0AIAIoAmghBQJAIANFBEAgBRAQQQEhBAwBCyAFIARBASADEJoBIgRFDQQLIAIgAzYCbCACIAQ2AmgLIAIoAmghBCABQgA3AwggAUIANwMAIAFBADYCUCABQRBqIgFB0JjAACkDADcDACABQQhqQdiYwAApAwA3AwAgAUEQakHgmMAAKQMANwMAIAFBGGpB6JjAACkDADcDACABQSBqQfCYwAApAwA3AwAgAUEoakH4mMAAKQMANwMAIAFBMGpBgJnAACkDADcDACABQThqQYiZwAApAwA3AwAgACADNgIEIAAgBDYCACACQaABaiQADwtBMEEBQbSlwAAoAgAiAEECIAAbEQAAAAtBh4zAAEEkQayMwAAQiAEACyADQQFBtKXAACgCACIAQQIgABsRAAAAC78GAQR/IAAgAWohAgJAAkACQAJAAkAgAEEEaigCACIDQQFxDQAgA0EDcUUNASAAKAIAIgMgAWohASAAIANrIgBB/KTAACgCAEYEQCACKAIEQQNxQQNHDQFB9KTAACABNgIAIAIgAigCBEF+cTYCBCAAIAFBAXI2AgQgAiABNgIADwsgACADEEwLAkAgAkEEaigCACIDQQJxBEAgAkEEaiADQX5xNgIAIAAgAUEBcjYCBCAAIAFqIAE2AgAMAQsCQCACQYClwAAoAgBHBEBB/KTAACgCACACRg0BIAIgA0F4cSICEEwgACABIAJqIgFBAXI2AgQgACABaiABNgIAIABB/KTAACgCAEcNAkH0pMAAIAE2AgAPC0GApcAAIAA2AgBB+KTAAEH4pMAAKAIAIAFqIgE2AgAgACABQQFyNgIEIABB/KTAACgCAEcNAkH0pMAAQQA2AgBB/KTAAEEANgIADwtB/KTAACAANgIAQfSkwABB9KTAACgCACABaiIBNgIAIAAgAUEBcjYCBCAAIAFqIAE2AgAPCyABQYACSQ0DIABCADcCECAAQRxqAn9BACABQQh2IgNFDQAaQR8gAUH///8HSw0AGiABQQYgA2ciAmtBH3F2QQFxIAJBAXRrQT5qCyICNgIAIAJBAnRB9KPAAGohAwJAAkBB6KHAACgCACIEQQEgAkEfcXQiBXEEQCADKAIAIgNBBGooAgBBeHEgAUcNASADIQIMAgtB6KHAACAEIAVyNgIAIAMgADYCAAwECyABQQBBGSACQQF2a0EfcSACQR9GG3QhBANAIAMgBEEddkEEcWpBEGoiBSgCACICRQ0DIARBAXQhBCACIQMgAkEEaigCAEF4cSABRw0ACwsgAigCCCIBIAA2AgwgAiAANgIIIABBGGpBADYCACAAIAI2AgwgACABNgIICw8LIAUgADYCAAsgAEEYaiADNgIAIAAgADYCDCAAIAA2AggPCyABQQN2IgJBA3RB7KHAAGohAQJ/QeShwAAoAgAiA0EBIAJ0IgJxBEAgASgCCAwBC0HkocAAIAIgA3I2AgAgAQshAiABIAA2AgggAiAANgIMIAAgATYCDCAAIAI2AggL1AYBBH8jAEHQAWsiAiQAIAJBygBqQgA3AQAgAkHSAGpBADsBACACQdQAakIANwIAIAJB3ABqQgA3AgAgAkHkAGpCADcCACACQewAakIANwIAIAJB9ABqQgA3AgAgAkH8AGpBADoAACACQf0AakEANgAAIAJBgQFqQQA7AAAgAkGDAWpBADoAACACQQA7AUQgAkEANgFGIAJBwAA2AkAgAkGIAWogAkFAa0HEABCLARogAkE4aiACQcQBaikCADcDACACQTBqIAJBvAFqKQIANwMAIAJBKGogAkG0AWopAgA3AwAgAkEgaiACQawBaikCADcDACACQRhqIAJBpAFqKQIANwMAIAJBEGogAkGcAWopAgA3AwAgAkEIaiACQZQBaikCADcDACACIAIpAowBNwMAIAEgAhARIAFCADcDCCABQgA3AwAgAUEANgJQIAFBkJnAACkDADcDECABQRhqQZiZwAApAwA3AwAgAUEgakGgmcAAKQMANwMAIAFBKGpBqJnAACkDADcDACABQTBqQbCZwAApAwA3AwAgAUE4akG4mcAAKQMANwMAIAFBQGtBwJnAACkDADcDACABQcgAakHImcAAKQMANwMAAkACQEHAAEEBEKEBIgMEQCACQsAANwKMASACIAM2AogBIAJBiAFqIAJBwAAQXgJAIAIoAowBIgQgAigCkAEiA0YEQCAEIQMMAQsgBCADSQ0CIARFDQAgAigCiAEhBQJAIANFBEAgBRAQQQEhBAwBCyAFIARBASADEJoBIgRFDQQLIAIgAzYCjAEgAiAENgKIAQsgAigCiAEhBCABQgA3AwggAUIANwMAIAFBADYCUCABQRBqIgFBkJnAACkDADcDACABQQhqQZiZwAApAwA3AwAgAUEQakGgmcAAKQMANwMAIAFBGGpBqJnAACkDADcDACABQSBqQbCZwAApAwA3AwAgAUEoakG4mcAAKQMANwMAIAFBMGpBwJnAACkDADcDACABQThqQciZwAApAwA3AwAgACADNgIEIAAgBDYCACACQdABaiQADwtBwABBAUG0pcAAKAIAIgBBAiAAGxEAAAALQYeMwABBJEGsjMAAEIgBAAsgA0EBQbSlwAAoAgAiAEECIAAbEQAAAAuOBgEKfyMAQTBrIgIkACACQSRqQYCHwAA2AgAgAkEDOgAoIAJCgICAgIAENwMIIAIgADYCICACQQA2AhggAkEANgIQAn8CQAJAAkAgASgCCCIDBEAgASgCACEFIAEoAgQiCCABQQxqKAIAIgYgBiAISxsiBkUNASABQRRqKAIAIQcgASgCECEJIAAgBSgCACAFKAIEQYyHwAAoAgARAwANAyAFQQhqIQECQAJAA0AgAiADQQRqKAIANgIMIAIgA0Ecai0AADoAKCACIANBCGooAgA2AgggA0EYaigCACEAQQAhBAJAAkACQCADQRRqKAIAQQFrDgIAAgELIAAgB08NAyAAQQN0IAlqIgooAgRBA0cNASAKKAIAKAIAIQALQQEhBAsgAiAANgIUIAIgBDYCECADQRBqKAIAIQBBACEEAkACQAJAIANBDGooAgBBAWsOAgACAQsgACAHTw0EIABBA3QgCWoiCigCBEEDRw0BIAooAgAoAgAh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IQEgACgCGCEAA0AgBkF/aiIGRQRAQQAPCyAAIAcgASgCEBEBAEUNAAsLIAULtwUBBH8jAEGQAWsiAiQAIAJBOmpCADcBACACQcIAakEAOwEAIAJBxABqQgA3AgAgAkHMAGpCADcCACACQdQAakIANwIAIAJBADsBNCACQQA2ATYgAkEoNgIwIAJBgAFqIAJB0ABqKQMANwMAIAJB+ABqIAJByABqKQMANwMAIAJB8ABqIAJBQGspAwA3AwAgAkHoAGogAkE4aikDADcDACACQYgBaiACQdgAaigCADYCACACIAIpAzA3A2AgAkEgaiACQfwAaikCADcDACACQRhqIAJB9ABqKQIANwMAIAJBEGogAkHsAGopAgA3AwAgAkEoaiACQYQBaikCADcDACACIAIpAmQ3AwggASACQQhqEE0gAUIANwMAIAFBADYCMCABQdCXwAApAwA3AwggAUEQakHYl8AAKQMANwMAIAFBGGpB4JfAACkDADcDACABQSBqQeiXwAApAwA3AwAgAUEoakHwl8AAKQMANwMAAkACQEEoQQEQoQEiAwRAIAJCKDcCZCACIAM2AmAgAkHgAGogAkEIakEoEF4CQCACKAJkIgQgAigCaCIDRgRAIAQhAwwBCyAEIANJDQIgBEUNACACKAJgIQUCQCADRQRAIAUQEEEBIQQMAQsgBSAEQQEgAxCaASIERQ0ECyACIAM2AmQgAiAENgJgCyACKAJgIQQgAUIANwMAIAFBADYCMCABQQhqIgFB0JfAACkDADcDACABQQhqQdiXwAApAwA3AwAgAUEQakHgl8AAKQMANwMAIAFBGGpB6JfAACkDADcDACABQSBqQfCXwAApAwA3AwAgACADNgIEIAAgBDYCACACQZABaiQADwtBKEEBQbSlwAAoAgAiAEECIAAbEQAAAAtBh4zAAEEkQayMwAAQiAEACyADQQFBtKXAACgCACIAQQIgABsRAAAAC8YEAQR/IwBBoAFrIgIkACACQTpqQgA3AQAgAkHCAGpBADsBACACQcQAakIANwIAIAJBzABqQgA3AgAgAkHUAGpCADcCACACQdwAakIANwIAIAJBADsBNCACQQA2ATYgAkEwNgIwIAJBkAFqIAJB2ABqKQMANwMAIAJBiAFqIAJB0ABqKQMANwMAIAJBgAFqIAJByABqKQMANwMAIAJB+ABqIAJBQGspAwA3AwAgAkHwAGogAkE4aikDADcDACACQZgBaiACQeAAaigCADYCACACIAIpAzA3A2ggAkEgaiACQYwBaikCADcDACACQRhqIAJBhAFqKQIANwMAIAJBEGogAkH8AGopAgA3AwAgAkEIaiACQfQAaikCADcDACACQShqIAJBlAFqKQIANwMAIAIgAikCbDcDACABIAIQYyABQQBByAEQkQEiBUEANgLIAQJAAkBBMEEBEKEBIgEEQCACQjA3AmwgAiABNgJoIAJB6ABqIAJBMBBeAkAgAigCbCIDIAIoAnAiAUYEQCADIQEMAQsgAyABSQ0CIANFDQAgAigCaCEEAkAgAUUEQCAEEBBBASEDDAELIAQgA0EBIAEQmgEiA0UNBAsgAiABNgJsIAIgAzYCaAsgAigCaCEDIAVBAEHIARCRAUEANgLIASAAIAE2AgQgACADNgIAIAJBoAFqJAAPC0EwQQFBtKXAACgCACIAQQIgABsRAAAAC0GHjMAAQSRBrIzAABCIAQALIAFBAUG0pcAAKAIAIgBBAiAAGxEAAAALxgQBBH8jAEGgAWsiAiQAIAJBOmpCADcBACACQcIAakEAOwEAIAJBxABqQgA3AgAgAkHMAGpCADcCACACQdQAakIANwIAIAJB3ABqQgA3AgAgAkEAOwE0IAJBADYBNiACQTA2AjAgAkGQAWogAkHYAGopAwA3AwAgAkGIAWogAkHQAGopAwA3AwAgAkGAAWogAkHIAGopAwA3AwAgAkH4AGogAkFAaykDADcDACACQfAAaiACQThqKQMANwMAIAJBmAFqIAJB4ABqKAIANgIAIAIgAikDMDcDaCACQSBqIAJBjAFqKQIANwMAIAJBGGogAkGEAWopAgA3AwAgAkEQaiACQfwAaikCADcDACACQQhqIAJB9ABqKQIANwMAIAJBKGogAkGUAWopAgA3AwAgAiACKQJsNwMAIAEgAhBkIAFBAEHIARCRASIFQQA2AsgBAkACQEEwQQEQoQEiAQRAIAJCMDcCbCACIAE2AmggAkHoAGogAkEwEF4CQCACKAJsIgMgAigCcCIBRgRAIAMhAQwBCyADIAFJDQIgA0UNACACKAJoIQQCQCABRQRAIAQQEEEBIQMMAQsgBCADQQEgARCaASIDRQ0ECyACIAE2AmwgAiADNgJoCyACKAJoIQMgBUEAQcgBEJEBQQA2AsgBIAAgATYCBCAAIAM2AgAgAkGgAWokAA8LQTBBAUG0pcAAKAIAIgBBAiAAGxEAAAALQYeMwABBJEGsjMAAEIgBAAsgAUEBQbSlwAAoAgAiAEECIAAbEQAAAAu8BAEEfyMAQaADayICJAAgAkHyAmpCADcBACACQfoCakEAOwEAIAJB/AJqQgA3AgAgAkGEA2pCADcCACACQYwDakIANwIAIAJBlANqQgA3AgAgAkEAOwHsAiACQQA2Ae4CIAJBMDYC6AIgAkHYAGogAkGQA2opAwA3AwAgAkHQAGogAkGIA2opAwA3AwAgAkHIAGogAkGAA2opAwA3AwAgAkFAayACQfgCaikDADcDACACQThqIAJB8AJqKQMANwMAIAJB4ABqIAJBmANqKAIANgIAIAIgAikD6AI3AzAgAkEgaiACQdQAaikCADcDACACQRhqIAJBzABqKQIANwMAIAJBEGogAkHEAGopAgA3AwAgAkEIaiACQTxqKQIANwMAIAJBKGogAkHcAGopAgA3AwAgAiACKQI0NwMAIAJBMGogAUG4AhCLARogAkEwaiACEGMCQAJAQTBBARChASIDBEAgAkIwNwI0IAIgAzYCMCACQTBqIAJBMBBeAkAgAigCNCIEIAIoAjgiA0YEQCAEIQMMAQsgBCADSQ0CIARFDQAgAigCMCEFAkAgA0UEQCAFEBBBASEEDAELIAUgBEEBIAMQmgEiBEUNBAsgAiADNgI0IAIgBDYCMAsgAigCMCEEIAEQECAAIAM2AgQgACAENgIAIAJBoANqJAAPC0EwQQFBtKXAACgCACIAQQIgABsRAAAAC0GHjMAAQSRBrIzAABCIAQALIANBAUG0pcAAKAIAIgBBAiAAGxEAAAALvAQBBH8jAEGgA2siAiQAIAJB8gJqQgA3AQAgAkH6AmpBADsBACACQfwCakIANwIAIAJBhANqQgA3AgAgAkGMA2pCADcCACACQZQDakIANwIAIAJBADsB7AIgAkEANgHuAiACQTA2AugCIAJB2ABqIAJBkANqKQMANwMAIAJB0ABqIAJBiANqKQMANwMAIAJByABqIAJBgANqKQMANwMAIAJBQGsgAkH4AmopAwA3AwAgAkE4aiACQfACaikDADcDACACQeAAaiACQZgDaigCADYCACACIAIpA+gCNwMwIAJBIGogAkHUAGopAgA3AwAgAkEYaiACQcwAaikCADcDACACQRBqIAJBxABqKQIANwMAIAJBCGogAkE8aikCADcDACACQShqIAJB3ABqKQIANwMAIAIgAikCNDcDACACQTBqIAFBuAIQiwEaIAJBMGogAhBkAkACQEEwQQEQoQEiAwRAIAJCMDcCNCACIAM2AjAgAkEwaiACQTAQXgJAIAIoAjQiBCACKAI4IgNGBEAgBCEDDAELIAQgA0kNAiAERQ0AIAIoAjAhBQJAIANFBEAgBRAQQQEhBAwBCyAFIARBASADEJoBIgRFDQQLIAIgAzYCNCACIAQ2AjALIAIoAjAhBCABEBAgACADNgIEIAAgBDYCACACQaADaiQADwtBMEEBQbSlwAAoAgAiAEECIAAbEQAAAAtBh4zAAEEkQayMwAAQiAEACyADQQFBtKXAACgCACIAQQIgABsRAAAAC7wEAQR/IwBBwAJrIgIkACACQZICakIANwEAIAJBmgJqQQA7AQAgAkGcAmpCADcCACACQaQCakIANwIAIAJBrAJqQgA3AgAgAkG0AmpCADcCACACQQA7AYwCIAJBADYBjgIgAkEwNgKIAiACQdgAaiACQbACaikDADcDACACQdAAaiACQagCaikDADcDACACQcgAaiACQaACaikDADcDACACQUBrIAJBmAJqKQMANwMAIAJBOGogAkGQAmopAwA3AwAgAkHgAGogAkG4AmooAgA2AgAgAiACKQOIAjcDMCACQSBqIAJB1ABqKQIANwMAIAJBGGogAkHMAGopAgA3AwAgAkEQaiACQcQAaikCADcDACACQQhqIAJBPGopAgA3AwAgAkEoaiACQdwAaikCADcDACACIAIpAjQ3AwAgAkEwaiABQdgBEIsBGiACQTBqIAIQHwJAAkBBMEEBEKEBIgMEQCACQjA3AjQgAiADNgIwIAJBMGogAkEwEF4CQCACKAI0IgQgAigCOCIDRgRAIAQhAwwBCyAEIANJDQIgBEUNACACKAIwIQUCQCADRQRAIAUQEEEBIQQMAQsgBSAEQQEgAxCaASIERQ0ECyACIAM2AjQgAiAENgIwCyACKAIwIQQgARAQIAAgAzYCBCAAIAQ2AgAgAkHAAmokAA8LQTBBAUG0pcAAKAIAIgBBAiAAGxEAAAALQYeMwABBJEGsjMAAEIgBAAsgA0EBQbSlwAAoAgAiAEECIAAbEQAAAAuBBQEBfiAAEEAgASAAKQMQIgJCOIYgAkIohkKAgICAgIDA/wCDhCACQhiGQoCAgICA4D+DIAJCCIZCgICAgPAfg4SEIAJCCIhCgICA+A+DIAJCGIhCgID8B4OEIAJCKIhCgP4DgyACQjiIhISENwAAIAEgAEEYaikDACICQjiGIAJCKIZCgICAgICAwP8Ag4QgAkIYhkKAgICAgOA/gyACQgiGQoCAgIDwH4OEhCACQgiIQoCAgPgPgyACQhiIQoCA/AeDhCACQiiIQoD+A4MgAkI4iISEhDcACCABIABBIGopAwAiAkI4hiACQiiGQoCAgICAgMD/AIOEIAJCGIZCgICAgIDgP4MgAkIIhkKAgICA8B+DhIQgAkIIiEKAgID4D4MgAkIYiEKAgPwHg4QgAkIoiEKA/gODIAJCOIiEhIQ3ABAgASAAQShqKQMAIgJCOIYgAkIohkKAgICAgIDA/wCDhCACQhiGQoCAgICA4D+DIAJCCIZCgICAgPAfg4SEIAJCCIhCgICA+A+DIAJCGIhCgID8B4OEIAJCKIhCgP4DgyACQjiIhISENwAYIAEgAEEwaikDACICQjiGIAJCKIZCgICAgICAwP8Ag4QgAkIYhkKAgICAgOA/gyACQgiGQoCAgIDwH4OEhCACQgiIQoCAgPgPgyACQhiIQoCA/AeDhCACQiiIQoD+A4MgAkI4iISEhDcAICABIABBOGopAwAiAkI4hiACQiiGQoCAgICAgMD/AIOEIAJCGIZCgICAgIDgP4MgAkIIhkKAgICA8B+DhIQgAkIIiEKAgID4D4MgAkIYiEKAgPwHg4QgAkIoiEKA/gODIAJCOIiEhIQ3ACgLyQQCBX8BfiAAQSBqIQMgAEEIaiEEIAApAwAhBwJAAkAgACgCHCICQcAARgRAIAQgA0EBEAhBACECIABBADYCHAwBCyACQT9LDQELIABBHGoiBSACakEEakGAAToAACAAIAAoAhwiBkEBaiICNgIcAkAgAkHBAEkEQCACIAVqQQRqQQBBPyAGaxCRARpBwAAgACgCHGtBB00EQCAEIANBARAIIAAoAhwiAkHBAE8NAiAAQSBqQQAgAhCRARoLIABB2ABqIAdCA4YiB0I4hiAHQiiGQoCAgICAgMD/AIOEIAdCGIZCgICAgIDgP4MgB0IIhkKAgICA8B+DhIQgB0IIiEKAgID4D4MgB0IYiEKAgPwHg4QgB0IoiEKA/gODIAdCOIiEhIQ3AgAgBCADQQEQCCAAQQA2AhwgASAAKAIIIgJBGHQgAkEIdEGAgPwHcXIgAkEIdkGA/gNxIAJBGHZycjYAACABIABBDGooAgAiAkEYdCACQQh0QYCA/AdxciACQQh2QYD+A3EgAkEYdnJyNgAEIAEgAEEQaigCACICQRh0IAJBCHRBgID8B3FyIAJBCHZBgP4DcSACQRh2cnI2AAggASAAQRRqKAIAIgJBGHQgAkEIdEGAgPwHcXIgAkEIdkGA/gNxIAJBGHZycjYADCABIABBGGooAgAiAEEYdCAAQQh0QYCA/AdxciAAQQh2QYD+A3EgAEEYdnJyNgAQDwsgAkHAAEGAmsAAEH4ACyACQcAAQZCawAAQfQALIAJBwABBoJrAABB8AAviBAEEfyMAQfAAayICJAAgAkEqakIANwEAIAJBMmpBADsBACACQTRqQgA3AgAgAkE8akIANwIAIAJBADsBJCACQQA2ASYgAkEgNgIgIAJB4ABqIAJBOGopAwA3AwAgAkHYAGogAkEwaikDADcDACACQdAAaiACQShqKQMANwMAIAJB6ABqIAJBQGsoAgA2AgAgAiACKQMgNwNIIAJBEGogAkHcAGopAgA3AwAgAkEIaiACQdQAaikCADcDACACQRhqIAJB5ABqKQIANwMAIAIgAikCTDcDACABIAIQOyABQQA2AgggAUIANwMAIAFBrJjAACkCADcCTCABQdQAakG0mMAAKQIANwIAIAFB3ABqQbyYwAApAgA3AgAgAUHkAGpBxJjAACkCADcCAAJAAkBBIEEBEKEBIgMEQCACQiA3AkwgAiADNgJIIAJByABqIAJBIBBeAkAgAigCTCIEIAIoAlAiA0YEQCAEIQMMAQsgBCADSQ0CIARFDQAgAigCSCEFAkAgA0UEQCAFEBBBASEEDAELIAUgBEEBIAMQmgEiBEUNBAsgAiADNgJMIAIgBDYCSAsgAigCSCEEIAFBADYCCCABQgA3AwAgAUHMAGoiAUGsmMAAKQIANwIAIAFBCGpBtJjAACkCADcCACABQRBqQbyYwAApAgA3AgAgAUEYakHEmMAAKQIANwIAIAAgAzYCBCAAIAQ2AgAgAkHwAGokAA8LQSBBAUG0pcAAKAIAIgBBAiAAGxEAAAALQYeMwABBJEGsjMAAEIgBAAsgA0EBQbSlwAAoAgAiAEECIAAbEQAAAAvMBAEEfyMAQdABayICJAAgAkHKAGpCADcBACACQdIAakEAOwEAIAJB1ABqQgA3AgAgAkHcAGpCADcCACACQeQAakIANwIAIAJB7ABqQgA3AgAgAkH0AGpCADcCACACQfwAakEAOgAAIAJB/QBqQQA2AAAgAkGBAWpBADsAACACQYMBakEAOgAAIAJBADsBRCACQQA2AUYgAkHAADYCQCACQYgBaiACQUBrQcQAEIsBGiACQThqIAJBxAFqKQIANwMAIAJBMGogAkG8AWopAgA3AwAgAkEoaiACQbQBaikCADcDACACQSBqIAJBrAFqKQIANwMAIAJBGGogAkGkAWopAgA3AwAgAkEQaiACQZwBaikCADcDACACQQhqIAJBlAFqKQIANwMAIAIgAikCjAE3AwAgASACEFsgAUEAQcgBEJEBIgVBADYCyAECQAJAQcAAQQEQoQEiAQRAIAJCwAA3AowBIAIgATYCiAEgAkGIAWogAkHAABBeAkAgAigCjAEiAyACKAKQASIBRgRAIAMhAQwBCyADIAFJDQIgA0UNACACKAKIASEEAkAgAUUEQCAEEBBBASEDDAELIAQgA0EBIAEQmgEiA0UNBAsgAiABNgKMASACIAM2AogBCyACKAKIASEDIAVBAEHIARCRAUEANgLIASAAIAE2AgQgACADNgIAIAJB0AFqJAAPC0HAAEEBQbSlwAAoAgAiAEECIAAbEQAAAAtBh4zAAEEkQayMwAAQiAEACyABQQFBtKXAACgCACIAQQIgABsRAAAAC8wEAQR/IwBB0AFrIgIkACACQcoAakIANwEAIAJB0gBqQQA7AQAgAkHUAGpCADcCACACQdwAakIANwIAIAJB5ABqQgA3AgAgAkHsAGpCADcCACACQfQAakIANwIAIAJB/ABqQQA6AAAgAkH9AGpBADYAACACQYEBakEAOwAAIAJBgwFqQQA6AAAgAkEAOwFEIAJBADYBRiACQcAANgJAIAJBiAFqIAJBQGtBxAAQiwEaIAJBOGogAkHEAWopAgA3AwAgAkEwaiACQbwBaikCADcDACACQShqIAJBtAFqKQIANwMAIAJBIGogAkGsAWopAgA3AwAgAkEYaiACQaQBaikCADcDACACQRBqIAJBnAFqKQIANwMAIAJBCGogAkGUAWopAgA3AwAgAiACKQKMATcDACABIAIQXCABQQBByAEQkQEiBUEANgLIAQJAAkBBwABBARChASIBBEAgAkLAADcCjAEgAiABNgKIASACQYgBaiACQcAAEF4CQCACKAKMASIDIAIoApABIgFGBEAgAyEBDAELIAMgAUkNAiADRQ0AIAIoAogBIQQCQCABRQRAIAQQEEEBIQMMAQsgBCADQQEgARCaASIDRQ0ECyACIAE2AowBIAIgAzYCiAELIAIoAogBIQMgBUEAQcgBEJEBQQA2AsgBIAAgATYCBCAAIAM2AgAgAkHQAWokAA8LQcAAQQFBtKXAACgCACIAQQIgABsRAAAAC0GHjMAAQSRBrIzAABCIAQALIAFBAUG0pcAAKAIAIgBBAiAAGxEAAAALuAQBBH8jAEGgA2siAiQAIAJB4gJqQgA3AQAgAkHqAmpBADsBACACQewCakIANwIAIAJB9AJqQgA3AgAgAkH8AmpCADcCACACQYQDakIANwIAIAJBjANqQgA3AgAgAkGUA2pBADoAACACQZUDakEANgAAIAJBmQNqQQA7AAAgAkGbA2pBADoAACACQQA7AdwCIAJBADYB3gIgAkHAADYC2AIgAkFAayACQdgCakHEABCLARogAkE4aiACQfwAaikCADcDACACQTBqIAJB9ABqKQIANwMAIAJBKGogAkHsAGopAgA3AwAgAkEgaiACQeQAaikCADcDACACQRhqIAJB3ABqKQIANwMAIAJBEGogAkHUAGopAgA3AwAgAkEIaiACQcwAaikCADcDACACIAIpAkQ3AwAgAkFAayABQZgCEIsBGiACQUBrIAIQWwJAAkBBwABBARChASIDBEAgAkLAADcCRCACIAM2AkAgAkFAayACQcAAEF4CQCACKAJEIgQgAigCSCIDRgRAIAQhAwwBCyAEIANJDQIgBEUNACACKAJAIQUCQCADRQRAIAUQEEEBIQQMAQsgBSAEQQEgAxCaASIERQ0ECyACIAM2AkQgAiAENgJACyACKAJAIQQgARAQIAAgAzYCBCAAIAQ2AgAgAkGgA2okAA8LQcAAQQFBtKXAACgCACIAQQIgABsRAAAAC0GHjMAAQSRBrIzAABCIAQALIANBAUG0pcAAKAIAIgBBAiAAGxEAAAALuAQBBH8jAEGgA2siAiQAIAJB4gJqQgA3AQAgAkHqAmpBADsBACACQewCakIANwIAIAJB9AJqQgA3AgAgAkH8AmpCADcCACACQYQDakIANwIAIAJBjANqQgA3AgAgAkGUA2pBADoAACACQZUDakEANgAAIAJBmQNqQQA7AAAgAkGbA2pBADoAACACQQA7AdwCIAJBADYB3gIgAkHAADYC2AIgAkFAayACQdgCakHEABCLARogAkE4aiACQfwAaikCADcDACACQTBqIAJB9ABqKQIANwMAIAJBKGogAkHsAGopAgA3AwAgAkEgaiACQeQAaikCADcDACACQRhqIAJB3ABqKQIANwMAIAJBEGogAkHUAGopAgA3AwAgAkEIaiACQcwAaikCADcDACACIAIpAkQ3AwAgAkFAayABQZgCEIsBGiACQUBrIAIQXAJAAkBBwABBARChASIDBEAgAkLAADcCRCACIAM2AkAgAkFAayACQcAAEF4CQCACKAJEIgQgAigCSCIDRgRAIAQhAwwBCyAEIANJDQIgBEUNACACKAJAIQUCQCADRQRAIAUQEEEBIQQMAQsgBSAEQQEgAxCaASIERQ0ECyACIAM2AkQgAiAENgJACyACKAJAIQQgARAQIAAgAzYCBCAAIAQ2AgAgAkGgA2okAA8LQcAAQQFBtKXAACgCACIAQQIgABsRAAAAC0GHjMAAQSRBrIzAABCIAQALIANBAUG0pcAAKAIAIgBBAiAAGxEAAAALuAQBBH8jAEHgAmsiAiQAIAJBogJqQgA3AQAgAkGqAmpBADsBACACQawCakIANwIAIAJBtAJqQgA3AgAgAkG8AmpCADcCACACQcQCakIANwIAIAJBzAJqQgA3AgAgAkHUAmpBADoAACACQdUCakEANgAAIAJB2QJqQQA7AAAgAkHbAmpBADoAACACQQA7AZwCIAJBADYBngIgAkHAADYCmAIgAkFAayACQZgCakHEABCLARogAkE4aiACQfwAaikCADcDACACQTBqIAJB9ABqKQIANwMAIAJBKGogAkHsAGopAgA3AwAgAkEgaiACQeQAaikCADcDACACQRhqIAJB3ABqKQIANwMAIAJBEGogAkHUAGopAgA3AwAgAkEIaiACQcwAaikCADcDACACIAIpAkQ3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AABzOgAAIAVBAWoiBUHIAEcNAAsgABAOIAZByABJDQJByAAhBSACQcgAaiECIAhByABqIQgMAAsACyACIARqIgYgBEkNAiAGQcgASw0DIAAgBGpBzAFqIAEgAhCLARogACgCyAEgAmohBwwBCyAAQcwBaiABIARqIAcQiwEaCyAAIAc2AsgBIANBQGskAA8LIAQgBkHAm8AAEH4ACyAGQcgAQcCbwAAQfQALIARByABB0JvAABB+AAsgBCACQeCbwAAQfQALIANBNGpBBjYCACADQSRqQQI2AgAgA0IDNwIUIANBuJ7AADYCECADQQY2AiwgAyADQQxqNgI4IANB3J7AADYCPCADIANBKGo2AiAgAyADQTxqNgIwIAMgA0E4ajYCKCADQRBqQeCewAAQkAEAC5sEAQd/IwBBQGoiAyQAAkACQAJAAkACQAJAAkBB6AAgACgCyAEiBGsiBiACTQRAIAQEQCAEQekATw0GIAAgBGpBzAFqIAEgBhCLARogAiAGayECIAEgBmohAQNAIAAgBWoiBCAELQAAIARBzAFqLQAAczoAACAFQQFqIgVB6ABHDQALIAAQDgsgAiACQegAcCIHayEEIAIgB0kNBiAEQegASQ0BIAFB6ABqIQggASECIAQhBkHoACEFA0AgAyAFNgIMIAVB6ABHDQggBkGYf2ohBkEAIQUDQCAAIAVqIgkgCS0AACACIAVqLQAAczoAACAFQQFqIgVB6ABHDQALIAAQDiAGQegASQ0CQegAIQUgAkHoAGohAiAIQegAaiEIDAALAAsgAiAEaiIGIARJDQIgBkHoAEsNAyAAIARqQcwBaiABIAIQiwEaIAAoAsgBIAJqIQcMAQsgAEHMAWogASAEaiAHEIsBGgsgACAHNgLIASADQUBrJAAPCyAEIAZBwJvAABB+AAsgBkHoAEHAm8AAEH0ACyAEQegAQdCbwAAQfgALIAQgAkHgm8AAEH0ACyADQTRqQQY2AgAgA0EkakECNgIAIANCAzcCFCADQbiewAA2AhAgA0EGNgIsIAMgA0EMajYCOCADQdiewAA2AjwgAyADQShqNgIgIAMgA0E8ajYCMCADIANBOGo2AiggA0EQakHgnsAAEJABAAvkAwEEfyMAQcABayICJAAgAkGiAWpCADcBACACQaoBakEAOwEAIAJBrAFqQgA3AgAgAkG0AWpCADcCACACQQA7AZwBIAJBADYBngEgAkEgNgKYASACQUBrIAJBsAFqKQMANwMAIAJBOGogAkGoAWopAwA3AwAgAkEwaiACQaABaikDADcDACACQcgAaiACQbgBaigCADYCACACIAIpA5gBNwMoIAJBGGogAkE8aikCADcDACACQRBqIAJBNGopAgA3AwAgAkEgaiACQcQAaikCADcDACACIAIpAiw3AwggAkEoaiABQfAAEIsBGiACQShqIAJBCGoQOwJAAkBBIEEBEKEBIgMEQCACQiA3AiwgAiADNgIoIAJBKGogAkEIakEgEF4CQCACKAIsIgQgAigCMCIDRgRAIAQhAwwBCyAEIANJDQIgBEUNACACKAIoIQUCQCADRQRAIAUQEEEBIQQMAQsgBSAEQQEgAxCaASIERQ0ECyACIAM2AiwgAiAENgIoCyACKAIoIQQgARAQIAAgAzYCBCAAIAQ2AgAgAkHAAWokAA8LQSBBAUG0pcAAKAIAIgBBAiAAGxEAAAALQYeMwABBJEGsjMAAEIgBAAsgA0EBQbSlwAAoAgAiAEECIAAbEQAAAAuOBAEFfyMAQYABayICJAAgAkHyAGpCADcBACACQfoAakEAOwEAIAJBADsBbCACQQA2AW4gAkEQNgJoIAJBGGogAkHwAGoiBCkDADcDACACQSBqIAJB+ABqKAIANgIAIAJBCGoiBSACQRxqKQIANwMAIAIgAikDaDcDECACIAIpAhQ3AwAgAkEQaiABQdQAEIsBGgJAAkACQCACKAIQIgNBEEkEQCACQRBqQQRyIgYgA2pBECADayIDIAMQkQEaIAJBADYCECACQSRqIgMgBhALIAQgAkHcAGopAgA3AwAgAiACQdQAaikCADcDaCADIAJB6ABqEAsgBSACQSxqKQIANwMAIAIgAikCJDcDAEEQQQEQoQEiA0UNASACQhA3AhQgAiADNgIQIAJBEGogAkEQEF4CQCACKAIUIgQgAigCGCIDRgRAIAQhAwwBCyAEIANJDQMgBEUNACACKAIQIQUCQCADRQRAIAUQEEEBIQQMAQsgBSAEQQEgAxCaASIERQ0FCyACIAM2AhQgAiAENgIQCyACKAIQIQQgARAQIAAgAzYCBCAAIAQ2AgAgAkGAAWokAA8LQbCawABBFyACQegAakGgl8AAQbCXwAAQeQALQRBBAUG0pcAAKAIAIgBBAiAAGxEAAAALQYeMwABBJEGsjMAAEIgBAAsgA0EBQbSlwAAoAgAiAEECIAAbEQAAAAuFBAEEfyMAQdAAayICJAAgAkEqakIANwEAIAJBMmpBADsBACACQRQ2AiAgAkE0akEANgIAIAJBADsBJCACQQA2ASYgAkHIAGogAkEwaikDADcDACACQUBrIAJBKGopAwA3AwAgAkEQaiACQcQAaikCADcDACACQRhqIAJBzABqKAIANgIAIAIgAikDIDcDOCACIAIpAjw3AwggASACQQhqEFogAUIANwMAIAFBADYCHCABQfiXwAApAwA3AwggAUEQakGAmMAAKQMANwMAIAFBGGpBiJjAACgCADYCAAJAAkBBFEEBEKEBIgMEQCACQhQ3AjwgAiADNgI4IAJBOGogAkEIakEUEF4CQCACKAI8IgQgAigCQCIDRgRAIAQhAwwBCyAEIANJDQIgBEUNACACKAI4IQUCQCADRQRAIAUQEEEBIQQMAQsgBSAEQQEgAxCaASIERQ0ECyACIAM2AjwgAiAENgI4CyACKAI4IQQgAUIANwMAIAFBADYCHCABQQhqIgFB+JfAACkDADcDACABQQhqQYCYwAApAwA3AwAgAUEQakGImMAAKAIANgIAIAAgAzYCBCAAIAQ2AgAgAkHQAGokAA8LQRRBAUG0pcAAKAIAIgBBAiAAGxEAAAALQYeMwABBJEGsjMAAEIgBAAsgA0EBQbSlwAAoAgAiAEECIAAbEQAAAAuFBAEEfyMAQdAAayICJAAgAkEqakIANwEAIAJBMmpBADsBACACQRQ2AiAgAkE0akEANgIAIAJBADsBJCACQQA2ASYgAkHIAGogAkEwaikDADcDACACQUBrIAJBKGopAwA3AwAgAkEQaiACQcQAaikCADcDACACQRhqIAJBzABqKAIANgIAIAIgAikDIDcDOCACIAIpAjw3AwggASACQQhqECAgAUEANgIcIAFCADcDACABQRhqQYiYwAAoAgA2AgAgAUEQakGAmMAAKQMANwMAIAFB+JfAACkDADcDCAJAAkBBFEEBEKEBIgMEQCACQhQ3AjwgAiADNgI4IAJBOGogAkEIakEUEF4CQCACKAI8IgQgAigCQCIDRgRAIAQhAwwBCyAEIANJDQIgBEUNACACKAI4IQUCQCADRQRAIAUQEEEBIQQMAQsgBSAEQQEgAxCaASIERQ0ECyACIAM2AjwgAiAENgI4CyACKAI4IQQgAUEANgIcIAFCADcDACABQQhqIgFBEGpBiJjAACgCADYCACABQQhqQYCYwAApAwA3AwAgAUH4l8AAKQMANwMAIAAgAzYCBCAAIAQ2AgAgAkHQAGokAA8LQRRBAUG0pcAAKAIAIgBBAiAAGxEAAAALQYeMwABBJEGsjMAAEIgBAAsgA0EBQbSlwAAoAgAiAEECIAAbEQAAAAvlAwEEfyMAQfAAayICJAAgAkEqakIANwEAIAJBMmpBADsBACACQTRqQgA3AgAgAkE8akIANwIAIAJBADsBJCACQQA2ASYgAkEgNgIgIAJB4ABqIAJBOGopAwA3AwAgAkHYAGogAkEwaikDADcDACACQdAAaiACQShqKQMANwMAIAJB6ABqIAJBQGsoAgA2AgAgAiACKQMgNwNIIAJBEGogAkHcAGopAgA3AwAgAkEIaiACQdQAaikCADcDACACQRhqIAJB5ABqKQIANwMAIAIgAikCTDcDACABIAIQZiABQQBByAEQkQEiBUEANgLIAQJAAkBBIEEBEKEBIgEEQCACQiA3AkwgAiABNgJIIAJByABqIAJBIBBeAkAgAigCTCIDIAIoAlAiAUYEQCADIQEMAQsgAyABSQ0CIANFDQAgAigCSCEEAkAgAUUEQCAEEBBBASEDDAELIAQgA0EBIAEQmgEiA0UNBAsgAiABNgJMIAIgAzYCSAsgAigCSCEDIAVBAEHIARCRAUEANgLIASAAIAE2AgQgACADNgIAIAJB8ABqJAAPC0EgQQFBtKXAACgCACIAQQIgABsRAAAAC0GHjMAAQSRBrIzAABCIAQALIAFBAUG0pcAAKAIAIgBBAiAAGxEAAAAL5QMBBH8jAEHwAGsiAiQAIAJBKmpCADcBACACQTJqQQA7AQAgAkE0akIANwIAIAJBPGpCADcCACACQQA7ASQgAkEANgEmIAJBIDYCICACQeAAaiACQThqKQMANwMAIAJB2ABqIAJBMGopAwA3AwAgAkHQAGogAkEoaikDADcDACACQegAaiACQUBrKAIANgIAIAIgAikDIDcDSCACQRBqIAJB3ABqKQIANwMAIAJBCGogAkHUAGopAgA3AwAgAkEYaiACQeQAaikCADcDACACIAIpAkw3AwAgASACEGcgAUEAQcgBEJEBIgVBADYCyAECQAJAQSBBARChASIBBEAgAkIgNwJMIAIgATYCSCACQcgAaiACQSAQXgJAIAIoAkwiAyACKAJQIgFGBEAgAyEBDAELIAMgAUkNAiADRQ0AIAIoAkghBAJAIAFFBEAgBBAQQQEhAwwBCyAEIANBASABEJoBIgNFDQQLIAIgATYCTCACIAM2AkgLIAIoAkghAyAFQQBByAEQkQFBADYCyAEgACABNgIEIAAgAzYCACACQfAAaiQADwtBIEEBQbSlwAAoAgAiAEECIAAbEQAAAAtBh4zAAEEkQayMwAAQiAEACyABQQFBtKXAACgCACIAQQIgABsRAAAAC9wDAQR/IwBBoANrIgIkACACQYIDakIANwEAIAJBigNqQQA7AQAgAkGMA2pCADcCACACQZQDakIANwIAIAJBADsB/AIgAkEANgH+AiACQSA2AvgCIAJBOGogAkGQA2opAwA3AwAgAkEwaiACQYgDaikDADcDACACQShqIAJBgANqKQMANwMAIAJBQGsgAkGYA2ooAgA2AgAgAiACKQP4AjcDICACQRBqIAJBNGopAgA3AwAgAkEIaiACQSxqKQIANwMAIAJBGGogAkE8aikCADcDACACIAIpAiQ3AwAgAkEgaiABQdgCEIsBGiACQSBqIAIQZgJAAkBBIEEBEKEBIgMEQCACQiA3AiQgAiADNgIgIAJBIGogAkEgEF4CQCACKAIkIgQgAigCKCIDRgRAIAQhAwwBCyAEIANJDQIgBEUNACACKAIgIQUCQCADRQRAIAUQEEEBIQQMAQsgBSAEQQEgAxCaASIERQ0ECyACIAM2AiQgAiAENgIgCyACKAIgIQQgARAQIAAgAzYCBCAAIAQ2AgAgAkGgA2okAA8LQSBBAUG0pcAAKAIAIgBBAiAAGxEAAAALQYeMwABBJEGsjMAAEIgBAAsgA0EBQbSlwAAoAgAiAEECIAAbEQAAAAvcAwEEfyMAQaADayICJAAgAkGCA2pCADcBACACQYoDakEAOwEAIAJBjANqQgA3AgAgAkGUA2pCADcCACACQQA7AfwCIAJBADYB/gIgAkEgNgL4AiACQThqIAJBkANqKQMANwMAIAJBMGogAkGIA2opAwA3AwAgAkEoaiACQYADaikDADcDACACQUBrIAJBmANqKAIANgIAIAIgAikD+AI3AyAgAkEQaiACQTRqKQIANwMAIAJBCGogAkEsaikCADcDACACQRhqIAJBPGopAgA3AwAgAiACKQIkNwMAIAJBIGogAUHYAhCLARogAkEgaiACEGcCQAJAQSBBARChASIDBEAgAkIgNwIkIAIgAzYCICACQSBqIAJBIBBeAkAgAigCJCIEIAIoAigiA0YEQCAEIQMMAQsgBCADSQ0CIARFDQAgAigCICEFAkAgA0UEQCAFEBBBASEEDAELIAUgBEEBIAMQmgEiBEUNBAsgAiADNgIkIAIgBDYCIAsgAigCICEEIAEQECAAIAM2AgQgACAENgIAIAJBoANqJAAPC0EgQQFBtKXAACgCACIAQQIgABsRAAAAC0GHjMAAQSRBrIzAABCIAQALIANBAUG0pcAAKAIAIgBBAiAAGxEAAAAL0wMBBH8jAEHgAGsiAiQAIAJBKmpCADcBACACQTJqQQA7AQAgAkE0akIANwIAIAJBHDYCICACQTxqQQA2AgAgAkEAOwEkIAJBADYBJiACQdgAaiACQThqKQMANwMAIAJB0ABqIAJBMGopAwA3AwAgAkHIAGogAkEoaikDADcDACACIAIpAyA3A0AgAkEYaiACQdwAaigCADYCACACQRBqIAJB1ABqKQIANwMAIAJBCGogAkHMAGopAgA3AwAgAiACKQJENwMAIAEgAhBoIAFBAEHIARCRASIFQQA2AsgBAkACQEEcQQEQoQEiAQRAIAJCHDcCRCACIAE2AkAgAkFAayACQRwQXgJAIAIoAkQiAyACKAJIIgFGBEAgAyEBDAELIAMgAUkNAiADRQ0AIAIoAkAhBAJAIAFFBEAgBBAQQQEhAwwBCyAEIANBASABEJoBIgNFDQQLIAIgATYCRCACIAM2AkALIAIoAkAhAyAFQQBByAEQkQFBADYCyAEgACABNgIEIAAgAzYCACACQeAAaiQADwtBHEEBQbSlwAAoAgAiAEECIAAbEQAAAAtBh4zAAEEkQayMwAAQiAEACyABQQFBtKXAACgCACIAQQIgABsRAAAAC9MDAQR/IwBB4ABrIgIkACACQSpqQgA3AQAgAkEyakEAOwEAIAJBNGpCADcCACACQRw2AiAgAkE8akEANgIAIAJBADsBJCACQQA2ASYgAkHYAGogAkE4aikDADcDACACQdAAaiACQTBqKQMANwMAIAJByABqIAJBKGopAwA3AwAgAiACKQMgNwNAIAJBGGogAkHcAGooAgA2AgAgAkEQaiACQdQAaikCADcDACACQQhqIAJBzABqKQIANwMAIAIgAikCRDcDACABIAIQaSABQQBByAEQkQEiBUEANgLIAQJAAkBBHEEBEKEBIgEEQCACQhw3AkQgAiABNgJAIAJBQGsgAkEcEF4CQCACKAJEIgMgAigCSCIBRgRAIAMhAQwBCyADIAFJDQIgA0UNACACKAJAIQQCQCABRQRAIAQQEEEBIQMMAQsgBCADQQEgARCaASIDRQ0ECyACIAE2AkQgAiADNgJACyACKAJAIQMgBUEAQcgBEJEBQQA2AsgBIAAgATYCBCAAIAM2AgAgAkHgAGokAA8LQRxBAUG0pcAAKAIAIgBBAiAAGxEAAAALQYeMwABBJEGsjMAAEIgBAAsgAUEBQbSlwAAoAgAiAEECIAAbEQAAAAvkAwIJfwF+IwBBoAFrIgIkACACQUBrIAFBBGoQciABKAIAIQggAkH4AGoiAyABQTxqKQAANwMAIAJB8ABqIgQgAUE0aikAADcDACACQegAaiIFIAFBLGopAAA3AwAgAkHgAGoiBiABQSRqKQAANwMAIAJB2ABqIgcgAUEcaikAADcDACACIAEpABQ3A1AgAkGQAWogAUHEAGoQciACQQhqIgkgBykDADcDACACQRBqIgcgBikDADcDACACQRhqIgYgBSkDADcDACACQSBqIgUgBCkDADcDACACQShqIgQgAykDADcDACACQTBqIgMgAikDkAEiCzcDACACQThqIgogAkGYAWopAwA3AwAgAiALNwOAASACIAIpA1A3AwBB1ABBBBChASIBRQRAQdQAQQRBtKXAACgCACIAQQIgABsRAAAACyABIAg2AgAgASACKQNANwIEIAEgAikDADcCFCABQQxqIAJByABqKQMANwIAIAFBHGogCSkDADcCACABQSRqIAcpAwA3AgAgAUEsaiAGKQMANwIAIAFBNGogBSkDADcCACABQTxqIAQpAwA3AgAgAUHEAGogAykDADcCACABQcwAaiAKKQMANwIAIABB9I/AADYCBCAAIAE2AgAgAkGgAWokAAvLAwEEfyMAQaADayICJAAgAkGKA2pCADcBACACQZIDakEAOwEAIAJBlANqQgA3AgAgAkEcNgKAAyACQZwDakEANgIAIAJBADsBhAMgAkEANgGGAyACQThqIAJBmANqKQMANwMAIAJBMGogAkGQA2opAwA3AwAgAkEoaiACQYgDaikDADcDACACIAIpA4ADNwMgIAJBGGogAkE8aigCADYCACACQRBqIAJBNGopAgA3AwAgAkEIaiACQSxqKQIANwMAIAIgAikCJDcDACACQSBqIAFB4AIQiwEaIAJBIGogAhBpAkACQEEcQQEQoQEiAwRAIAJCHDcCJCACIAM2AiAgAkEgaiACQRwQXgJAIAIoAiQiBCACKAIoIgNGBEAgBCEDDAELIAQgA0kNAiAERQ0AIAIoAiAhBQJAIANFBEAgBRAQQQEhBAwBCyAFIARBASADEJoBIgRFDQQLIAIgAzYCJCACIAQ2AiALIAIoAiAhBCABEBAgACADNgIEIAAgBDYCACACQaADaiQADwtBHEEBQbSlwAAoAgAiAEECIAAbEQAAAAtBh4zAAEEkQayMwAAQiAEACyADQQFBtKXAACgCACIAQQIgABsRAAAAC8sDAQR/IwBBoANrIgIkACACQYoDakIANwEAIAJBkgNqQQA7AQAgAkGUA2pCADcCACACQRw2AoADIAJBnANqQQA2AgAgAkEAOwGEAyACQQA2AYYDIAJBOGogAkGYA2opAwA3AwAgAkEwaiACQZADaikDADcDACACQShqIAJBiANqKQMANwMAIAIgAikDgAM3AyAgAkEYaiACQTxqKAIANgIAIAJBEGogAkE0aikCADcDACACQQhqIAJBLGopAgA3AwAgAiACKQIkNwMAIAJBIGogAUHgAhCLARogAkEgaiACEGgCQAJAQRxBARChASIDBEAgAkIcNwIkIAIgAzYCICACQSBqIAJBHBBeAkAgAigCJCIEIAIoAigiA0YEQCAEIQMMAQsgBCADSQ0CIARFDQAgAigCICEFAkAgA0UEQCAFEBBBASEEDAELIAUgBEEBIAMQmgEiBEUNBAsgAiADNgIkIAIgBDYCIAsgAigCICEEIAEQECAAIAM2AgQgACAENgIAIAJBoANqJAAPC0EcQQFBtKXAACgCACIAQQIgABsRAAAAC0GHjMAAQSRBrIzAABCIAQALIANBAUG0pcAAKAIAIgBBAiAAGxEAAAALywMBBH8jAEGwAWsiAiQAIAJBmgFqQgA3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HCIEayIFIAJNBEAgAEEIaiEHIAQEQCAEQcEATw0GIAQgAEEgaiIEaiABIAUQiwEaIAcgBBAHIAIgBWshAiABIAVqIQELIAJBP3EhBSACQUBxIghBwABJDQEgAiAFa0FAaiEGIAEhBEHAACECA0AgAyACNgIMIAJBwABHDQcgByAEEAcgBkHAAEkNAiAEQUBrIQQgBkFAaiEGDAALAAsgAiAEaiIFIARJDQIgBUHAAEsNAyAAIARqQSBqIAEgAhCLARogACgCHCACaiEFDAELIABBIGogASAIaiAFEIsBGgsgACAFNgIcIANBQGskAA8LIAQgBUHAm8AAEH4ACyAFQcAAQcCbwAAQfQALIARBwABB0JvAABB+AAsgA0E0akEGNgIAIANBJGpBAjYCACADQgM3AhQgA0G4nsAANgIQIANBBjYCLCADIANBDGo2AjggA0GsjcAANgI8IAMgA0EoajYCICADIANBPGo2AjAgAyADQThqNgIoIANBEGpB4J7AABCQAQALtAMBBn8jAEFAaiIDJAAgACAAKQMAIAKtfDcDAAJAAkACQAJAAkACQEHAACAAKAIIIgRrIgUgAk0EQCAAQcwAaiEHIAQEQCAEQcEATw0GIAQgAEEMaiIEaiABIAUQiwEaIAcgBBAKIAIgBWshAiABIAVqIQELIAJBP3EhBSACQUBxIghBwABJDQEgAiAFa0FAaiEGIAEhBEHAACECA0AgAyACNgIMIAJBwABHDQcgByAEEAogBkHAAEkNAiAEQUBrIQQgBkFAaiEGDAALAAsgAiAEaiIFIARJDQIgBUHAAEsNAyAAIARqQQxqIAEgAhCLARogACgCCCACaiEFDAELIABBDGogASAIaiAFEIsBGgsgACAFNgIIIANBQGskAA8LIAQgBUHAm8AAEH4ACyAFQcAAQcCbwAAQfQALIARBwABB0JvAABB+AAsgA0E0akEGNgIAIANBJGpBAjYCACADQgM3AhQgA0G4nsAANgIQIANBBjYCLCADIANBDGo2AjggA0GsjcAANgI8IAMgA0EoajYCICADIANBPGo2AjAgAyADQThqNgIoIANBEGpB4J7AABCQAQAL0QMCBX8CfiAAQdQAaiECIABBEGohAyAAQQhqKQMAIQYgACkDACEHAkACQCAAKAJQIgFBgAFGBEAgAyACQQEQDEEAIQEgAEEA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AXFyQQJyNgIEIAAgA2oiAyADKAIEQQFyNgIEIAUgAiAFKAIAQQFxckECcjYCACAAIAAoAgRBAXI2AgQgASACEBQMAQsgASgCACEBIAAgAzYCBCAAIAEgAmo2AgALAkAgAEEEaigCACIBQQNxRQ0AIAFBeHEiAiAEQRBqTQ0AIABBBGogBCABQQFxckECcjYCACAAIARqIgEgAiAEayIEQQNyNgIEIAAgAmoiAiACKAIEQQFyNgIEIAEgBBAUCyAAQQhqIQMLIAMLiwMCBn8BfiMAQfAAayICJAAgAkHQAGoiAyABQRBqKQMANwMAIAJB2ABqIgQgAUEYaikDADcDACACQeAAaiIFIAFBIGopAwA3AwAgAkHoAGoiBiABQShqKQMANwMAIAIgASkDCDcDSCABKQMAIQggAkEIaiABQTRqEGUgASgCMCEHQfgAQQgQoQEiAUUEQEH4AEEIQbSlwAAoAgAiAEECIAAbEQAAAAsgASAINwMAIAEgAikDSDcDCCABIAc2AjAgASACKQMINwI0IAFBEGogAykDADcDACABQRhqIAQpAwA3AwAgAUEgaiAFKQMANwMAIAFBKGogBikDADcDACABQTxqIAJBEGopAwA3AgAgAUHEAGogAkEYaikDADcCACABQcwAaiACQSBqKQMANwIAIAFB1ABqIAJBKGopAwA3AgAgAUHcAGogAkEwaikDADcCACABQeQAaiACQThqKQMANwIAIAFB7ABqIAJBQGspAwA3AgAgAEHgjMAANgIEIAAgATYCACACQfAAaiQAC4YDAQR/IwBBkAFrIgIkACACQYIBakIANwEAIAJBigFqQQA7AQAgAkEAOwF8IAJBADYBfiACQRA2AnggAkEgaiACQYABaikDADcDACACQShqIAJBiAFqKAIANgIAIAJBEGogAkEkaikCADcDACACIAIpA3g3AxggAiACKQIcNwMIIAJBGGogAUHgABCLARogAkEYaiACQQhqEF0CQAJAQRBBARChASIDBEAgAkIQNwIcIAIgAzYCGCACQRhqIAJBCGpBEBBeAkAgAigCHCIEIAIoAiAiA0YEQCAEIQMMAQsgBCADSQ0CIARFDQAgAigCGCEFAkAgA0UEQCAFEBBBASEEDAELIAUgBEEBIAMQmgEiBEUNBAsgAiADNgIcIAIgBDYCGAsgAigCGCEEIAEQ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QT8gBmsQkQEaQcAAIAAoAjBrQQdNBEAgBCADEAYgACgCMCICQcEATw0CIABBNGpBACACEJEBGgsgAEHsAGogB0IDhjcCACAEIAMQBiAAQQA2AjAgASAAKAIINgAAIAEgAEEMaigCADYABCABIABBEGooAgA2AAggASAAQRRqKAIANgAMIAEgAEEYaigCADYAECABIABBHGooAgA2ABQgASAAQSBqKAIANgAYIAEgAEEkaigCADYAHCABIABBKGooAgA2ACAgASAAQSxqKAIANgAkDwsgAkHAAEGAmsAAEH4ACyACQcAAQZCawAAQfQALIAJBwABBoJrAABB8AAvwAgIGfwF+IwBBEGsiBCQAIABBDGohBSAAQcwAaiEDIAApAwAhCAJAAkAgACgCCCICQcAARgRAIAMgBRAKQQAhAiAAQQA2AggMAQsgAkE/Sw0BCyAAQQhqIgYgAmpBBGpBgAE6AAAgACAAKAIIIgdBAWoiAjYCCAJAIAJBwQBJBEAgAiAGakEEakEAQT8gB2sQkQEaQcAAIAAoAghrQQdNBEAgAyAFEAogACgCCCICQcEATw0CIABBDGpBACACEJEBGgsgAEHEAGogCEIDhjcCACADIAUQCiAAQQA2AgggBEEIaiICIABB3ABqNgIEIAIgAzYCACAEKAIMIAQoAggiAGtBAnYiA0EEIANBBEkbIgIEQEEAIQMDQCABIAAoAgA2AAAgAEEEaiEAIAFBBGohASADQQFqIgMgAkkNAAsLIARBEGokAA8LIAJBwABBgJrAABB+AAsgAkHAAEGQmsAAEH0ACyACQcAAQaCawAAQfAAL1AIBAX8gABBUIAEgACgCTCICQRh0IAJBCHRBgID8B3FyIAJBCHZBgP4DcSACQRh2cnI2AAAgASAAQdAAaigCACICQRh0IAJBCHRBgID8B3FyIAJBCHZBgP4DcSACQRh2cnI2AAQgASAAQdQAaigCACICQRh0IAJBCHRBgID8B3FyIAJBCHZBgP4DcSACQRh2cnI2AAggASAAQdgAaigCACICQRh0IAJBCHRBgID8B3FyIAJBCHZBgP4DcSACQRh2cnI2AAwgASAAQdwAaigCACICQRh0IAJBCHRBgID8B3FyIAJBCHZBgP4DcSACQRh2cnI2ABAgASAAQeAAaigCACICQRh0IAJBCHRBgID8B3FyIAJBCHZBgP4DcSACQRh2cnI2ABQgASAAQeQAaigCACIAQRh0IABBCHRBgID8B3FyIABBCHZBgP4DcSAAQRh2cnI2ABgL7AICBX8BfiMAQeAAayICJAAgASkDACEHIAJBIGogAUEMahBlIAJBCGoiAyABQdQAaikCADcDACACQRBqIgQgAUHcAGopAgA3AwAgAkEYaiIFIAFB5ABqKQIANwMAIAIgASkCTDcDACABKAIIIQZB8ABBCBChASIBRQRAQfAAQQhBtKXAACgCACIAQQIgABsRAAAACyABIAY2AgggASAHNwMAIAEgAikDIDcCDCABQRRqIAJBKGopAwA3AgAgAUEcaiACQTBqKQMANwIAIAFBJGogAkE4aikDADcCACABQSxqIAJBQGspAwA3AgAgAUE0aiACQcgAaikDADcCACABQTxqIAJB0ABqKQMANwIAIAFBxABqIAJB2ABqKQMANwIAIAFB5ABqIAUpAwA3AgAgAUHcAGogBCkDADcCACABQdQAaiADKQMANwIAIAEgAikDADcCTCAAQeCQwAA2AgQgACABNgIAIAJB4ABqJAAL7AICBX8BfiMAQeAAayICJAAgASkDACEHIAJBIGogAUEMahBlIAJBCGoiAyABQdQAaikCADcDACACQRBqIgQgAUHcAGopAgA3AwAgAkEYaiIFIAFB5ABqKQIANwMAIAIgASkCTDcDACABKAIIIQZB8ABBCBChASIBRQRAQfAAQQhBtKXAACgCACIAQQIgABsRAAAACyABIAY2AgggASAHNwMAIAEgAikDIDcCDCABQRRqIAJBKGopAwA3AgAgAUEcaiACQTBqKQMANwIAIAFBJGogAkE4aikDADcCACABQSxqIAJBQGspAwA3AgAgAUE0aiACQcgAaikDADcCACABQTxqIAJB0ABqKQMANwIAIAFBxABqIAJB2ABqKQMANwIAIAFB5ABqIAUpAwA3AgAgAUHcAGogBCkDADcCACABQdQAaiADKQMANwIAIAEgAikDADcCTCAAQYSRwAA2AgQgACABNgIAIAJB4ABqJAALyAICBH8BfiMAQeAAayICJAAgAkHQAGoiAyABQRBqKQMANwMAIAJB2ABqIgQgAUEYaigCADYCACACIAEpAwg3A0ggASkDACEGIAJBCGogAUEgahBlIAEoAhwhBUHgAEEIEKEBIgFFBEBB4ABBCEG0pcAAKAIAIgBBAiAAGxEAAAALIAEgBjcDACABIAIpA0g3AwggASAFNgIcIAEgAikDCDcDICABQRBqIAMpAwA3AwAgAUEYaiAEKAIANgIAIAFBKGogAkEQaikDADcDACABQTBqIAJBGGopAwA3AwAgAUE4aiACQSBqKQMANwMAIAFBQGsgAkEoaikDADcDACABQcgAaiACQTBqKQMANwMAIAFB0ABqIAJBOGopAwA3AwAgAUHYAGogAkFAaykDADcDACAAQYSNwAA2AgQgACABNgIAIAJB4ABqJAALyAICBH8BfiMAQeAAayICJAAgAkHQAGoiAyABQRBqKQMANwMAIAJB2ABqIgQgAUEYaigCADYCACACIAEpAwg3A0ggASkDACEGIAJBCGogAUEgahBlIAEoAhwhBUHgAEEIEKEBIgFFBEBB4ABBCEG0pcAAKAIAIgBBAiAAGxEAAAALIAEgBjcDACABIAIpA0g3AwggASAFNgIcIAEgAikDCDcDICABQRBqIAMpAwA3AwAgAUEYaiAEKAIANgIAIAFBKGogAkEQaikDADcDACABQTBqIAJBGGopAwA3AwAgAUE4aiACQSBqKQMANwMAIAFBQGsgAkEoaikDADcDACABQcgAaiACQTBqKQMANwMAIAFB0ABqIAJBOGopAwA3AwAgAUHYAGogAkFAaykDADcDACAAQbCNwAA2AgQgACABNgIAIAJB4ABqJAAL3QICBX8BfiAAQQxqIQIgAEHMAGohAyAAKQMAIQYCQAJAIAAoAggiAUHAAEYEQCADIAJBARAEQQAhASAAQQA2AggMAQsgAUE/Sw0BCyAAQQhqIgQgAWpBBGpBgAE6AAAgACAAKAIIIgVBAWoiATYCCAJAIAFBwQBJBEAgASAEakEEakEAQT8gBWsQkQEaQcAAIAAoAghrQQdNBEAgAyACQQEQBCAAKAIIIgFBwQBPDQIgAEEMakEAIAEQkQEaCyAAQcQAaiAGQiiGQoCAgICAgMD/AIMgBkI4hoQgBkIYhkKAgICAgOA/gyAGQgiGQoCAgIDwH4OEhCAGQgiIQoCAgPgPgyAGQhiIQoCA/AeDhCAGQiiIQoD+A4MgBkI4iISEhDcCACADIAJBARAEIABBADYCCA8LIAFBwABBgJrAABB+AAsgAUHAAEGQmsAAEH0ACyABQcAAQaCawAAQfAALvgICBX8BfiMAQTBrIgQkAEEnIQICQCAAQpDOAFQEQCAAIQcMAQsDQCAEQQlqIAJqIgNBfGogACAAQpDOAIAiB0LwsX9+fKciBUH//wNxQeQAbiIGQQF0QdqIwABqLwAAOwAAIANBfmogBkGcf2wgBWpB//8DcUEBdEHaiMAAai8AADsAACACQXxqIQIgAEL/wdcvViAHIQANAAsLIAenIgNB4wBKBEAgAkF+aiICIARBCWpqIAenIgVB//8DcUHkAG4iA0Gcf2wgBWpB//8DcUEBdEHaiMAAai8AADsAAAsCQCADQQpOBEAgAkF+aiICIARBCWpqIANBAXRB2ojAAGovAAA7AAAMAQsgAkF/aiICIARBCWpqIANBMGo6AAALIAFByKDAAEEAIARBCWogAmpBJyACaxAYIARBMGokAAu/AgEDfyMAQRBrIgIkAAJAIAAoAgAiAAJ/AkAgAUGAAU8EQCACQQA2AgwgAUGAEEkNASABQYCABEkEQCACIAFBP3FBgAFyOgAOIAIgAUEMdkHgAXI6AAwgAiABQQZ2QT9xQYABcjoADUEDDAMLIAIgAUE/cUGAAXI6AA8gAiABQRJ2QfABcjoADCACIAFBBnZBP3FBgAFyOgAOIAIgAUEMdkE/cUGAAXI6AA1BBAwCCyAAKAIIIgMgAEEEaigCAEYEfyAAQQEQaiAAKAIIBSADCyAAKAIAaiABOgAAIAAgACgCCEEBajYCCAwCCyACIAFBP3FBgAFyOgANIAIgAUEGdkHAAXI6AAxBAgsiARBqIABBCGoiAygCACIEIAAoAgBqIAJBDGogARCLARogAyABIARqNgIACyACQRBqJABBAAvLAgEIfyMAQYABayIBQShqIgJCADcDACABQSBqIgNCADcDACABQRhqIgRCADcDACABQRBqIgVCADcDACABQQhqIgZCADcDACABQgA3AwAgAUHaAGpCADcBACABQeIAakEAOwEAIAFBEDYCUCABQQA7AVQgAUEANgFWIAFB+ABqIAFB4ABqKAIANgIAIAFB8ABqIAFB2ABqKQMANwMAIAFByABqIgcgAUH0AGopAgA3AwAgASABKQNQNwNoIAEgASkCbDcDQCABQThqIgggBykDADcDACABIAEpA0A3AzAgAEHMAGogCCkDADcAACAAQcQAaiABKQMwNwAAIABBPGogAikDADcAACAAQTRqIAMpAwA3AAAgAEEsaiAEKQMANwAAIABBJGogBSkDADcAACAAQRxqIAYpAwA3AAAgACABKQMANwAUIABBADYCAAuxAgEDfyMAQYABayIEJAAgACgCACEAAkACQAJ/AkAgASgCACIDQRBxRQRAIAAoAgAhAiADQSBxDQEgAq0gARBVDAILIAAoAgAhAkEAIQADQCAAIARqQf8AaiACQQ9xIgNBMHIgA0HXAGogA0EKSRs6AAAgAEF/aiEAIAJBBHYiAg0ACyAAQYABaiICQYEBTw0CIAFB2IvAAEECIAAgBGpBgAFqQQAgAGsQGAwBC0EAIQADQCAAIARqQf8AaiACQQ9xIgNBMHIgA0E3aiADQQpJGzoAACAAQX9qIQAgAkEEdiICDQALIABBgAFqIgJBgQFPDQIgAUHYi8AAQQIgACAEakGAAWpBACAAaxAYCyAEQYABaiQADwsgAkGAAUHIi8AAEH4ACyACQYABQciLwAAQfgALrAICA38CfiAAIAApAwAiBiACrUIDhnwiBzcDACAAQQhqIgMgAykDACAHIAZUrXw3AwACQAJAQYABIAAoAlAiA2siBCACTQRAIABBEGoiBSADBEAgA0GBAU8NAiADIABB1ABqIgNqIAEgBBCLARogAEEANgJQIAUgA0EBEAwgAiAEayECIAEgBGohAQsgASACQQd2EAwgAkH/AHEiA0GBAU8NAiAAQdQAaiABIAJBgH9xaiADEIsBGiAAIAM2AlAPCwJAIAIgA2oiBCADTwRAIARBgAFLDQEgACADakHUAGogASACEIsBGiAAIAAoAlAgAmo2AlAPCyADIARB0JnAABB+AAsgBEGAAUHQmcAAEH0ACyADQYABQeCZwAAQfgALIANBgAFB8JnAABB9AAu8AgIFfwF+IABBIGohAyAAQQhqIQQgACkDACEHAkACQCAAKAIcIgJBwABGBEAgBCADEAdBACECIABBADYCHAwBCyACQT9LDQELIABBHGoiBSACakEEakGAAToAACAAIAAoAhwiBkEBaiICNgIcAkAgAkHBAEkEQCACIAVqQQRqQQBBPyAGaxCRARpBwAAgACgCHGtBB00EQCAEIAMQByAAKAIcIgJBwQBPDQIgAEEgakEAIAIQkQEaCyAAQdgAaiAHQgOGNwIAIAQgAxAHIABBADYCHCABIAAoAgg2AAAgASAAQQxqKAIANgAEIAEgAEEQaigCADYACCABIABBFGooAgA2AAwgASAAQRhqKAIANgAQDwsgAkHAAEGAmsAAEH4ACyACQcAAQZCawAAQfQALIAJBwABBoJrAABB8AAu1AgEDfyMAQRBrIgQkACAAKALIASICQccATQRAIAAgAmpBzAFqQQY6AAAgAkEBaiIDQcgARwRAIAAgA2pBzAFqQQBBxwAgAmsQkQEaC0EAIQIgAEEANgLIASAAQZMCaiIDIAMtAABBgAFyOgAAA0AgACACaiIDIAMtAAAgA0HMAWotAABzOgAAIAJBAWoiAkHIAEcNAAsgABAOIAEgACkAADcAACABQThqIABBOGopAAA3AAAgAUEwaiAAQTBqKQAANwAAIAFBKGogAEEoaikAADcAACABQSBqIABBIGopAAA3AAAgAUEYaiAAQRhqKQAANwAAIAFBEGogAEEQaikAADcAACABQQhqIABBCGopAAA3AAAgBEEQaiQADwtBsJrAAEEXIARBCGpByJrAAEGknsAAEHkAC7UCAQN/IwBBEGsiBCQAIAAoAsgBIgJBxwBNBEAgACACakHMAWpBAToAACACQQFqIgNByABHBEAgACADakHMAWpBAEHHACACaxCRARoLQQAhAiAAQQA2AsgBIABBkwJqIgMgAy0AAEGAAXI6AAADQCAAIAJqIgMgAy0AACADQcwBai0AAHM6AAAgAkEBaiICQcgARw0ACyAAEA4gASAAKQAANwAAIAFBOGogAEE4aikAADcAACABQTBqIABBMGopAAA3AAAgAUEoaiAAQShqKQAANwAAIAFBIGogAEEgaikAADcAACABQRhqIABBGGopAAA3AAAgAUEQaiAAQRBqKQAANwAAIAFBCGogAEEIaikAADcAACAEQRBqJAAPC0GwmsAAQRcgBEEIakHImsAAQeSdwAAQeQALswICBX8BfiAAQQxqIQMgAEHMAGohBCAAKQMAIQcCQAJAIAAoAggiAkHAAEYEQCAEIAMQDUEAIQIgAEEANgIIDAELIAJBP0sNAQsgAEEIaiIFIAJqQQRqQYABOgAAIAAgACgCCCIGQQFqIgI2AggCQCACQcEASQRAIAIgBWpBBGpBAEE/IAZrEJEBGkHAACAAKAIIa0EHTQRAIAQgAxANIAAoAggiAkHBAE8NAiAAQQxqQQAgAhCRARoLIABBxABqIAdCA4Y3AgAgBCADEA0gAEEANgIIIAEgACgCTDYAACABIABB0ABqKAIANgAEIAEgAEHUAGooAgA2AAggASAAQdgAaigCADYADA8LIAJBwABBgJrAABB+AAsgAkHAAEGQmsAAEH0ACyACQcAAQaCawAAQfAALhgIBBH8CQCAAQQRqKAIAIgYgAEEIaigCACIFayACTwRAIAAoAgAhBAwBCwJAAn8gAiAFaiIDIAVPBEBBACAGQQF0IgUgAyAFIANLGyIDQQggA0EISxsiA0EASA0BGgJAIAAoAgBBACAGGyIERQRAIANBARChASIEDQQMAQsgAyAGRg0DIAZFBEAgA0EBEKEBIgRFDQEMBAsgBCAGQQEgAxCaASIEDQMLQQEMAQtBAAsiBARAIAMgBEG0pcAAKAIAIgBBAiAAGxEAAAALEJsBAAsgACAENgIAIABBBGogAzYCACAAQQhqKAIAIQULIAQgBWogASACEIsBGiAAQQhqIAIgBWo2AgALjAIBA38gACAAKQMAIAKtQgOGfDcDAAJAAkBBwAAgACgCCCIDayIEIAJNBEAgAEHMAGoiBSADBEAgA0HBAE8NAiADIABBDGoiA2ogASAEEIsBGiAAQQA2AgggBSADQQEQBCACIARrIQIgASAEaiEBCyABIAJBBnYQBCACQT9xIgNBwQBPDQIgAEEMaiABIAJBQHFqIAMQiwEaIAAgAzYCCA8LAkAgAiADaiIEIANPBEAgBEHAAEsNASAAIANqQQxqIAEgAhCLARogACAAKAIIIAJqNgIIDwsgAyAEQdCZwAAQfgALIARBwABB0JnAABB9AAsgA0HAAEHgmcAAEH4ACyADQcAAQfCZwAAQfQALqAICA38BfiMAQdAAayICJAAgASkDACEFIAJBEGogAUEMahBlIAJBCGoiAyABQdQAaikCADcDACACIAEpAkw3AwAgASgCCCEEQeAAQQgQoQEiAUUEQEHgAEEIQbSlwAAoAgAiAEECIAAbEQAAAAsgASAENgIIIAEgBTcDACABIAIpAxA3AgwgAUEUaiACQRhqKQMANwIAIAFBHGogAkEgaikDADcCACABQSRqIAJBKGopAwA3AgAgAUEsaiACQTBqKQMANwIAIAFBNGogAkE4aikDADcCACABQTxqIAJBQGspAwA3AgAgAUHEAGogAkHIAGopAwA3AgAgAUHUAGogAykDADcCACABIAIpAwA3AkwgAEG8jMAANgIEIAAgATYCACACQdAAaiQAC4gCAQN/IAAgACkDACACrXw3AwACQAJAQcAAIAAoAhwiA2siBCACTQRAIABBCGoiBSADBEAgA0HBAE8NAiADIABBIGoiA2ogASAEEIsBGiAAQQA2AhwgBSADQQEQCCACIARrIQIgASAEaiEBCyABIAJBBnYQCCACQT9xIgNBwQBPDQIgAEEgaiABIAJBQHFqIAMQiwEaIAAgAzYCHA8LAkAgAiADaiIEIANPBEAgBEHAAEsNASAAIANqQSBqIAEgAhCLARogACAAKAIcIAJqNgIcDwsgAyAEQdCZwAAQfgALIARBwABB0JnAABB9AAsgA0HAAEHgmcAAEH4ACyADQcAAQfCZwAAQfQALqAICA38BfiMAQdAAayICJAAgASkDACEFIAJBEGogAUEMahBlIAJBCGoiAyABQdQAaikCADcDACACIAEpAkw3AwAgASgCCCEEQeAAQQgQoQEiAUUEQEHgAEEIQbSlwAAoAgAiAEECIAAbEQAAAAsgASAENgIIIAEgBTcDACABIAIpAxA3AgwgAUEUaiACQRhqKQMANwIAIAFBHGogAkEgaikDADcCACABQSRqIAJBKGopAwA3AgAgAUEsaiACQTBqKQMANwIAIAFBNGogAkE4aikDADcCACABQTxqIAJBQGspAwA3AgAgAUHEAGogAkHIAGopAwA3AgAgAUHUAGogAykDADcCACABIAIpAwA3AkwgAEGokcAANgIEIAAgATYCACACQdAAaiQAC5UCAQN/IwBBEGsiBCQAIAAoAsgBIgJB5wBNBEAgACACakHMAWpBBjoAACACQQFqIgNB6ABHBEAgACADakHMAWpBAEHnACACaxCRARoLQQAhAiAAQQA2AsgBIABBswJqIgMgAy0AAEGAAXI6AAADQCAAIAJqIgMgAy0AACADQcwBai0AAHM6AAAgAkEBaiICQegARw0ACyAAEA4gASAAKQAANwAAIAFBKGogAEEoaikAADcAACABQSBqIABBIGopAAA3AAAgAUEYaiAAQRhqKQAANwAAIAFBEGogAEEQaikAADcAACABQQhqIABBCGopAAA3AAAgBEEQaiQADwtBsJrAAEEXIARBCGpByJrAAEGUnsAAEHkAC5UCAQN/IwBBEGsiBCQAIAAoAsgBIgJB5wBNBEAgACACakHMAWpBAToAACACQQFqIgNB6ABHBEAgACADakHMAWpBAEHnACACaxCRARoLQQAhAiAAQQA2AsgBIABBswJqIgMgAy0AAEGAAXI6AAADQCAAIAJqIgMgAy0AACADQcwBai0AAHM6AAAgAkEBaiICQegARw0ACyAAEA4gASAAKQAANwAAIAFBKGogAEEoaikAADcAACABQSBqIABBIGopAAA3AAAgAUEYaiAAQRhqKQAANwAAIAFBEGogAEEQaikAADcAACABQQhqIABBCGopAAA3AAAgBEEQaiQADwtBsJrAAEEXIARBCGpByJrAAEHUncAAEHkAC/MBAQR/IwBBkAFrIgIkACACQQA2AgAgAkEEciEFA0AgAyAFaiABIANqLQAAOgAAIAIgAigCAEEBaiIENgIAIANBAWoiA0HAAEcNAAsgBEE/TQRAIARBwAAQfwALIAJByABqIAJBxAAQiwEaIABBOGogAkGEAWopAgA3AAAgAEEwaiACQfwAaikCADcAACAAQShqIAJB9ABqKQIANwAAIABBIGogAkHsAGopAgA3AAAgAEEYaiACQeQAaikCADcAACAAQRBqIAJB3ABqKQIANwAAIABBCGogAkHUAGopAgA3AAAgACACKQJMNwAAIAJBkAFqJAAL9QEBA38jAEEQayIEJAAgACgCyAEiAkGHAU0EQCAAIAJqQcwBakEBOgAAIAJBAWoiA0GIAUcEQCAAIANqQcwBakEAQYcBIAJrEJEBGgtBACECIABBADYCyAEgAEHTAmoiAyADLQAAQYABcjoAAANAIAAgAmoiAyADLQAAIANBzAFqLQAAczoAACACQQFqIgJBiAFHDQALIAAQDiABIAApAAA3AAAgAUEYaiAAQRhqKQAANwAAIAFBEGogAEEQaikAADcAACABQQhqIABBCGopAAA3AAAgBEEQaiQADwtBsJrAAEEXIARBCGpByJrAAEHEncAAEHkAC/UBAQN/IwBBEGsiBCQAIAAoAsgBIgJBhwFNBEAgACACakHMAWpBBjoAACACQQFqIgNBiAFHBEAgACADakHMAWpBAEGHASACaxCRARoLQQAhAiAAQQA2AsgBIABB0wJqIgMgAy0AAEGAAXI6AAADQCAAIAJqIgMgAy0AACADQcwBai0AAHM6AAAgAkEBaiICQYgBRw0ACyAAEA4gASAAKQAANwAAIAFBGGogAEEYaikAADcAACABQRBqIABBEGopAAA3AAAgAUEIaiAAQQhqKQAANwAAIARBEGokAA8LQbCawABBFyAEQQhqQciawABBhJ7AABB5AAv1AQEDfyMAQRBrIgQkACAAKALIASICQY8BTQRAIAAgAmpBzAFqQQE6AAAgAkEBaiIDQZABRwRAIAAgA2pBzAFqQQBBjwEgAmsQkQEaC0EAIQIgAEEANgLIASAAQdsCaiIDIAMtAABBgAFyOgAAA0AgACACaiIDIAMtAAAgA0HMAWotAABzOgAAIAJBAWoiAkGQAUcNAAsgABAOIAEgACkAADcAACABQRhqIABBGGooAAA2AAAgAUEQaiAAQRBqKQAANwAAIAFBCGogAEEIaikAADcAACAEQRBqJAAPC0GwmsAAQRcgBEEIakHImsAAQdiawAAQeQAL9QEBA38jAEEQayIEJAAgACgCyAEiAkGPAU0EQCAAIAJqQcwBakEGOgAAIAJBAWoiA0GQAUcEQCAAIANqQcwBakEAQY8BIAJrEJEBGgtBACECIABBADYCyAEgAEHbAmoiAyADLQAAQYABcjoAAANAIAAgAmoiAyADLQAAIANBzAFqLQAAczoAACACQQFqIgJBkAFHDQALIAAQDiABIAApAAA3AAAgAUEYaiAAQRhqKAAANgAAIAFBEGogAEEQaikAADcAACABQQhqIABBCGopAAA3AAAgBEEQaiQADwtBsJrAAEEXIARBCGpByJrAAEH0ncAAEHkAC8MBAQJ/AkACQCAAQQRqKAIAIgMgACgCCCICayABSQRAIAEgAmoiASACSQ0BIANBAXQiAiABIAIgAUsbIgFBCCABQQhLGyICQQBIDQECQCAAKAIAQQAgAxsiAUUEQCACQQEQoQEhAQwBCyACIANGDQAgA0UEQCACQQEQoQEhAQwBCyABIANBASACEJoBIQELIAFFDQIgACABNgIAIABBBGogAjYCAAsPCxCbAQALIAJBAUG0pcAAKAIAIgBBAiAAGxEAAAALhQEBBH8jAEGgAWsiAiQAIAJBADYCACACQQRyIQUDQCADIAVqIAEgA2otAAA6AAAgAiACKAIAQQFqIgQ2AgAgA0EBaiIDQcgARw0A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JAAgAkGIAWogAUHIARCLARogAiABQcwBahBuIAEoAsgBIQNB2AJBCBChASIBRQRAQdgCQQhBtKXAACgCACIAQQIgABsRAAAACyABIAJBiAFqQcgBEIsBIgEgAzYCyAEgAUHMAWogAkGIARCLARogAEGsj8AANgIEIAAgATYCACACQdACaiQAC34BAX8jAEFAaiIFJAAgBSABNgIMIAUgADYCCCAFIAM2AhQgBSACNgIQIAVBLGpBAjYCACAFQTxqQQQ2AgAgBUICNwIcIAVB8IvAADYCGCAFQQE2AjQgBSAFQTBqNgIoIAUgBUEQajYCOCAFIAVBCGo2AjAgBUEYaiAEEJABAAuVAQAgAEIANwMIIABCADcDACAAQQA2AlAgAEGQmcAAKQMANwMQIABBGGpBmJnAACkDADcDACAAQSBqQaCZwAApAwA3AwAgAEEoakGomcAAKQMANwMAIABBMGpBsJnAACkDADcDACAAQThqQbiZwAApAwA3AwAgAEFAa0HAmcAAKQMANwMAIABByABqQciZwAApAwA3AwALlQEAIABCADcDCCAAQgA3AwAgAEEANgJQIABB0JjAACkDADcDECAAQRhqQdiYwAApAwA3AwAgAEEgakHgmMAAKQMANwMAIABBKGpB6JjAACkDADcDACAAQTBqQfCYwAApAwA3AwAgAEE4akH4mMAAKQMANwMAIABBQGtBgJnAACkDADcDACAAQcgAakGImcAAKQMANwMAC20BAX8jAEEwayIDJAAgAyABNgIEIAMgADYCACADQRxqQQI2AgAgA0EsakEFNgIAIANCAjcCDCADQYiIwAA2AgggA0EFNgIkIAMgA0EgajYCGCADIAM2AiggAyADQQRqNgIgIANBCGogAhCQAQALbQEBfyMAQTBrIgMkACADIAE2AgQgAyAANgIAIANBHGpBAjYCACADQSxqQQU2AgAgA0ICNwIMIANBpIrAADYCCCADQQU2AiQgAyADQSBqNgIYIAMgA0EEajYCKCADIAM2AiAgA0EIaiACEJABAAttAQF/IwBBMGsiAyQAIAMgATYCBCADIAA2AgAgA0EcakECNgIAIANBLGpBBTYCACADQgI3AgwgA0HcisAANgIIIANBBTYCJCADIANBIGo2AhggAyADQQRqNgIoIAMgAzYCICADQQhqIAIQkAEAC3ABAX8jAEEwayICJAAgAiABNgIEIAIgADYCACACQRxqQQI2AgAgAkEsakEFNgIAIAJCAjcCDCACQcyRwAA2AgggAkEFNgIkIAIgAkEgajYCGCACIAJBBGo2AiggAiACNgIgIAJBCGpB3JHAABCQAQALVAEBfyMAQSBrIgIkACACIAAoAgA2AgQgAkEYaiABQRBqKQIANwMAIAJBEGogAUEIaikCADcDACACIAEpAgA3AwggAkEEaiACQQhqEBYgAkEgaiQAC30BAn9BASEAQeChwABB4KHAACgCAEEBajYCAAJAAkBBqKXAACgCAEEBRwRAQailwABCgYCAgBA3AwAMAQtBrKXAAEGspcAAKAIAQQFqIgA2AgAgAEECSw0BC0GwpcAAKAIAIgFBf0wNAEGwpcAAIAE2AgAgAEEBSw0AAAsAC2ICAX8BfiMAQRBrIgIkAAJAIAEEQCABKAIADQEgAUF/NgIAIAJBCGogASgCBCABQQhqKAIAKAIQEQAAIAIpAwghAyABQQA2AgAgACADNwIAIAJBEGokAA8LEJ0BAAsQngEAC0MBA38CQCACRQ0AA0AgAC0AACIEIAEtAAAiBUYEQCAAQQFqIQAgAUEBaiEBIAJBf2oiAg0BDAILCyAEIAVrIQMLIAMLSwECfwJAIAAEQCAAKAIADQEgAEEANgIAIAAoAgQhASAAKAIIIQIgABAQIAEgAigCABEEACACKAIEBEAgARAQCw8LEJ0BAAsQngEAC0gAAkAgAARAIAAoAgANASAAQX82AgAgACgCBCABIAIgAEEIaigCACgCDBECACACBEAgARAQCyAAQQA2AgAPCxCdAQALEJ4BAAtKAAJ/IAFBgIDEAEcEQEEBIAAoAhggASAAQRxqKAIAKAIQEQEADQEaCyACRQRAQQAPCyAAKAIYIAIgAyAAQRxqKAIAKAIMEQMACwtdACAAQgA3AwAgAEEANgIwIABB0JfAACkDADcDCCAAQRBqQdiXwAApAwA3AwAgAEEYakHgl8AAKQMANwMAIABBIGpB6JfAACkDADcDACAAQShqQfCXwAApAwA3AwALSAEBfyMAQSBrIgMkACADQRRqQQA2AgAgA0HIoMAANgIQIANCATcCBCADIAE2AhwgAyAANgIYIAMgA0EYajYCACADIAIQkAEAC1AAIABBADYCCCAAQgA3AwAgAEGsmMAAKQIANwJMIABB1ABqQbSYwAApAgA3AgAgAEHcAGpBvJjAACkCADcCACAAQeQAakHEmMAAKQIANwIAC1AAIABBADYCCCAAQgA3AwAgAEGMmMAAKQIANwJMIABB1ABqQZSYwAApAgA3AgAgAEHcAGpBnJjAACkCADcCACAAQeQAakGkmMAAKQIANwIACzMBAX8gAgRAIAAhAwNAIAMgAS0AADoAACABQQFqIQEgA0EBaiEDIAJBf2oiAg0ACwsgAAs1AQJ/IAAoAgAiACACEGogAEEIaiIDKAIAIgQgACgCAGogASACEIsBGiADIAIgBGo2AgBBAAsrAAJAIABBfEsNACAARQRAQQQPCyAAIABBfUlBAnQQoQEiAEUNACAADwsACz0AIABCADcDACAAQQA2AhwgAEH4l8AAKQMANwMIIABBEGpBgJjAACkDADcDACAAQRhqQYiYwAAoAgA2AgALPQAgAEEANgIcIABCADcDACAAQRhqQYiYwAAoAgA2AgAgAEEQakGAmMAAKQMANwMAIABB+JfAACkDADcDCAtMAQF/IwBBEGsiAiQAIAIgATYCDCACIAA2AgggAkGYiMAANgIEIAJByKDAADYCACACKAIIRQRAQZ2gwABBK0HIoMAAEIgBAAsQgQEACykBAX8gAgRAIAAhAwNAIAMgAToAACADQQFqIQMgAkF/aiICDQALCyAACy4AIABBADYCCCAAQgA3AwAgAEHUAGpByJfAACkCADcCACAAQcCXwAApAgA3AkwLIAACQCABQXxLDQAgACABQQQgAhCaASIARQ0AIAAPCwALHAAgASgCGEH/h8AAQQggAUEcaigCACgCDBEDAAscACABKAIYQYKMwABBBSABQRxqKAIAKAIMEQMACxQAIAAoAgAgASAAKAIEKAIMEQEACxAAIAEgACgCACAAKAIEEBILEgAgAEEAQcgBEJEBQQA2AsgBCwsAIAEEQCAAEBALCwwAIAAgASACIAMQFwsSAEHEhsAAQRFB2IbAABCIAQALDgAgACgCABoDQAwACwALDQBB76DAAEEbEKABAAsOAEGKocAAQc8AEKABAAsLACAANQIAIAEQVQsJACAAIAEQAQALGQACfyABQQlPBEAgASAAEEYMAQsgABAJCwsNAEKtqduM/5imovgACwQAQRALBABBKAsEAEEUCwUAQcAACwQA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AAAvaG9tZS9sdWNhY2Fzb25hdG8vLmNhcmdvL3JlZ2lzdHJ5L3NyYy9naXRodWIuY29tLTFlY2M2Mjk5ZGI5ZWM4MjMvYmxvY2stYnVmZmVyLTAuOS4wL3NyYy9saWIucnMvaG9tZS9sdWNhY2Fzb25hdG8vLnJ1c3R1cC90b29sY2hhaW5zL3N0YWJsZS14ODZfNjQtdW5rbm93bi1saW51eC1nbnUvbGliL3J1c3RsaWIvc3JjL3J1c3Qvc3JjL2xpYmNvcmUvc2xpY2UvbW9kLnJzAGgNEABYAAAASAAAAAEAAABoDRAAWAAAAE8AAAABAAAAaA0QAFgAAABWAAAAAQAAAGgNEABYAAAAZgAAAAEAAABoDRAAWAAAAG0AAAABAAAAaA0QAFgAAAB0AAAAAQAAAGgNEABYAAAAewAAAAEAAACQAAAA5A8QAC0AAAAREBAADAAAAFAPEAABAAAAYAAAAIgAAABoAAAASAAAAHAPEAB0AAAAEAAAAAkAAAAvaG9tZS9sdWNhY2Fzb25hdG8vLnJ1c3R1cC90b29sY2hhaW5zL3N0YWJsZS14ODZfNjQtdW5rbm93bi1saW51eC1nbnUvbGliL3J1c3RsaWIvc3JjL3J1c3Qvc3JjL2xpYmNvcmUvbWFjcm9zL21vZC5yc2Fzc2VydGlvbiBmYWlsZWQ6IGAobGVmdCA9PSByaWdodClgCiAgbGVmdDogYGAsCiByaWdodDogYGNhbGxlZCBgT3B0aW9uOjp1bndyYXAoKWAgb24gYSBgTm9uZWAgdmFsdWVYEBAAFwAAALQBAAAeAAAAc3JjL2xpYnN0ZC9wYW5pY2tpbmcucnNudWxsIHBvaW50ZXIgcGFzc2VkIHRvIHJ1c3RyZWN1cnNpdmUgdXNlIG9mIGFuIG9iamVjdCBkZXRlY3RlZCB3aGljaCB3b3VsZCBsZWFkIHRvIHVuc2FmZSBhbGlhc2luZyBpbiBydXN0AHsJcHJvZHVjZXJzAghsYW5ndWFnZQEEUnVzdAAMcHJvY2Vzc2VkLWJ5AwVydXN0Yx0xLjQ2LjAgKDA0NDg4YWZlMyAyMDIwLTA4LTI0KQZ3YWxydXMGMC4xOC4wDHdhc20tYmluZGdlbhIwLjIuNjggKGEwNGUxODk3MSk="); -let wasm; - -let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); - -cachedTextDecoder.decode(); - -let cachegetUint8Memory0 = null; -function getUint8Memory0() { - if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) { - cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer); - } - return cachegetUint8Memory0; -} - -function getStringFromWasm0(ptr, len) { - return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); -} - -const heap = new Array(32).fill(undefined); - -heap.push(undefined, null, true, false); - -let heap_next = heap.length; - -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - -function getObject(idx) { return heap[idx]; } - -function dropObject(idx) { - if (idx < 36) return; - heap[idx] = heap_next; - heap_next = idx; -} - -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} - -let WASM_VECTOR_LEN = 0; - -let cachedTextEncoder = new TextEncoder('utf-8'); - -const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' - ? function (arg, view) { - return cachedTextEncoder.encodeInto(arg, view); -} - : function (arg, view) { - const buf = cachedTextEncoder.encode(arg); - view.set(buf); - return { - read: arg.length, - written: buf.length - }; -}); - -function passStringToWasm0(arg, malloc, realloc) { - - if (realloc === undefined) { - const buf = cachedTextEncoder.encode(arg); - const ptr = malloc(buf.length); - getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); - WASM_VECTOR_LEN = buf.length; - return ptr; - } - - let len = arg.length; - let ptr = malloc(len); - - const mem = getUint8Memory0(); - - let offset = 0; - - for (; offset < len; offset++) { - const code = arg.charCodeAt(offset); - if (code > 0x7F) break; - mem[ptr + offset] = code; - } - - if (offset !== len) { - if (offset !== 0) { - arg = arg.slice(offset); - } - ptr = realloc(ptr, len, len = offset + arg.length * 3); - const view = getUint8Memory0().subarray(ptr + offset, ptr + len); - const ret = encodeString(arg, view); - - offset += ret.written; - } - - WASM_VECTOR_LEN = offset; - return ptr; -} -/** -* @param {string} algorithm -* @returns {DenoHash} -*/ -export function create_hash(algorithm) { - var ptr0 = passStringToWasm0(algorithm, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len0 = WASM_VECTOR_LEN; - var ret = wasm.create_hash(ptr0, len0); - return DenoHash.__wrap(ret); -} - -function _assertClass(instance, klass) { - if (!(instance instanceof klass)) { - throw new Error(`expected instance of ${klass.name}`); - } - return instance.ptr; -} - -function passArray8ToWasm0(arg, malloc) { - const ptr = malloc(arg.length * 1); - getUint8Memory0().set(arg, ptr / 1); - WASM_VECTOR_LEN = arg.length; - return ptr; -} -/** -* @param {DenoHash} hash -* @param {Uint8Array} data -*/ -export function update_hash(hash, data) { - _assertClass(hash, DenoHash); - var ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc); - var len0 = WASM_VECTOR_LEN; - wasm.update_hash(hash.ptr, ptr0, len0); -} - -let cachegetInt32Memory0 = null; -function getInt32Memory0() { - if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) { - cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); - } - return cachegetInt32Memory0; -} - -function getArrayU8FromWasm0(ptr, len) { - return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); -} -/** -* @param {DenoHash} hash -* @returns {Uint8Array} -*/ -export function digest_hash(hash) { - try { - const retptr = wasm.__wbindgen_export_2.value - 16; - wasm.__wbindgen_export_2.value = retptr; - _assertClass(hash, DenoHash); - wasm.digest_hash(retptr, hash.ptr); - var r0 = getInt32Memory0()[retptr / 4 + 0]; - var r1 = getInt32Memory0()[retptr / 4 + 1]; - var v0 = getArrayU8FromWasm0(r0, r1).slice(); - wasm.__wbindgen_free(r0, r1 * 1); - return v0; - } finally { - wasm.__wbindgen_export_2.value += 16; - } -} - -/** -*/ -export class DenoHash { - - static __wrap(ptr) { - const obj = Object.create(DenoHash.prototype); - obj.ptr = ptr; - - return obj; - } - - free() { - const ptr = this.ptr; - this.ptr = 0; - - wasm.__wbg_denohash_free(ptr); - } -} - -async function load(module, imports) { - if (typeof Response === 'function' && module instanceof Response) { - - if (typeof WebAssembly.instantiateStreaming === 'function') { - try { - return await WebAssembly.instantiateStreaming(module, imports); - - } catch (e) { - if (module.headers.get('Content-Type') != 'application/wasm') { - console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); - - } else { - throw e; - } - } - } - - const bytes = await module.arrayBuffer(); - return await WebAssembly.instantiate(bytes, imports); - - } else { - - const instance = await WebAssembly.instantiate(module, imports); - - if (instance instanceof WebAssembly.Instance) { - return { instance, module }; - - } else { - return instance; - } - } -} - -async function init(input) { - if (typeof input === 'undefined') { - input = import.meta.url.replace(/\.js$/, '_bg.wasm'); - } - const imports = {}; - imports.wbg = {}; - imports.wbg.__wbindgen_string_new = function(arg0, arg1) { - var ret = getStringFromWasm0(arg0, arg1); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_throw = function(arg0, arg1) { - throw new Error(getStringFromWasm0(arg0, arg1)); - }; - imports.wbg.__wbindgen_rethrow = function(arg0) { - throw takeObject(arg0); - }; - - if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { - input = fetch(input); - } - - const { instance, module } = await load(await input, imports); - - wasm = instance.exports; - init.__wbindgen_wasm_module = module; - - return wasm; -} - -export default init; - diff --git a/std/hash/fnv.ts b/std/hash/fnv.ts deleted file mode 100644 index 8c0ba21c1a..0000000000 --- a/std/hash/fnv.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -export { Fnv32, Fnv32a } from "./_fnv/fnv32.ts"; -export { Fnv64, Fnv64a } from "./_fnv/fnv64.ts"; diff --git a/std/hash/fnv_test.ts b/std/hash/fnv_test.ts deleted file mode 100644 index 923c63d9e2..0000000000 --- a/std/hash/fnv_test.ts +++ /dev/null @@ -1,111 +0,0 @@ -// Ported from Go: -// https://github.com/golang/go/tree/go1.13.10/src/hash/fnv/fnv_test.go -// Copyright 2011 The Go Authors. All rights reserved. BSD license. -// https://github.com/golang/go/blob/master/LICENSE -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { Fnv32, Fnv32a, Fnv64, Fnv64a } from "./fnv.ts"; - -const golden32 = [ - ["", [0x81, 0x1c, 0x9d, 0xc5]], - ["a", [0x05, 0x0c, 0x5d, 0x7e]], - ["ab", [0x70, 0x77, 0x2d, 0x38]], - ["abc", [0x43, 0x9c, 0x2f, 0x4b]], - ["deno", [0x6e, 0xd5, 0xa7, 0xa9]], -]; - -const golden32a = [ - ["", [0x81, 0x1c, 0x9d, 0xc5]], - ["a", [0xe4, 0x0c, 0x29, 0x2c]], - ["ab", [0x4d, 0x25, 0x05, 0xca]], - ["abc", [0x1a, 0x47, 0xe9, 0x0b]], - ["deno", [0x8e, 0xf6, 0x47, 0x11]], -]; - -const golden64 = [ - ["", [0xcb, 0xf2, 0x9c, 0xe4, 0x84, 0x22, 0x23, 0x25]], - ["a", [0xaf, 0x63, 0xbd, 0x4c, 0x86, 0x01, 0xb7, 0xbe]], - ["ab", [0x08, 0x32, 0x67, 0x07, 0xb4, 0xeb, 0x37, 0xb8]], - ["abc", [0xd8, 0xdc, 0xca, 0x18, 0x6b, 0xaf, 0xad, 0xcb]], - ["deno", [0x14, 0xed, 0xb2, 0x7e, 0xec, 0xda, 0xad, 0xc9]], -]; - -const golden64a = [ - ["", [0xcb, 0xf2, 0x9c, 0xe4, 0x84, 0x22, 0x23, 0x25]], - ["a", [0xaf, 0x63, 0xdc, 0x4c, 0x86, 0x01, 0xec, 0x8c]], - ["ab", [0x08, 0x9c, 0x44, 0x07, 0xb5, 0x45, 0x98, 0x6a]], - ["abc", [0xe7, 0x1f, 0xa2, 0x19, 0x05, 0x41, 0x57, 0x4b]], - ["deno", [0xa5, 0xd9, 0xfb, 0x67, 0x42, 0x6e, 0x48, 0xb1]], -]; - -Deno.test("[hash/fnv] testFnv32", () => { - for (const [input, output] of golden32) { - const fnv = new Fnv32(); - fnv.write(new TextEncoder().encode(input as string)); - assertEquals(fnv.sum(), output); - } -}); - -Deno.test("[hash/fnv] testFnv32a", () => { - for (const [input, output] of golden32a) { - const fnv = new Fnv32a(); - fnv.write(new TextEncoder().encode(input as string)); - assertEquals(fnv.sum(), output); - } -}); - -Deno.test("[hash/fnv] testFnv64", () => { - for (const [input, output] of golden64) { - const fnv = new Fnv64(); - fnv.write(new TextEncoder().encode(input as string)); - assertEquals(fnv.sum(), output); - } -}); - -Deno.test("[hash/fnv] testFnv64a", () => { - for (const [input, output] of golden64a) { - const fnv = new Fnv64a(); - fnv.write(new TextEncoder().encode(input as string)); - assertEquals(fnv.sum(), output); - } -}); - -Deno.test("[hash/fnv] testFnv32WriteChain", () => { - const fnv = new Fnv32(); - fnv - .write(new TextEncoder().encode("d")) - .write(new TextEncoder().encode("e")) - .write(new TextEncoder().encode("n")) - .write(new TextEncoder().encode("o")); - assertEquals(fnv.sum(), [0x6e, 0xd5, 0xa7, 0xa9]); -}); - -Deno.test("[hash/fnv] testFnv32aWriteChain", () => { - const fnv = new Fnv32a(); - fnv - .write(new TextEncoder().encode("d")) - .write(new TextEncoder().encode("e")) - .write(new TextEncoder().encode("n")) - .write(new TextEncoder().encode("o")); - assertEquals(fnv.sum(), [0x8e, 0xf6, 0x47, 0x11]); -}); - -Deno.test("[hash/fnv] testFnv64WriteChain", () => { - const fnv = new Fnv64(); - fnv - .write(new TextEncoder().encode("d")) - .write(new TextEncoder().encode("e")) - .write(new TextEncoder().encode("n")) - .write(new TextEncoder().encode("o")); - assertEquals(fnv.sum(), [0x14, 0xed, 0xb2, 0x7e, 0xec, 0xda, 0xad, 0xc9]); -}); - -Deno.test("[hash/fnv] testFnv64aWriteChain", () => { - const fnv = new Fnv64a(); - fnv - .write(new TextEncoder().encode("d")) - .write(new TextEncoder().encode("e")) - .write(new TextEncoder().encode("n")) - .write(new TextEncoder().encode("o")); - assertEquals(fnv.sum(), [0xa5, 0xd9, 0xfb, 0x67, 0x42, 0x6e, 0x48, 0xb1]); -}); diff --git a/std/hash/hasher.ts b/std/hash/hasher.ts deleted file mode 100644 index e04cb56ef6..0000000000 --- a/std/hash/hasher.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -export type Message = string | ArrayBuffer; -export type OutputFormat = "hex" | "base64"; - -export interface Hasher { - update(data: Message): this; - digest(): ArrayBuffer; - toString(format?: OutputFormat): string; -} diff --git a/std/hash/md5.ts b/std/hash/md5.ts deleted file mode 100644 index 36984d3ab1..0000000000 --- a/std/hash/md5.ts +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import * as hex from "../encoding/hex.ts"; - -const TYPE_ERROR_MSG = "md5: `data` is invalid type"; -const BLOCK_SIZE = 64; - -export type Message = string | ArrayBuffer; - -/** Md5 hash */ -export class Md5 { - #a: number; - #b: number; - #c: number; - #d: number; - #block: Uint8Array; - #pos: number; - #n0: number; - #n1: number; - - constructor() { - this.#a = 0x67452301; - this.#b = 0xefcdab89; - this.#c = 0x98badcfe; - this.#d = 0x10325476; - this.#block = new Uint8Array(BLOCK_SIZE); - this.#pos = 0; - this.#n0 = 0; - this.#n1 = 0; - } - - private addLength(len: number): void { - let n0 = this.#n0; - n0 += len; - if (n0 > 0xffffffff) this.#n1 += 1; - this.#n0 = n0 >>> 0; - } - - private hash(block: Uint8Array): void { - let a = this.#a; - let b = this.#b; - let c = this.#c; - let d = this.#d; - - const blk = (i: number): number => - block[i] | - (block[i + 1] << 8) | - (block[i + 2] << 16) | - (block[i + 3] << 24); - - const rol32 = (x: number, n: number): number => (x << n) | (x >>> (32 - n)); - - const x0 = blk(0); - const x1 = blk(4); - const x2 = blk(8); - const x3 = blk(12); - const x4 = blk(16); - const x5 = blk(20); - const x6 = blk(24); - const x7 = blk(28); - const x8 = blk(32); - const x9 = blk(36); - const xa = blk(40); - const xb = blk(44); - const xc = blk(48); - const xd = blk(52); - const xe = blk(56); - const xf = blk(60); - - // round 1 - a = b + rol32((((c ^ d) & b) ^ d) + a + x0 + 0xd76aa478, 7); - d = a + rol32((((b ^ c) & a) ^ c) + d + x1 + 0xe8c7b756, 12); - c = d + rol32((((a ^ b) & d) ^ b) + c + x2 + 0x242070db, 17); - b = c + rol32((((d ^ a) & c) ^ a) + b + x3 + 0xc1bdceee, 22); - a = b + rol32((((c ^ d) & b) ^ d) + a + x4 + 0xf57c0faf, 7); - d = a + rol32((((b ^ c) & a) ^ c) + d + x5 + 0x4787c62a, 12); - c = d + rol32((((a ^ b) & d) ^ b) + c + x6 + 0xa8304613, 17); - b = c + rol32((((d ^ a) & c) ^ a) + b + x7 + 0xfd469501, 22); - a = b + rol32((((c ^ d) & b) ^ d) + a + x8 + 0x698098d8, 7); - d = a + rol32((((b ^ c) & a) ^ c) + d + x9 + 0x8b44f7af, 12); - c = d + rol32((((a ^ b) & d) ^ b) + c + xa + 0xffff5bb1, 17); - b = c + rol32((((d ^ a) & c) ^ a) + b + xb + 0x895cd7be, 22); - a = b + rol32((((c ^ d) & b) ^ d) + a + xc + 0x6b901122, 7); - d = a + rol32((((b ^ c) & a) ^ c) + d + xd + 0xfd987193, 12); - c = d + rol32((((a ^ b) & d) ^ b) + c + xe + 0xa679438e, 17); - b = c + rol32((((d ^ a) & c) ^ a) + b + xf + 0x49b40821, 22); - - // round 2 - a = b + rol32((((b ^ c) & d) ^ c) + a + x1 + 0xf61e2562, 5); - d = a + rol32((((a ^ b) & c) ^ b) + d + x6 + 0xc040b340, 9); - c = d + rol32((((d ^ a) & b) ^ a) + c + xb + 0x265e5a51, 14); - b = c + rol32((((c ^ d) & a) ^ d) + b + x0 + 0xe9b6c7aa, 20); - a = b + rol32((((b ^ c) & d) ^ c) + a + x5 + 0xd62f105d, 5); - d = a + rol32((((a ^ b) & c) ^ b) + d + xa + 0x02441453, 9); - c = d + rol32((((d ^ a) & b) ^ a) + c + xf + 0xd8a1e681, 14); - b = c + rol32((((c ^ d) & a) ^ d) + b + x4 + 0xe7d3fbc8, 20); - a = b + rol32((((b ^ c) & d) ^ c) + a + x9 + 0x21e1cde6, 5); - d = a + rol32((((a ^ b) & c) ^ b) + d + xe + 0xc33707d6, 9); - c = d + rol32((((d ^ a) & b) ^ a) + c + x3 + 0xf4d50d87, 14); - b = c + rol32((((c ^ d) & a) ^ d) + b + x8 + 0x455a14ed, 20); - a = b + rol32((((b ^ c) & d) ^ c) + a + xd + 0xa9e3e905, 5); - d = a + rol32((((a ^ b) & c) ^ b) + d + x2 + 0xfcefa3f8, 9); - c = d + rol32((((d ^ a) & b) ^ a) + c + x7 + 0x676f02d9, 14); - b = c + rol32((((c ^ d) & a) ^ d) + b + xc + 0x8d2a4c8a, 20); - - // round 3 - a = b + rol32((b ^ c ^ d) + a + x5 + 0xfffa3942, 4); - d = a + rol32((a ^ b ^ c) + d + x8 + 0x8771f681, 11); - c = d + rol32((d ^ a ^ b) + c + xb + 0x6d9d6122, 16); - b = c + rol32((c ^ d ^ a) + b + xe + 0xfde5380c, 23); - a = b + rol32((b ^ c ^ d) + a + x1 + 0xa4beea44, 4); - d = a + rol32((a ^ b ^ c) + d + x4 + 0x4bdecfa9, 11); - c = d + rol32((d ^ a ^ b) + c + x7 + 0xf6bb4b60, 16); - b = c + rol32((c ^ d ^ a) + b + xa + 0xbebfbc70, 23); - a = b + rol32((b ^ c ^ d) + a + xd + 0x289b7ec6, 4); - d = a + rol32((a ^ b ^ c) + d + x0 + 0xeaa127fa, 11); - c = d + rol32((d ^ a ^ b) + c + x3 + 0xd4ef3085, 16); - b = c + rol32((c ^ d ^ a) + b + x6 + 0x04881d05, 23); - a = b + rol32((b ^ c ^ d) + a + x9 + 0xd9d4d039, 4); - d = a + rol32((a ^ b ^ c) + d + xc + 0xe6db99e5, 11); - c = d + rol32((d ^ a ^ b) + c + xf + 0x1fa27cf8, 16); - b = c + rol32((c ^ d ^ a) + b + x2 + 0xc4ac5665, 23); - - // round 4 - a = b + rol32((c ^ (b | ~d)) + a + x0 + 0xf4292244, 6); - d = a + rol32((b ^ (a | ~c)) + d + x7 + 0x432aff97, 10); - c = d + rol32((a ^ (d | ~b)) + c + xe + 0xab9423a7, 15); - b = c + rol32((d ^ (c | ~a)) + b + x5 + 0xfc93a039, 21); - a = b + rol32((c ^ (b | ~d)) + a + xc + 0x655b59c3, 6); - d = a + rol32((b ^ (a | ~c)) + d + x3 + 0x8f0ccc92, 10); - c = d + rol32((a ^ (d | ~b)) + c + xa + 0xffeff47d, 15); - b = c + rol32((d ^ (c | ~a)) + b + x1 + 0x85845dd1, 21); - a = b + rol32((c ^ (b | ~d)) + a + x8 + 0x6fa87e4f, 6); - d = a + rol32((b ^ (a | ~c)) + d + xf + 0xfe2ce6e0, 10); - c = d + rol32((a ^ (d | ~b)) + c + x6 + 0xa3014314, 15); - b = c + rol32((d ^ (c | ~a)) + b + xd + 0x4e0811a1, 21); - a = b + rol32((c ^ (b | ~d)) + a + x4 + 0xf7537e82, 6); - d = a + rol32((b ^ (a | ~c)) + d + xb + 0xbd3af235, 10); - c = d + rol32((a ^ (d | ~b)) + c + x2 + 0x2ad7d2bb, 15); - b = c + rol32((d ^ (c | ~a)) + b + x9 + 0xeb86d391, 21); - - this.#a = (this.#a + a) >>> 0; - this.#b = (this.#b + b) >>> 0; - this.#c = (this.#c + c) >>> 0; - this.#d = (this.#d + d) >>> 0; - } - - /** - * Update internal state - * @param data data to update, data cannot exceed 2^32 bytes - */ - update(data: Message): this { - let msg: Uint8Array; - - if (typeof data === "string") { - msg = new TextEncoder().encode(data as string); - } else if (typeof data === "object") { - if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) { - msg = new Uint8Array(data); - } else { - throw new Error(TYPE_ERROR_MSG); - } - } else { - throw new Error(TYPE_ERROR_MSG); - } - - let pos = this.#pos; - const free = BLOCK_SIZE - pos; - - if (msg.length < free) { - this.#block.set(msg, pos); - pos += msg.length; - } else { - // hash first block - this.#block.set(msg.slice(0, free), pos); - this.hash(this.#block); - - // hash as many blocks as possible - let i = free; - while (i + BLOCK_SIZE <= msg.length) { - this.hash(msg.slice(i, i + BLOCK_SIZE)); - i += BLOCK_SIZE; - } - - // store leftover - this.#block.fill(0).set(msg.slice(i), 0); - pos = msg.length - i; - } - - this.#pos = pos; - this.addLength(msg.length); - - return this; - } - - /** Returns final hash */ - digest(): ArrayBuffer { - let padLen = BLOCK_SIZE - this.#pos; - if (padLen < 9) padLen += BLOCK_SIZE; - - const pad = new Uint8Array(padLen); - - pad[0] = 0x80; - - const n0 = this.#n0 << 3; - const n1 = (this.#n1 << 3) | (this.#n0 >>> 29); - pad[pad.length - 8] = n0 & 0xff; - pad[pad.length - 7] = (n0 >>> 8) & 0xff; - pad[pad.length - 6] = (n0 >>> 16) & 0xff; - pad[pad.length - 5] = (n0 >>> 24) & 0xff; - pad[pad.length - 4] = n1 & 0xff; - pad[pad.length - 3] = (n1 >>> 8) & 0xff; - pad[pad.length - 2] = (n1 >>> 16) & 0xff; - pad[pad.length - 1] = (n1 >>> 24) & 0xff; - - this.update(pad.buffer); - - const hash = new ArrayBuffer(16); - const hashView = new DataView(hash); - hashView.setUint32(0, this.#a, true); - hashView.setUint32(4, this.#b, true); - hashView.setUint32(8, this.#c, true); - hashView.setUint32(12, this.#d, true); - - return hash; - } - - /** - * Returns hash as a string of given format - * @param format format of output string (hex or base64). Default is hex - */ - toString(format: "hex" | "base64" = "hex"): string { - const hash = this.digest(); - - switch (format) { - case "hex": - return hex.encodeToString(new Uint8Array(hash)); - case "base64": { - const data = new Uint8Array(hash); - let dataString = ""; - for (let i = 0; i < data.length; ++i) { - dataString += String.fromCharCode(data[i]); - } - return btoa(dataString); - } - default: - throw new Error("md5: invalid format"); - } - } -} diff --git a/std/hash/md5_test.ts b/std/hash/md5_test.ts deleted file mode 100644 index ebdc873ddb..0000000000 --- a/std/hash/md5_test.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { Md5 } from "./md5.ts"; - -const millionAs = "a".repeat(1000000); - -const testSetHex = [ - ["", "d41d8cd98f00b204e9800998ecf8427e"], - ["abc", "900150983cd24fb0d6963f7d28e17f72"], - ["deno", "c8772b401bc911da102a5291cc4ec83b"], - [ - "The quick brown fox jumps over the lazy dog", - "9e107d9d372bb6826bd81d3542a419d6", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "3b0c8ac703f828b04c6c197006d17218", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "014842d480b571495a4a0363793f7367", - ], - [millionAs, "7707d6ae4e027c70eea2a935c2296f21"], -]; - -const testSetBase64 = [ - ["", "1B2M2Y8AsgTpgAmY7PhCfg=="], - ["abc", "kAFQmDzST7DWlj99KOF/cg=="], - ["deno", "yHcrQBvJEdoQKlKRzE7IOw=="], - ["The quick brown fox jumps over the lazy dog", "nhB9nTcrtoJr2B01QqQZ1g=="], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "OwyKxwP4KLBMbBlwBtFyGA==", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "AUhC1IC1cUlaSgNjeT9zZw==", - ], - [millionAs, "dwfWrk4CfHDuoqk1wilvIQ=="], -]; - -Deno.test("[hash/md5] testMd5Hex", () => { - for (const [input, output] of testSetHex) { - const md5 = new Md5(); - assertEquals(md5.update(input).toString(), output); - } -}); - -Deno.test("[hash/md5] testMd5Base64", () => { - for (const [input, output] of testSetBase64) { - const md5 = new Md5(); - assertEquals(md5.update(input).toString("base64"), output); - } -}); diff --git a/std/hash/mod.ts b/std/hash/mod.ts deleted file mode 100644 index 7ab4dbbc47..0000000000 --- a/std/hash/mod.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { Hash } from "./_wasm/hash.ts"; -import type { Hasher } from "./hasher.ts"; - -export type { Hasher } from "./hasher.ts"; -export type SupportedAlgorithm = - | "md2" - | "md4" - | "md5" - | "ripemd160" - | "ripemd320" - | "sha1" - | "sha224" - | "sha256" - | "sha384" - | "sha512" - | "sha3-224" - | "sha3-256" - | "sha3-384" - | "sha3-512" - | "keccak224" - | "keccak256" - | "keccak384" - | "keccak512"; - -/** - * Creates a new `Hash` instance. - * - * @param algorithm name of hash algorithm to use - */ -export function createHash(algorithm: SupportedAlgorithm): Hasher { - return new Hash(algorithm as string); -} diff --git a/std/hash/sha1.ts b/std/hash/sha1.ts deleted file mode 100644 index 91b1f0de98..0000000000 --- a/std/hash/sha1.ts +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -/* - * [js-sha1]{@link https://github.com/emn178/js-sha1} - * - * @version 0.6.0 - * @author Chen, Yi-Cyuan [emn178@gmail.com] - * @copyright Chen, Yi-Cyuan 2014-2017 - * @license MIT - */ - -export type Message = string | number[] | ArrayBuffer; - -const HEX_CHARS = "0123456789abcdef".split(""); -const EXTRA = [-2147483648, 8388608, 32768, 128] as const; -const SHIFT = [24, 16, 8, 0] as const; - -const blocks: number[] = []; - -export class Sha1 { - #blocks!: number[]; - #block!: number; - #start!: number; - #bytes!: number; - #hBytes!: number; - #finalized!: boolean; - #hashed!: boolean; - - #h0 = 0x67452301; - #h1 = 0xefcdab89; - #h2 = 0x98badcfe; - #h3 = 0x10325476; - #h4 = 0xc3d2e1f0; - #lastByteIndex = 0; - - constructor(sharedMemory = false) { - this.init(sharedMemory); - } - protected init(sharedMemory: boolean) { - if (sharedMemory) { - // deno-fmt-ignore - blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - this.#blocks = blocks; - } else { - this.#blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - } - - this.#h0 = 0x67452301; - this.#h1 = 0xefcdab89; - this.#h2 = 0x98badcfe; - this.#h3 = 0x10325476; - this.#h4 = 0xc3d2e1f0; - - this.#block = this.#start = this.#bytes = this.#hBytes = 0; - this.#finalized = this.#hashed = false; - } - update(message: Message): this { - if (this.#finalized) { - return this; - } - - let msg: string | number[] | Uint8Array | undefined; - if (message instanceof ArrayBuffer) { - msg = new Uint8Array(message); - } else { - msg = message; - } - - let index = 0; - const length = msg.length; - const blocks = this.#blocks; - - while (index < length) { - let i: number; - if (this.#hashed) { - this.#hashed = false; - blocks[0] = this.#block; - // deno-fmt-ignore - blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - } - - if (typeof msg !== "string") { - for (i = this.#start; index < length && i < 64; ++index) { - blocks[i >> 2] |= msg[index] << SHIFT[i++ & 3]; - } - } else { - for (i = this.#start; index < length && i < 64; ++index) { - let code = msg.charCodeAt(index); - if (code < 0x80) { - blocks[i >> 2] |= code << SHIFT[i++ & 3]; - } else if (code < 0x800) { - blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } else if (code < 0xd800 || code >= 0xe000) { - blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } else { - code = 0x10000 + - (((code & 0x3ff) << 10) | (msg.charCodeAt(++index) & 0x3ff)); - blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } - } - } - - this.#lastByteIndex = i; - this.#bytes += i - this.#start; - if (i >= 64) { - this.#block = blocks[16]; - this.#start = i - 64; - this.hash(); - this.#hashed = true; - } else { - this.#start = i; - } - } - if (this.#bytes > 4294967295) { - this.#hBytes += (this.#bytes / 4294967296) >>> 0; - this.#bytes = this.#bytes >>> 0; - } - return this; - } - - protected finalize(): void { - if (this.#finalized) { - return; - } - this.#finalized = true; - const blocks = this.#blocks; - const i = this.#lastByteIndex; - blocks[16] = this.#block; - blocks[i >> 2] |= EXTRA[i & 3]; - this.#block = blocks[16]; - if (i >= 56) { - if (!this.#hashed) { - this.hash(); - } - blocks[0] = this.#block; - // deno-fmt-ignore - blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - } - blocks[14] = (this.#hBytes << 3) | (this.#bytes >>> 29); - blocks[15] = this.#bytes << 3; - this.hash(); - } - - private hash(): void { - let a = this.#h0; - let b = this.#h1; - let c = this.#h2; - let d = this.#h3; - let e = this.#h4; - let f: number; - let j: number; - let t: number; - const blocks = this.#blocks; - - for (j = 16; j < 80; ++j) { - t = blocks[j - 3] ^ blocks[j - 8] ^ blocks[j - 14] ^ blocks[j - 16]; - blocks[j] = (t << 1) | (t >>> 31); - } - - for (j = 0; j < 20; j += 5) { - f = (b & c) | (~b & d); - t = (a << 5) | (a >>> 27); - e = (t + f + e + 1518500249 + blocks[j]) >>> 0; - b = (b << 30) | (b >>> 2); - - f = (a & b) | (~a & c); - t = (e << 5) | (e >>> 27); - d = (t + f + d + 1518500249 + blocks[j + 1]) >>> 0; - a = (a << 30) | (a >>> 2); - - f = (e & a) | (~e & b); - t = (d << 5) | (d >>> 27); - c = (t + f + c + 1518500249 + blocks[j + 2]) >>> 0; - e = (e << 30) | (e >>> 2); - - f = (d & e) | (~d & a); - t = (c << 5) | (c >>> 27); - b = (t + f + b + 1518500249 + blocks[j + 3]) >>> 0; - d = (d << 30) | (d >>> 2); - - f = (c & d) | (~c & e); - t = (b << 5) | (b >>> 27); - a = (t + f + a + 1518500249 + blocks[j + 4]) >>> 0; - c = (c << 30) | (c >>> 2); - } - - for (; j < 40; j += 5) { - f = b ^ c ^ d; - t = (a << 5) | (a >>> 27); - e = (t + f + e + 1859775393 + blocks[j]) >>> 0; - b = (b << 30) | (b >>> 2); - - f = a ^ b ^ c; - t = (e << 5) | (e >>> 27); - d = (t + f + d + 1859775393 + blocks[j + 1]) >>> 0; - a = (a << 30) | (a >>> 2); - - f = e ^ a ^ b; - t = (d << 5) | (d >>> 27); - c = (t + f + c + 1859775393 + blocks[j + 2]) >>> 0; - e = (e << 30) | (e >>> 2); - - f = d ^ e ^ a; - t = (c << 5) | (c >>> 27); - b = (t + f + b + 1859775393 + blocks[j + 3]) >>> 0; - d = (d << 30) | (d >>> 2); - - f = c ^ d ^ e; - t = (b << 5) | (b >>> 27); - a = (t + f + a + 1859775393 + blocks[j + 4]) >>> 0; - c = (c << 30) | (c >>> 2); - } - - for (; j < 60; j += 5) { - f = (b & c) | (b & d) | (c & d); - t = (a << 5) | (a >>> 27); - e = (t + f + e - 1894007588 + blocks[j]) >>> 0; - b = (b << 30) | (b >>> 2); - - f = (a & b) | (a & c) | (b & c); - t = (e << 5) | (e >>> 27); - d = (t + f + d - 1894007588 + blocks[j + 1]) >>> 0; - a = (a << 30) | (a >>> 2); - - f = (e & a) | (e & b) | (a & b); - t = (d << 5) | (d >>> 27); - c = (t + f + c - 1894007588 + blocks[j + 2]) >>> 0; - e = (e << 30) | (e >>> 2); - - f = (d & e) | (d & a) | (e & a); - t = (c << 5) | (c >>> 27); - b = (t + f + b - 1894007588 + blocks[j + 3]) >>> 0; - d = (d << 30) | (d >>> 2); - - f = (c & d) | (c & e) | (d & e); - t = (b << 5) | (b >>> 27); - a = (t + f + a - 1894007588 + blocks[j + 4]) >>> 0; - c = (c << 30) | (c >>> 2); - } - - for (; j < 80; j += 5) { - f = b ^ c ^ d; - t = (a << 5) | (a >>> 27); - e = (t + f + e - 899497514 + blocks[j]) >>> 0; - b = (b << 30) | (b >>> 2); - - f = a ^ b ^ c; - t = (e << 5) | (e >>> 27); - d = (t + f + d - 899497514 + blocks[j + 1]) >>> 0; - a = (a << 30) | (a >>> 2); - - f = e ^ a ^ b; - t = (d << 5) | (d >>> 27); - c = (t + f + c - 899497514 + blocks[j + 2]) >>> 0; - e = (e << 30) | (e >>> 2); - - f = d ^ e ^ a; - t = (c << 5) | (c >>> 27); - b = (t + f + b - 899497514 + blocks[j + 3]) >>> 0; - d = (d << 30) | (d >>> 2); - - f = c ^ d ^ e; - t = (b << 5) | (b >>> 27); - a = (t + f + a - 899497514 + blocks[j + 4]) >>> 0; - c = (c << 30) | (c >>> 2); - } - - this.#h0 = (this.#h0 + a) >>> 0; - this.#h1 = (this.#h1 + b) >>> 0; - this.#h2 = (this.#h2 + c) >>> 0; - this.#h3 = (this.#h3 + d) >>> 0; - this.#h4 = (this.#h4 + e) >>> 0; - } - - hex(): string { - this.finalize(); - - const h0 = this.#h0; - const h1 = this.#h1; - const h2 = this.#h2; - const h3 = this.#h3; - const h4 = this.#h4; - - return ( - HEX_CHARS[(h0 >> 28) & 0x0f] + - HEX_CHARS[(h0 >> 24) & 0x0f] + - HEX_CHARS[(h0 >> 20) & 0x0f] + - HEX_CHARS[(h0 >> 16) & 0x0f] + - HEX_CHARS[(h0 >> 12) & 0x0f] + - HEX_CHARS[(h0 >> 8) & 0x0f] + - HEX_CHARS[(h0 >> 4) & 0x0f] + - HEX_CHARS[h0 & 0x0f] + - HEX_CHARS[(h1 >> 28) & 0x0f] + - HEX_CHARS[(h1 >> 24) & 0x0f] + - HEX_CHARS[(h1 >> 20) & 0x0f] + - HEX_CHARS[(h1 >> 16) & 0x0f] + - HEX_CHARS[(h1 >> 12) & 0x0f] + - HEX_CHARS[(h1 >> 8) & 0x0f] + - HEX_CHARS[(h1 >> 4) & 0x0f] + - HEX_CHARS[h1 & 0x0f] + - HEX_CHARS[(h2 >> 28) & 0x0f] + - HEX_CHARS[(h2 >> 24) & 0x0f] + - HEX_CHARS[(h2 >> 20) & 0x0f] + - HEX_CHARS[(h2 >> 16) & 0x0f] + - HEX_CHARS[(h2 >> 12) & 0x0f] + - HEX_CHARS[(h2 >> 8) & 0x0f] + - HEX_CHARS[(h2 >> 4) & 0x0f] + - HEX_CHARS[h2 & 0x0f] + - HEX_CHARS[(h3 >> 28) & 0x0f] + - HEX_CHARS[(h3 >> 24) & 0x0f] + - HEX_CHARS[(h3 >> 20) & 0x0f] + - HEX_CHARS[(h3 >> 16) & 0x0f] + - HEX_CHARS[(h3 >> 12) & 0x0f] + - HEX_CHARS[(h3 >> 8) & 0x0f] + - HEX_CHARS[(h3 >> 4) & 0x0f] + - HEX_CHARS[h3 & 0x0f] + - HEX_CHARS[(h4 >> 28) & 0x0f] + - HEX_CHARS[(h4 >> 24) & 0x0f] + - HEX_CHARS[(h4 >> 20) & 0x0f] + - HEX_CHARS[(h4 >> 16) & 0x0f] + - HEX_CHARS[(h4 >> 12) & 0x0f] + - HEX_CHARS[(h4 >> 8) & 0x0f] + - HEX_CHARS[(h4 >> 4) & 0x0f] + - HEX_CHARS[h4 & 0x0f] - ); - } - - toString(): string { - return this.hex(); - } - - digest(): number[] { - this.finalize(); - - const h0 = this.#h0; - const h1 = this.#h1; - const h2 = this.#h2; - const h3 = this.#h3; - const h4 = this.#h4; - - return [ - (h0 >> 24) & 0xff, - (h0 >> 16) & 0xff, - (h0 >> 8) & 0xff, - h0 & 0xff, - (h1 >> 24) & 0xff, - (h1 >> 16) & 0xff, - (h1 >> 8) & 0xff, - h1 & 0xff, - (h2 >> 24) & 0xff, - (h2 >> 16) & 0xff, - (h2 >> 8) & 0xff, - h2 & 0xff, - (h3 >> 24) & 0xff, - (h3 >> 16) & 0xff, - (h3 >> 8) & 0xff, - h3 & 0xff, - (h4 >> 24) & 0xff, - (h4 >> 16) & 0xff, - (h4 >> 8) & 0xff, - h4 & 0xff, - ]; - } - - array(): number[] { - return this.digest(); - } - - arrayBuffer(): ArrayBuffer { - this.finalize(); - - const buffer = new ArrayBuffer(20); - const dataView = new DataView(buffer); - dataView.setUint32(0, this.#h0); - dataView.setUint32(4, this.#h1); - dataView.setUint32(8, this.#h2); - dataView.setUint32(12, this.#h3); - dataView.setUint32(16, this.#h4); - - return buffer; - } -} -export class HmacSha1 extends Sha1 { - #sharedMemory: boolean; - #inner: boolean; - #oKeyPad: number[]; - constructor(secretKey: Message, sharedMemory = false) { - super(sharedMemory); - let key: number[] | Uint8Array | undefined; - if (typeof secretKey === "string") { - const bytes: number[] = []; - const length: number = secretKey.length; - let index = 0; - for (let i = 0; i < length; i++) { - let code = secretKey.charCodeAt(i); - if (code < 0x80) { - bytes[index++] = code; - } else if (code < 0x800) { - bytes[index++] = 0xc0 | (code >> 6); - bytes[index++] = 0x80 | (code & 0x3f); - } else if (code < 0xd800 || code >= 0xe000) { - bytes[index++] = 0xe0 | (code >> 12); - bytes[index++] = 0x80 | ((code >> 6) & 0x3f); - bytes[index++] = 0x80 | (code & 0x3f); - } else { - code = 0x10000 + - (((code & 0x3ff) << 10) | (secretKey.charCodeAt(++i) & 0x3ff)); - bytes[index++] = 0xf0 | (code >> 18); - bytes[index++] = 0x80 | ((code >> 12) & 0x3f); - bytes[index++] = 0x80 | ((code >> 6) & 0x3f); - bytes[index++] = 0x80 | (code & 0x3f); - } - } - key = bytes; - } else { - if (secretKey instanceof ArrayBuffer) { - key = new Uint8Array(secretKey); - } else { - key = secretKey; - } - } - if (key.length > 64) { - key = new Sha1(true).update(key).array(); - } - const oKeyPad: number[] = []; - const iKeyPad: number[] = []; - for (let i = 0; i < 64; i++) { - const b = key[i] || 0; - oKeyPad[i] = 0x5c ^ b; - iKeyPad[i] = 0x36 ^ b; - } - - this.update(iKeyPad); - this.#oKeyPad = oKeyPad; - this.#inner = true; - this.#sharedMemory = sharedMemory; - } - protected finalize(): void { - super.finalize(); - if (this.#inner) { - this.#inner = false; - const innerHash = this.array(); - super.init(this.#sharedMemory); - this.update(this.#oKeyPad); - this.update(innerHash); - super.finalize(); - } - } -} diff --git a/std/hash/sha1_test.ts b/std/hash/sha1_test.ts deleted file mode 100644 index fc66df0bfa..0000000000 --- a/std/hash/sha1_test.ts +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { HmacSha1, Message, Sha1 } from "./sha1.ts"; -import { dirname, fromFileUrl, join, resolve } from "../path/mod.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); -const testdataDir = resolve(moduleDir, "testdata"); - -/** Handy function to convert an array/array buffer to a string of hex values. */ -function toHexString(value: number[] | ArrayBuffer): string { - const array = new Uint8Array(value); - let hex = ""; - for (const v of array) { - const c = v.toString(16); - hex += c.length === 1 ? `0${c}` : c; - } - return hex; -} - -// deno-fmt-ignore -const fixtures: { - sha1: Record>; - sha1Hmac: Record>; -} = { - sha1: { - "ascii": { - "da39a3ee5e6b4b0d3255bfef95601890afd80709": "", - "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12": "The quick brown fox jumps over the lazy dog", - "408d94384216f890ff7a0c3528e8bed1e0b01621": "The quick brown fox jumps over the lazy dog." - }, - "ascii more than 64 bytes": { - "8690faab7755408a03875895176fac318f14a699": "The MD5 message-digest algorithm is a widely used cryptographic hash function producing a 128-bit (16-byte) hash value, typically expressed in text format as a 32 digit hexadecimal number. MD5 has been utilized in a wide variety of cryptographic applications, and is also commonly used to verify data integrity." - }, - "UTF8": { - "7be2d2d20c106eee0836c9bc2b939890a78e8fb3": "中文", - "9e4e5d978deced901d621475b03f1ded19e945bf": "aécio", - "4667688a63420661469c8dbc0f871770349bab08": "𠜎" - }, - "UTF8 more than 64 bytes": { - "ad8aae581c915fe01c4964a5e8b322cae74ee5c5": "訊息摘要演算法第五版(英語:Message-Digest Algorithm 5,縮寫為MD5),是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一", - "3a15ad3ce9efdd4bf982eaaaecdeda36a887a3f9": "訊息摘要演算法第五版(英語:Message-Digest Algorithm 5,縮寫為MD5),是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一(又譯雜湊演算法、摘要演算法等),主流程式語言普遍已有MD5的實作。" - }, - "special length": { - "4cdeae78e8b7285aef73e0a15eec7d5b30f3f3e3": "0123456780123456780123456780123456780123456780123456780", - "e657e6bb6b5d0c2bf7e929451c14a5302589a60b": "01234567801234567801234567801234567801234567801234567801", - "e7ad97591c1a99d54d80751d341899769884c75a": "0123456780123456780123456780123456780123456780123456780123456780", - "55a13698cdc010c0d16dab2f7dc10f43a713f12f": "01234567801234567801234567801234567801234567801234567801234567801234567", - "006575418c27b0158e55a6d261c46f86b33a496a": "012345678012345678012345678012345678012345678012345678012345678012345678" - }, - "Array": { - "da39a3ee5e6b4b0d3255bfef95601890afd80709": [], - '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12': [84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103], - '55a13698cdc010c0d16dab2f7dc10f43a713f12f': [48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55] - }, - "Uint8Array": { - '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12': new Uint8Array([84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103]) - }, - "Int8Array": { - '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12': new Int8Array([84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103]) - }, - "ArrayBuffer": { - '5ba93c9db0cff93f52b521d7420e43f6eda2784f': new ArrayBuffer(1) - } - }, - sha1Hmac:{ - - "Test Vectors": { - "b617318655057264e28bc0b6fb378c8ef146be00": [ - [0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b], - "Hi There" - ], - "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79": [ - "Jefe", - "what do ya want for nothing?" - ], - "125d7342b9ac11cd91a39af48aa17b4f63f175d3": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - [0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd] - ], - "4c9007f4026250c6bc8414f9bf50c86c2d7235da": [ - [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19], - [0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd] - ], - "90d0dace1c1bdc957339307803160335bde6df2b": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - "Test Using Larger Than Block-Size Key - Hash Key First" - ], - "217e44bb08b6e06a2d6c30f3cb9f537f97c63356": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm." - ] - }, - "UTF8": { - "f495de6d0f1b5681070a024bbaed5b5f42847306": ["中文", "中文"], - "58891d68487ffebddba5925abedec77a5a578db2": ["aécio", "aécio"], - "a1816bff2dae324c283aeab564d5edb5170fbada": ["𠜎", "𠜎"] - } - }, -}; - -const methods = ["array", "arrayBuffer", "digest", "hex"] as const; - -for (const method of methods) { - for (const [name, tests] of Object.entries(fixtures.sha1)) { - let i = 1; - for (const [expected, message] of Object.entries(tests)) { - Deno.test({ - name: `sha1.${method}() - ${name} - #${i++}`, - fn() { - const algorithm = new Sha1(); - algorithm.update(message); - const actual = method === "hex" - ? algorithm[method]() - : toHexString(algorithm[method]()); - assertEquals(actual, expected); - }, - }); - } - } -} - -for (const method of methods) { - for (const [name, tests] of Object.entries(fixtures.sha1)) { - let i = 1; - for (const [expected, message] of Object.entries(tests)) { - Deno.test({ - name: `sha1.${method}() - ${name} - #${i++}`, - fn() { - const algorithm = new Sha1(true); - algorithm.update(message); - const actual = method === "hex" - ? algorithm[method]() - : toHexString(algorithm[method]()); - assertEquals(actual, expected); - }, - }); - } - } -} - -for (const method of methods) { - for (const [name, tests] of Object.entries(fixtures.sha1Hmac)) { - let i = 1; - for (const [expected, [key, message]] of Object.entries(tests)) { - Deno.test({ - name: `hmacSha1.${method}() - ${name} - #${i++}`, - fn() { - const algorithm = new HmacSha1(key); - algorithm.update(message); - const actual = method === "hex" - ? algorithm[method]() - : toHexString(algorithm[method]()); - assertEquals(actual, expected); - }, - }); - } - } -} - -Deno.test("[hash/sha1] test Uint8Array from Reader", async () => { - const data = await Deno.readFile(join(testdataDir, "hashtest")); - - const hash = new Sha1().update(data).hex(); - assertEquals(hash, "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"); -}); diff --git a/std/hash/sha256.ts b/std/hash/sha256.ts deleted file mode 100644 index a566f838a5..0000000000 --- a/std/hash/sha256.ts +++ /dev/null @@ -1,543 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -/* - * Adapted to deno from: - * - * [js-sha256]{@link https://github.com/emn178/js-sha256} - * - * @version 0.9.0 - * @author Chen, Yi-Cyuan [emn178@gmail.com] - * @copyright Chen, Yi-Cyuan 2014-2017 - * @license MIT - */ - -export type Message = string | number[] | ArrayBuffer; - -const HEX_CHARS = "0123456789abcdef".split(""); -const EXTRA = [-2147483648, 8388608, 32768, 128] as const; -const SHIFT = [24, 16, 8, 0] as const; -// deno-fmt-ignore -const K = [ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, - 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, - 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, - 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, - 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, - 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, -] as const; - -const blocks: number[] = []; - -export class Sha256 { - #block!: number; - #blocks!: number[]; - #bytes!: number; - #finalized!: boolean; - #first!: boolean; - #h0!: number; - #h1!: number; - #h2!: number; - #h3!: number; - #h4!: number; - #h5!: number; - #h6!: number; - #h7!: number; - #hashed!: boolean; - #hBytes!: number; - #is224!: boolean; - #lastByteIndex = 0; - #start!: number; - - constructor(is224 = false, sharedMemory = false) { - this.init(is224, sharedMemory); - } - - protected init(is224: boolean, sharedMemory: boolean): void { - if (sharedMemory) { - // deno-fmt-ignore - blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - this.#blocks = blocks; - } else { - this.#blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - } - - if (is224) { - this.#h0 = 0xc1059ed8; - this.#h1 = 0x367cd507; - this.#h2 = 0x3070dd17; - this.#h3 = 0xf70e5939; - this.#h4 = 0xffc00b31; - this.#h5 = 0x68581511; - this.#h6 = 0x64f98fa7; - this.#h7 = 0xbefa4fa4; - } else { - // 256 - this.#h0 = 0x6a09e667; - this.#h1 = 0xbb67ae85; - this.#h2 = 0x3c6ef372; - this.#h3 = 0xa54ff53a; - this.#h4 = 0x510e527f; - this.#h5 = 0x9b05688c; - this.#h6 = 0x1f83d9ab; - this.#h7 = 0x5be0cd19; - } - - this.#block = this.#start = this.#bytes = this.#hBytes = 0; - this.#finalized = this.#hashed = false; - this.#first = true; - this.#is224 = is224; - } - - /** Update hash - * - * @param message The message you want to hash. - */ - update(message: Message): this { - if (this.#finalized) { - return this; - } - - let msg: string | number[] | Uint8Array | undefined; - if (message instanceof ArrayBuffer) { - msg = new Uint8Array(message); - } else { - msg = message; - } - - let index = 0; - const length = msg.length; - const blocks = this.#blocks; - - while (index < length) { - let i: number; - if (this.#hashed) { - this.#hashed = false; - blocks[0] = this.#block; - // deno-fmt-ignore - blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - } - - if (typeof msg !== "string") { - for (i = this.#start; index < length && i < 64; ++index) { - blocks[i >> 2] |= msg[index] << SHIFT[i++ & 3]; - } - } else { - for (i = this.#start; index < length && i < 64; ++index) { - let code = msg.charCodeAt(index); - if (code < 0x80) { - blocks[i >> 2] |= code << SHIFT[i++ & 3]; - } else if (code < 0x800) { - blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } else if (code < 0xd800 || code >= 0xe000) { - blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } else { - code = 0x10000 + - (((code & 0x3ff) << 10) | (msg.charCodeAt(++index) & 0x3ff)); - blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } - } - } - - this.#lastByteIndex = i; - this.#bytes += i - this.#start; - if (i >= 64) { - this.#block = blocks[16]; - this.#start = i - 64; - this.hash(); - this.#hashed = true; - } else { - this.#start = i; - } - } - if (this.#bytes > 4294967295) { - this.#hBytes += (this.#bytes / 4294967296) << 0; - this.#bytes = this.#bytes % 4294967296; - } - return this; - } - - protected finalize(): void { - if (this.#finalized) { - return; - } - this.#finalized = true; - const blocks = this.#blocks; - const i = this.#lastByteIndex; - blocks[16] = this.#block; - blocks[i >> 2] |= EXTRA[i & 3]; - this.#block = blocks[16]; - if (i >= 56) { - if (!this.#hashed) { - this.hash(); - } - blocks[0] = this.#block; - // deno-fmt-ignore - blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - } - blocks[14] = (this.#hBytes << 3) | (this.#bytes >>> 29); - blocks[15] = this.#bytes << 3; - this.hash(); - } - - protected hash(): void { - let a = this.#h0; - let b = this.#h1; - let c = this.#h2; - let d = this.#h3; - let e = this.#h4; - let f = this.#h5; - let g = this.#h6; - let h = this.#h7; - const blocks = this.#blocks; - let s0: number; - let s1: number; - let maj: number; - let t1: number; - let t2: number; - let ch: number; - let ab: number; - let da: number; - let cd: number; - let bc: number; - - for (let j = 16; j < 64; ++j) { - // rightrotate - t1 = blocks[j - 15]; - s0 = ((t1 >>> 7) | (t1 << 25)) ^ ((t1 >>> 18) | (t1 << 14)) ^ (t1 >>> 3); - t1 = blocks[j - 2]; - s1 = ((t1 >>> 17) | (t1 << 15)) ^ ((t1 >>> 19) | (t1 << 13)) ^ - (t1 >>> 10); - blocks[j] = (blocks[j - 16] + s0 + blocks[j - 7] + s1) << 0; - } - - bc = b & c; - for (let j = 0; j < 64; j += 4) { - if (this.#first) { - if (this.#is224) { - ab = 300032; - t1 = blocks[0] - 1413257819; - h = (t1 - 150054599) << 0; - d = (t1 + 24177077) << 0; - } else { - ab = 704751109; - t1 = blocks[0] - 210244248; - h = (t1 - 1521486534) << 0; - d = (t1 + 143694565) << 0; - } - this.#first = false; - } else { - s0 = ((a >>> 2) | (a << 30)) ^ - ((a >>> 13) | (a << 19)) ^ - ((a >>> 22) | (a << 10)); - s1 = ((e >>> 6) | (e << 26)) ^ - ((e >>> 11) | (e << 21)) ^ - ((e >>> 25) | (e << 7)); - ab = a & b; - maj = ab ^ (a & c) ^ bc; - ch = (e & f) ^ (~e & g); - t1 = h + s1 + ch + K[j] + blocks[j]; - t2 = s0 + maj; - h = (d + t1) << 0; - d = (t1 + t2) << 0; - } - s0 = ((d >>> 2) | (d << 30)) ^ - ((d >>> 13) | (d << 19)) ^ - ((d >>> 22) | (d << 10)); - s1 = ((h >>> 6) | (h << 26)) ^ - ((h >>> 11) | (h << 21)) ^ - ((h >>> 25) | (h << 7)); - da = d & a; - maj = da ^ (d & b) ^ ab; - ch = (h & e) ^ (~h & f); - t1 = g + s1 + ch + K[j + 1] + blocks[j + 1]; - t2 = s0 + maj; - g = (c + t1) << 0; - c = (t1 + t2) << 0; - s0 = ((c >>> 2) | (c << 30)) ^ - ((c >>> 13) | (c << 19)) ^ - ((c >>> 22) | (c << 10)); - s1 = ((g >>> 6) | (g << 26)) ^ - ((g >>> 11) | (g << 21)) ^ - ((g >>> 25) | (g << 7)); - cd = c & d; - maj = cd ^ (c & a) ^ da; - ch = (g & h) ^ (~g & e); - t1 = f + s1 + ch + K[j + 2] + blocks[j + 2]; - t2 = s0 + maj; - f = (b + t1) << 0; - b = (t1 + t2) << 0; - s0 = ((b >>> 2) | (b << 30)) ^ - ((b >>> 13) | (b << 19)) ^ - ((b >>> 22) | (b << 10)); - s1 = ((f >>> 6) | (f << 26)) ^ - ((f >>> 11) | (f << 21)) ^ - ((f >>> 25) | (f << 7)); - bc = b & c; - maj = bc ^ (b & d) ^ cd; - ch = (f & g) ^ (~f & h); - t1 = e + s1 + ch + K[j + 3] + blocks[j + 3]; - t2 = s0 + maj; - e = (a + t1) << 0; - a = (t1 + t2) << 0; - } - - this.#h0 = (this.#h0 + a) << 0; - this.#h1 = (this.#h1 + b) << 0; - this.#h2 = (this.#h2 + c) << 0; - this.#h3 = (this.#h3 + d) << 0; - this.#h4 = (this.#h4 + e) << 0; - this.#h5 = (this.#h5 + f) << 0; - this.#h6 = (this.#h6 + g) << 0; - this.#h7 = (this.#h7 + h) << 0; - } - - /** Return hash in hex string. */ - hex(): string { - this.finalize(); - - const h0 = this.#h0; - const h1 = this.#h1; - const h2 = this.#h2; - const h3 = this.#h3; - const h4 = this.#h4; - const h5 = this.#h5; - const h6 = this.#h6; - const h7 = this.#h7; - - let hex = HEX_CHARS[(h0 >> 28) & 0x0f] + - HEX_CHARS[(h0 >> 24) & 0x0f] + - HEX_CHARS[(h0 >> 20) & 0x0f] + - HEX_CHARS[(h0 >> 16) & 0x0f] + - HEX_CHARS[(h0 >> 12) & 0x0f] + - HEX_CHARS[(h0 >> 8) & 0x0f] + - HEX_CHARS[(h0 >> 4) & 0x0f] + - HEX_CHARS[h0 & 0x0f] + - HEX_CHARS[(h1 >> 28) & 0x0f] + - HEX_CHARS[(h1 >> 24) & 0x0f] + - HEX_CHARS[(h1 >> 20) & 0x0f] + - HEX_CHARS[(h1 >> 16) & 0x0f] + - HEX_CHARS[(h1 >> 12) & 0x0f] + - HEX_CHARS[(h1 >> 8) & 0x0f] + - HEX_CHARS[(h1 >> 4) & 0x0f] + - HEX_CHARS[h1 & 0x0f] + - HEX_CHARS[(h2 >> 28) & 0x0f] + - HEX_CHARS[(h2 >> 24) & 0x0f] + - HEX_CHARS[(h2 >> 20) & 0x0f] + - HEX_CHARS[(h2 >> 16) & 0x0f] + - HEX_CHARS[(h2 >> 12) & 0x0f] + - HEX_CHARS[(h2 >> 8) & 0x0f] + - HEX_CHARS[(h2 >> 4) & 0x0f] + - HEX_CHARS[h2 & 0x0f] + - HEX_CHARS[(h3 >> 28) & 0x0f] + - HEX_CHARS[(h3 >> 24) & 0x0f] + - HEX_CHARS[(h3 >> 20) & 0x0f] + - HEX_CHARS[(h3 >> 16) & 0x0f] + - HEX_CHARS[(h3 >> 12) & 0x0f] + - HEX_CHARS[(h3 >> 8) & 0x0f] + - HEX_CHARS[(h3 >> 4) & 0x0f] + - HEX_CHARS[h3 & 0x0f] + - HEX_CHARS[(h4 >> 28) & 0x0f] + - HEX_CHARS[(h4 >> 24) & 0x0f] + - HEX_CHARS[(h4 >> 20) & 0x0f] + - HEX_CHARS[(h4 >> 16) & 0x0f] + - HEX_CHARS[(h4 >> 12) & 0x0f] + - HEX_CHARS[(h4 >> 8) & 0x0f] + - HEX_CHARS[(h4 >> 4) & 0x0f] + - HEX_CHARS[h4 & 0x0f] + - HEX_CHARS[(h5 >> 28) & 0x0f] + - HEX_CHARS[(h5 >> 24) & 0x0f] + - HEX_CHARS[(h5 >> 20) & 0x0f] + - HEX_CHARS[(h5 >> 16) & 0x0f] + - HEX_CHARS[(h5 >> 12) & 0x0f] + - HEX_CHARS[(h5 >> 8) & 0x0f] + - HEX_CHARS[(h5 >> 4) & 0x0f] + - HEX_CHARS[h5 & 0x0f] + - HEX_CHARS[(h6 >> 28) & 0x0f] + - HEX_CHARS[(h6 >> 24) & 0x0f] + - HEX_CHARS[(h6 >> 20) & 0x0f] + - HEX_CHARS[(h6 >> 16) & 0x0f] + - HEX_CHARS[(h6 >> 12) & 0x0f] + - HEX_CHARS[(h6 >> 8) & 0x0f] + - HEX_CHARS[(h6 >> 4) & 0x0f] + - HEX_CHARS[h6 & 0x0f]; - if (!this.#is224) { - hex += HEX_CHARS[(h7 >> 28) & 0x0f] + - HEX_CHARS[(h7 >> 24) & 0x0f] + - HEX_CHARS[(h7 >> 20) & 0x0f] + - HEX_CHARS[(h7 >> 16) & 0x0f] + - HEX_CHARS[(h7 >> 12) & 0x0f] + - HEX_CHARS[(h7 >> 8) & 0x0f] + - HEX_CHARS[(h7 >> 4) & 0x0f] + - HEX_CHARS[h7 & 0x0f]; - } - return hex; - } - - /** Return hash in hex string. */ - toString(): string { - return this.hex(); - } - - /** Return hash in integer array. */ - digest(): number[] { - this.finalize(); - - const h0 = this.#h0; - const h1 = this.#h1; - const h2 = this.#h2; - const h3 = this.#h3; - const h4 = this.#h4; - const h5 = this.#h5; - const h6 = this.#h6; - const h7 = this.#h7; - - const arr = [ - (h0 >> 24) & 0xff, - (h0 >> 16) & 0xff, - (h0 >> 8) & 0xff, - h0 & 0xff, - (h1 >> 24) & 0xff, - (h1 >> 16) & 0xff, - (h1 >> 8) & 0xff, - h1 & 0xff, - (h2 >> 24) & 0xff, - (h2 >> 16) & 0xff, - (h2 >> 8) & 0xff, - h2 & 0xff, - (h3 >> 24) & 0xff, - (h3 >> 16) & 0xff, - (h3 >> 8) & 0xff, - h3 & 0xff, - (h4 >> 24) & 0xff, - (h4 >> 16) & 0xff, - (h4 >> 8) & 0xff, - h4 & 0xff, - (h5 >> 24) & 0xff, - (h5 >> 16) & 0xff, - (h5 >> 8) & 0xff, - h5 & 0xff, - (h6 >> 24) & 0xff, - (h6 >> 16) & 0xff, - (h6 >> 8) & 0xff, - h6 & 0xff, - ]; - if (!this.#is224) { - arr.push( - (h7 >> 24) & 0xff, - (h7 >> 16) & 0xff, - (h7 >> 8) & 0xff, - h7 & 0xff, - ); - } - return arr; - } - - /** Return hash in integer array. */ - array(): number[] { - return this.digest(); - } - - /** Return hash in ArrayBuffer. */ - arrayBuffer(): ArrayBuffer { - this.finalize(); - - const buffer = new ArrayBuffer(this.#is224 ? 28 : 32); - const dataView = new DataView(buffer); - dataView.setUint32(0, this.#h0); - dataView.setUint32(4, this.#h1); - dataView.setUint32(8, this.#h2); - dataView.setUint32(12, this.#h3); - dataView.setUint32(16, this.#h4); - dataView.setUint32(20, this.#h5); - dataView.setUint32(24, this.#h6); - if (!this.#is224) { - dataView.setUint32(28, this.#h7); - } - return buffer; - } -} - -export class HmacSha256 extends Sha256 { - #inner: boolean; - #is224: boolean; - #oKeyPad: number[]; - #sharedMemory: boolean; - - constructor(secretKey: Message, is224 = false, sharedMemory = false) { - super(is224, sharedMemory); - - let key: number[] | Uint8Array | undefined; - if (typeof secretKey === "string") { - const bytes: number[] = []; - const length = secretKey.length; - let index = 0; - for (let i = 0; i < length; ++i) { - let code = secretKey.charCodeAt(i); - if (code < 0x80) { - bytes[index++] = code; - } else if (code < 0x800) { - bytes[index++] = 0xc0 | (code >> 6); - bytes[index++] = 0x80 | (code & 0x3f); - } else if (code < 0xd800 || code >= 0xe000) { - bytes[index++] = 0xe0 | (code >> 12); - bytes[index++] = 0x80 | ((code >> 6) & 0x3f); - bytes[index++] = 0x80 | (code & 0x3f); - } else { - code = 0x10000 + - (((code & 0x3ff) << 10) | (secretKey.charCodeAt(++i) & 0x3ff)); - bytes[index++] = 0xf0 | (code >> 18); - bytes[index++] = 0x80 | ((code >> 12) & 0x3f); - bytes[index++] = 0x80 | ((code >> 6) & 0x3f); - bytes[index++] = 0x80 | (code & 0x3f); - } - } - key = bytes; - } else { - if (secretKey instanceof ArrayBuffer) { - key = new Uint8Array(secretKey); - } else { - key = secretKey; - } - } - - if (key.length > 64) { - key = new Sha256(is224, true).update(key).array(); - } - - const oKeyPad: number[] = []; - const iKeyPad: number[] = []; - for (let i = 0; i < 64; ++i) { - const b = key[i] || 0; - oKeyPad[i] = 0x5c ^ b; - iKeyPad[i] = 0x36 ^ b; - } - - this.update(iKeyPad); - this.#oKeyPad = oKeyPad; - this.#inner = true; - this.#is224 = is224; - this.#sharedMemory = sharedMemory; - } - - protected finalize(): void { - super.finalize(); - if (this.#inner) { - this.#inner = false; - const innerHash = this.array(); - super.init(this.#is224, this.#sharedMemory); - this.update(this.#oKeyPad); - this.update(innerHash); - super.finalize(); - } - } -} diff --git a/std/hash/sha256_test.ts b/std/hash/sha256_test.ts deleted file mode 100644 index 1ca9292237..0000000000 --- a/std/hash/sha256_test.ts +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { HmacSha256, Message, Sha256 } from "./sha256.ts"; -import { assertEquals } from "../testing/asserts.ts"; -import { dirname, fromFileUrl, join, resolve } from "../path/mod.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); -const testdataDir = resolve(moduleDir, "testdata"); - -/** Handy function to convert an array/array buffer to a string of hex values. */ -function toHexString(value: number[] | ArrayBuffer): string { - const array = new Uint8Array(value); - let hex = ""; - for (const v of array) { - const c = v.toString(16); - hex += c.length === 1 ? `0${c}` : c; - } - return hex; -} - -// deno-fmt-ignore -const fixtures: { - sha256: Record>; - sha224: Record>; - sha256Hmac: Record>; - sha224Hmac: Record>; -} = { - sha256: { - "ascii": { - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855": "", - "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592": "The quick brown fox jumps over the lazy dog", - "ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c": "The quick brown fox jumps over the lazy dog." - }, - "ascii more than 64 bytes": { - "54e73d89e1924fdcd056390266a983924b6d6d461e9470b6cd50bbaf69b5c54c": "The MD5 message-digest algorithm is a widely used cryptographic hash function producing a 128-bit (16-byte) hash value, typically expressed in text format as a 32 digit hexadecimal number. MD5 has been utilized in a wide variety of cryptographic applications, and is also commonly used to verify data integrity." - }, - "UTF8": { - "72726d8818f693066ceb69afa364218b692e62ea92b385782363780f47529c21": "中文", - "53196d1acfce0c4b264e01e8018c989d571351f59e33f055f76ff15b4f0516c6": "aécio", - "8d10a48685dbc34484696de7ea7434d80a54c1d60100530faccf697463ef19c9": "𠜎" - }, - "UTF8 more than 64 bytes": { - "d691014feebf35b3500ef6f6738d0094cac63628a7a018a980a40292a77703d1": "訊息摘要演算法第五版(英語:Message-Digest Algorithm 5,縮寫為MD5),是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一", - "81a1472ebdeb09406a783d607ff49ee2fde3e9f44ac1cd158ad8d6ad3c4e69fa": "訊息摘要演算法第五版(英語:Message-Digest Algorithm 5,縮寫為MD5),是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一(又譯雜湊演算法、摘要演算法等),主流程式語言普遍已有MD5的實作。" - }, - "special length": { - "5e6b963e2b6444dab8544beab8532850cef2a9d143872a6a5384abe37e61b3db": "0123456780123456780123456780123456780123456780123456780", - "85d240a4a03a0710423fc4f701da51e8785c9eaa96d718ab1c7991d6afd60d62": "01234567801234567801234567801234567801234567801234567801", - "c3ee464d5620eb2dde3dfda4c7955dbd9e9e2e9b113c13983fc67b0dfd892a53": "0123456780123456780123456780123456780123456780123456780123456780", - "74b51c6911f9a8b5e7c499effe7604e43b672166818873c27752c248de434841": "01234567801234567801234567801234567801234567801234567801234567801234567", - "6fba9e623ae6abf028a1b195748814aa95eebfb22e3ec5e15d2444cd6c48186a": "012345678012345678012345678012345678012345678012345678012345678012345678" - }, - "Array": { - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855": [], - "182889f925ae4e5cc37118ded6ed87f7bdc7cab5ec5e78faef2e50048999473f": [211, 212], - "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592": [84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103], - "74b51c6911f9a8b5e7c499effe7604e43b672166818873c27752c248de434841": [48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55] - } - }, - sha224: { - "ascii": { - "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f": "", - "730e109bd7a8a32b1cb9d9a09aa2325d2430587ddbc0c38bad911525": "The quick brown fox jumps over the lazy dog", - "619cba8e8e05826e9b8c519c0a5c68f4fb653e8a3d8aa04bb2c8cd4c": "The quick brown fox jumps over the lazy dog." - }, - "ascii more than 64 bytes": { - "4d97e15967391d2e846ea7d21bb480efadbae5868b731e7cc6267006": "The MD5 message-digest algorithm is a widely used cryptographic hash function producing a 128-bit (16-byte) hash value, typically expressed in text format as a 32 digit hexadecimal number. MD5 has been utilized in a wide variety of cryptographic applications, and is also commonly used to verify data integrity." - }, - "UTF8": { - "dfbab71afdf54388af4d55f8bd3de8c9b15e0eb916bf9125f4a959d4": "中文", - "d12841cafd89c534924a839e62bf35a2b5f3717b7802eb19bd8d8e15": "aécio", - "eaa0129b5509f5701db218fb7076b282e4409da52d06363aa3bdd63d": "𠜎" - }, - "UTF8 more than 64 bytes": { - "0dda421f3f81272418e1313673e9d74b7f2d04efc9c52c69458e12c3": "訊息摘要演算法第五版(英語:Message-Digest Algorithm 5,縮寫為MD5),是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一", - "a8cb74a54e6dc6ab6110db3915ba08ffe5e1abafaea78538fa12a626": "訊息摘要演算法第五版(英語:Message-Digest Algorithm 5,縮寫為MD5),是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一(又譯雜湊演算法、摘要演算法等),主流程式語言普遍已有MD5的實作。" - }, - "special length": { - "bc4a354d66f3cff4bc6dd6a88fbb0435cede7fd5fe94da0760cb1924": "0123456780123456780123456780123456780123456780123456780", - "2f148f757d1295784a7c69bf328b8bf827a536669e132234cd6f50e7": "01234567801234567801234567801234567801234567801234567801", - "496275a96bf41aa27ce89c3ae0fc63c3a3eab063887a8ea075bd091b": "0123456780123456780123456780123456780123456780123456780123456780", - "16ee1b101fe0e0d8dd156d598931ec19d75b0f8dc0a0455733c168c8": "01234567801234567801234567801234567801234567801234567801234567801234567", - "04c7a30079c640e440d884cdf0d7ab04fd05501d4498cb21be29ca1f": "012345678012345678012345678012345678012345678012345678012345678012345678" - }, - "Array": { - "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f": [], - "730e109bd7a8a32b1cb9d9a09aa2325d2430587ddbc0c38bad911525": [84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103], - "16ee1b101fe0e0d8dd156d598931ec19d75b0f8dc0a0455733c168c8": [48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55] - } - }, - sha256Hmac: { - "Test Vectors": { - "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7": [ - [0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b], - "Hi There" - ], - "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843": [ - "Jefe", - "what do ya want for nothing?" - ], - "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - [0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd] - ], - "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b": [ - [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19], - [0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd] - ], - "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - "Test Using Larger Than Block-Size Key - Hash Key First" - ], - "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm." - ] - }, - "UTF8": { - "865cc329d317f6d9fdbd183a3c5cc5fd4c370d11f98abbbb404bceb1e6392c7e": ["中文", "中文"], - "efeef87be5731506b69bb64a9898a456dd12c94834c36a4d8ba99e3db79ad7ed": ["aécio", "aécio"], - "8a6e527049b9cfc7e1c84bcf356a1289c95da68a586c03de3327f3de0d3737fe": ["𠜎", "𠜎"] - } - }, - sha224Hmac: { - "Test Vectors": { - "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22": [ - [0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b], - "Hi There" - ], - "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44": [ - "Jefe", - "what do ya want for nothing?" - ], - "7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - [0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd] - ], - "6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a": [ - [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19], - [0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd] - ], - "95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - "Test Using Larger Than Block-Size Key - Hash Key First" - ], - "3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm." - ] - }, - "UTF8": { - "e2280928fe813aeb7fa59aa14dd5e589041bfdf91945d19d25b9f3db": ["中文", "中文"], - "86c53dc054b16f6e006a254891bc9ff0da5df8e1a6faee3b0aaa732d": ["aécio", "aécio"], - "e9e5991bfb84506b105f800afac1599ff807bb8e20db8ffda48997b9": ["𠜎", "𠜎"] - } - }, -}; - -// deno-fmt-ignore -fixtures.sha256.Uint8Array = { - '182889f925ae4e5cc37118ded6ed87f7bdc7cab5ec5e78faef2e50048999473f': new Uint8Array([211, 212]), - 'd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592': new Uint8Array([84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103]) -}; -// deno-fmt-ignore -fixtures.sha256.Int8Array = { - 'd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592': new Int8Array([84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103]) -}; -// deno-fmt-ignore -fixtures.sha256.ArrayBuffer = { - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855': new ArrayBuffer(0), - '6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d': new ArrayBuffer(1) -}; -// deno-fmt-ignore -fixtures.sha224.Uint8Array = { - 'e17541396a3ecd1cd5a2b968b84e597e8eae3b0ea3127963bf48dd3b': new Uint8Array([211, 212]), - '730e109bd7a8a32b1cb9d9a09aa2325d2430587ddbc0c38bad911525': new Uint8Array([84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103]) -}; -// deno-fmt-ignore -fixtures.sha224.Int8Array = { - '730e109bd7a8a32b1cb9d9a09aa2325d2430587ddbc0c38bad911525': new Int8Array([84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103]) -}; -// deno-fmt-ignore -fixtures.sha224.ArrayBuffer = { - 'd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f': new ArrayBuffer(0), - 'fff9292b4201617bdc4d3053fce02734166a683d7d858a7f5f59b073': new ArrayBuffer(1), -}; -fixtures.sha256Hmac.Uint8Array = { - e48411262715c8370cd5e7bf8e82bef53bd53712d007f3429351843b77c7bb9b: [ - new Uint8Array(0), - "Hi There", - ], -}; -fixtures.sha256Hmac.ArrayBuffer = { - e48411262715c8370cd5e7bf8e82bef53bd53712d007f3429351843b77c7bb9b: [ - new ArrayBuffer(0), - "Hi There", - ], -}; -fixtures.sha224Hmac.Uint8Array = { - da8f94de91d62154b55ea4e8d6eb133f6d553bcd1f1ba205b9488945: [ - new Uint8Array(0), - "Hi There", - ], -}; -fixtures.sha224Hmac.ArrayBuffer = { - da8f94de91d62154b55ea4e8d6eb133f6d553bcd1f1ba205b9488945: [ - new ArrayBuffer(0), - "Hi There", - ], -}; - -const methods = ["array", "arrayBuffer", "digest", "hex"] as const; - -for (const method of methods) { - for (const [name, tests] of Object.entries(fixtures.sha256)) { - let i = 1; - for (const [expected, message] of Object.entries(tests)) { - Deno.test({ - name: `sha256.${method}() - ${name} - #${i++}`, - fn() { - const algorithm = new Sha256(); - algorithm.update(message); - const actual = method === "hex" - ? algorithm[method]() - : toHexString(algorithm[method]()); - assertEquals(actual, expected); - }, - }); - } - } -} - -for (const method of methods) { - for (const [name, tests] of Object.entries(fixtures.sha224)) { - let i = 1; - for (const [expected, message] of Object.entries(tests)) { - Deno.test({ - name: `sha224.${method}() - ${name} - #${i++}`, - fn() { - const algorithm = new Sha256(true); - algorithm.update(message); - const actual = method === "hex" - ? algorithm[method]() - : toHexString(algorithm[method]()); - assertEquals(actual, expected); - }, - }); - } - } -} - -for (const method of methods) { - for (const [name, tests] of Object.entries(fixtures.sha256Hmac)) { - let i = 1; - for (const [expected, [key, message]] of Object.entries(tests)) { - Deno.test({ - name: `hmacSha256.${method}() - ${name} - #${i++}`, - fn() { - const algorithm = new HmacSha256(key); - algorithm.update(message); - const actual = method === "hex" - ? algorithm[method]() - : toHexString(algorithm[method]()); - assertEquals(actual, expected); - }, - }); - } - } -} - -for (const method of methods) { - for (const [name, tests] of Object.entries(fixtures.sha224Hmac)) { - let i = 1; - for (const [expected, [key, message]] of Object.entries(tests)) { - Deno.test({ - name: `hmacSha224.${method}() - ${name} - #${i++}`, - fn() { - const algorithm = new HmacSha256(key, true); - algorithm.update(message); - const actual = method === "hex" - ? algorithm[method]() - : toHexString(algorithm[method]()); - assertEquals(actual, expected); - }, - }); - } - } -} - -Deno.test("[hash/sha256] test Uint8Array from Reader", async () => { - const data = await Deno.readFile(join(testdataDir, "hashtest")); - - const hash = new Sha256().update(data).hex(); - assertEquals( - hash, - "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", - ); -}); diff --git a/std/hash/sha3.ts b/std/hash/sha3.ts deleted file mode 100644 index 2bfbd6d7ba..0000000000 --- a/std/hash/sha3.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -export { Sha3_224, Sha3_256, Sha3_384, Sha3_512 } from "./_sha3/sha3.ts"; -export { Keccak224, Keccak256, Keccak384, Keccak512 } from "./_sha3/keccak.ts"; -export { Shake128, Shake256 } from "./_sha3/shake.ts"; diff --git a/std/hash/sha3_test.ts b/std/hash/sha3_test.ts deleted file mode 100644 index 5ceea075df..0000000000 --- a/std/hash/sha3_test.ts +++ /dev/null @@ -1,585 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { assertEquals, assertThrows } from "../testing/asserts.ts"; -import { - Keccak224, - Keccak256, - Keccak384, - Keccak512, - // deno-lint-ignore camelcase - Sha3_224, - // deno-lint-ignore camelcase - Sha3_256, - // deno-lint-ignore camelcase - Sha3_384, - // deno-lint-ignore camelcase - Sha3_512, - Shake128, - Shake256, -} from "./sha3.ts"; -import * as hex from "../encoding/hex.ts"; - -const millionAs = "a".repeat(1000000); - -// deno-lint-ignore camelcase -const testSetSha3_224 = [ - ["", "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7"], - ["abc", "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf"], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "8a24108b154ada21c9fd5574494479ba5c7e7ab76ef264ead0fcce33", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "f9019111996dcf160e284e320fd6d8825cabcd41a5ffdc4c5e9d64b6", - ], - [millionAs, "d69335b93325192e516a912e6d19a15cb51c6ed5c15243e7a7fd653c"], -]; - -// deno-lint-ignore camelcase -const testSetSha3_256 = [ - ["", "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"], - ["abc", "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "3fc5559f14db8e453a0a3091edbd2bc25e11528d81c66fa570a4efdcc2695ee1", - ], - [ - millionAs, - "5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1", - ], -]; - -// deno-lint-ignore camelcase -const testSetSha3_384 = [ - [ - "", - "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004", - ], - [ - "abc", - "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "3a4f3b6284e571238884e95655e8c8a60e068e4059a9734abc08823a900d161592860243f00619ae699a29092ed91a16", - ], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "991c665755eb3a4b6bbdfb75c78a492e8c56a22c5c4d7e429bfdbc32b9d4ad5aa04a1f076e62fea19eef51acd0657c22", - ], - [ - millionAs, - "eee9e24d78c1855337983451df97c8ad9eedf256c6334f8e948d252d5e0e76847aa0774ddb90a842190d2c558b4b8340", - ], -]; - -// deno-lint-ignore camelcase -const testSetSha3_512 = [ - [ - "", - "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26", - ], - [ - "abc", - "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0", - ], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "a8ae722a78e10cbbc413886c02eb5b369a03f6560084aff566bd597bb7ad8c1ccd86e81296852359bf2faddb5153c0a7445722987875e74287adac21adebe952", - ], - [ - millionAs, - "3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87", - ], -]; - -const testSetKeccak224 = [ - ["", "f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd"], - ["abc", "c30411768506ebe1c2871b1ee2e87d38df342317300a9b97a95ec6a8"], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "e51faa2b4655150b931ee8d700dc202f763ca5f962c529eae55012b6", - ], - [millionAs, "19f9167be2a04c43abd0ed554788101b9c339031acc8e1468531303f"], -]; - -const testSetKeccak256 = [ - ["", "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"], - ["abc", "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45"], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "45d3b367a6904e6e8d502ee04999a7c27647f91fa845d456525fd352ae3d7371", - ], - [ - millionAs, - "fadae6b49f129bbb812be8407b7b2894f34aecf6dbd1f9b0f0c7e9853098fc96", - ], -]; - -const testSetKeccak384 = [ - [ - "", - "2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff", - ], - [ - "abc", - "f7df1165f033337be098e7d288ad6a2f74409d7a60b49c36642218de161b1f99f8c681e4afaf31a34db29fb763e3c28e", - ], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "b41e8896428f1bcbb51e17abd6acc98052a3502e0d5bf7fa1af949b4d3c855e7c4dc2c390326b3f3e74c7b1e2b9a3657", - ], - [ - millionAs, - "0c8324e1ebc182822c5e2a086cac07c2fe00e3bce61d01ba8ad6b71780e2dec5fb89e5ae90cb593e57bc6258fdd94e17", - ], -]; - -const testSetKeccak512 = [ - [ - "", - "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", - ], - [ - "abc", - "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", - ], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "6aa6d3669597df6d5a007b00d09c20795b5c4218234e1698a944757a488ecdc09965435d97ca32c3cfed7201ff30e070cd947f1fc12b9d9214c467d342bcba5d", - ], - [ - millionAs, - "5cf53f2e556be5a624425ede23d0e8b2c7814b4ba0e4e09cbbf3c2fac7056f61e048fc341262875ebc58a5183fea651447124370c1ebf4d6c89bc9a7731063bb", - ], -]; - -const testSetShake128 = [ - ["", "7f9c2ba4e88f827d616045507605853e"], - ["abc", "5881092dd818bf5cf8a3ddb793fbcba7"], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "1a96182b50fb8c7e74e0a707788f55e9", - ], - [millionAs, "9d222c79c4ff9d092cf6ca86143aa411"], -]; - -// deno-lint-ignore camelcase -const testSetShake128_224 = [ - ["", "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eac"], - ["abc", "5881092dd818bf5cf8a3ddb793fbcba74097d5c526a6d35f97b83351"], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "1a96182b50fb8c7e74e0a707788f55e98209b8d91fade8f32f8dd5cf", - ], - [millionAs, "9d222c79c4ff9d092cf6ca86143aa411e369973808ef97093255826c"], -]; - -// deno-lint-ignore camelcase -const testSetShake128_2048 = [ - [ - "", - "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef263cb1eea988004b93103cfb0aeefd2a686e01fa4a58e8a3639ca8a1e3f9ae57e235b8cc873c23dc62b8d260169afa2f75ab916a58d974918835d25e6a435085b2badfd6dfaac359a5efbb7bcc4b59d538df9a04302e10c8bc1cbf1a0b3a5120ea17cda7cfad765f5623474d368ccca8af0007cd9f5e4c849f167a580b14aabdefaee7eef47cb0fca9767be1fda69419dfb927e9df07348b196691abaeb580b32def58538b8d23f87732ea63b02b4fa0f4873360e2841928cd60dd4cee8cc0d4c922a96188d032675c8ac850933c7aff1533b94c834adbb69c6115bad4692d8619", - ], - [ - "abc", - "5881092dd818bf5cf8a3ddb793fbcba74097d5c526a6d35f97b83351940f2cc844c50af32acd3f2cdd066568706f509bc1bdde58295dae3f891a9a0fca5783789a41f8611214ce612394df286a62d1a2252aa94db9c538956c717dc2bed4f232a0294c857c730aa16067ac1062f1201fb0d377cfb9cde4c63599b27f3462bba4a0ed296c801f9ff7f57302bb3076ee145f97a32ae68e76ab66c48d51675bd49acc29082f5647584e6aa01b3f5af057805f973ff8ecb8b226ac32ada6f01c1fcd4818cb006aa5b4cdb3611eb1e533c8964cacfdf31012cd3fb744d02225b988b475375faad996eb1b9176ecb0f8b2871723d6dbb804e23357e50732f5cfc904b1", - ], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "1a96182b50fb8c7e74e0a707788f55e98209b8d91fade8f32f8dd5cff7bf21f54ee5f19550825a6e070030519e944263ac1c6765287065621f9fcb3201723e3223b63a46c2938aa953ba8401d0ea77b8d26490775566407b95673c0f4cc1ce9fd966148d7efdff26bbf9f48a21c6da35bfaa545654f70ae586ff10131420771483ec92edab408c767bf4c5b4fffaa80c8ca214d84c4dc700d0c50630b2ffc3793ea4d87258b4c9548c5485a5ca666ef73fbd816d418aea6395b503addd9b150f9e0663325f01e5518b71ffa1244ea284cebe0cea2f774d7b3a437dca3282e324777e19624bf2be3cd355c1bfbddb323a33f11efafb2448293501dc0454c6b72f", - ], - [ - millionAs, - "9d222c79c4ff9d092cf6ca86143aa411e369973808ef97093255826c5572ef58424c4b5c28475ffdcf981663867fec6321c1262e387bccf8ca676884c4a9d0c13bfa6869763d5ae4bbc9b3ccd09d1ca5ea7446538d69b3fb98c72b59a2b4817db5eadd9011f90fa71091931f8134f4f00b562e2fe105937270361c1909862ad45046e3932f5dd311ec72fec5f8fb8f60b45a3bee3f85bbf7fcedc6a555677648e0654b381941a86bd3e512657b0d57a7991fc4543f89d8290492222ce4a33e17602b3b99c009f7655f87535cdaa3716f58c47b8a157ad195f02809f27500b9254979311c6bb415968cd10431169a27d5a8d61e13a6b8b77af1f8b6dd2eefdea0", - ], -]; - -const testSetShake256 = [ - ["", "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f"], - ["abc", "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739"], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "4d8c2dd2435a0128eefbb8c36f6f87133a7911e18d979ee1ae6be5d4fd2e3329", - ], - [ - millionAs, - "3578a7a4ca9137569cdf76ed617d31bb994fca9c1bbf8b184013de8234dfd13a", - ], -]; - -// deno-lint-ignore camelcase -const testSetShake256_128 = [ - ["", "46b9dd2b0ba88d13233b3feb743eeb24"], - ["abc", "483366601360a8771c6863080cc4114d"], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "4d8c2dd2435a0128eefbb8c36f6f8713", - ], - [millionAs, "3578a7a4ca9137569cdf76ed617d31bb"], -]; - -// deno-lint-ignore camelcase -const testSetShake256_384 = [ - [ - "", - "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fd75dc4ddd8c0f200cb05019d67b592f6", - ], - [ - "abc", - "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739d5a15bef186a5386c75744c0527e1faa", - ], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "4d8c2dd2435a0128eefbb8c36f6f87133a7911e18d979ee1ae6be5d4fd2e332940d8688a4e6a59aa8060f1f9bc996c05", - ], - [ - millionAs, - "3578a7a4ca9137569cdf76ed617d31bb994fca9c1bbf8b184013de8234dfd13a3fd124d4df76c0a539ee7dd2f6e1ec34", - ], -]; - -// deno-lint-ignore camelcase -const testSetShake256_512 = [ - [ - "", - "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fd75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be", - ], - [ - "abc", - "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739d5a15bef186a5386c75744c0527e1faa9f8726e462a12a4feb06bd8801e751e4", - ], - [ - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "4d8c2dd2435a0128eefbb8c36f6f87133a7911e18d979ee1ae6be5d4fd2e332940d8688a4e6a59aa8060f1f9bc996c05aca3c696a8b66279dc672c740bb224ec", - ], - [ - millionAs, - "3578a7a4ca9137569cdf76ed617d31bb994fca9c1bbf8b184013de8234dfd13a3fd124d4df76c0a539ee7dd2f6e1ec346124c815d9410e145eb561bcd97b18ab", - ], -]; - -function s2b(data: string): Uint8Array { - return new TextEncoder().encode(data); -} - -Deno.test("[hash/sha3] testSha3-224Raw", () => { - const sha3sum = (data: ArrayBuffer): ArrayBuffer => { - const sha3 = new Sha3_224(); - return sha3.update(data).digest(); - }; - - for (const [input, output] of testSetSha3_224) { - const rawOutput = hex.decodeString(output); - assertEquals(sha3sum(s2b(input)), rawOutput); - } -}); - -Deno.test("[hash/sha3] testSha3-224String", () => { - const sha3sum = (data: string): string => { - const sha3 = new Sha3_224(); - return sha3.update(data).toString(); - }; - - for (const [input, output] of testSetSha3_224) { - assertEquals(sha3sum(input), output); - } -}); - -Deno.test("[hash/sha3] testSha3-256Raw", () => { - const sha3sum = (data: ArrayBuffer): ArrayBuffer => { - const sha3 = new Sha3_256(); - return sha3.update(data).digest(); - }; - - for (const [input, output] of testSetSha3_256) { - const rawOutput = hex.decodeString(output); - assertEquals(sha3sum(s2b(input)), rawOutput); - } -}); - -Deno.test("[hash/sha3] testSha3-256String", () => { - const sha3sum = (data: string): string => { - const sha3 = new Sha3_256(); - return sha3.update(data).toString(); - }; - - for (const [input, output] of testSetSha3_256) { - assertEquals(sha3sum(input), output); - } -}); - -Deno.test("[hash/sha3] testSha3-384Raw", () => { - const sha3sum = (data: ArrayBuffer): ArrayBuffer => { - const sha3 = new Sha3_384(); - return sha3.update(data).digest(); - }; - - for (const [input, output] of testSetSha3_384) { - const rawOutput = hex.decodeString(output); - assertEquals(sha3sum(s2b(input)), rawOutput); - } -}); - -Deno.test("[hash/sha3] testSha3-384String", () => { - const sha3sum = (data: string): string => { - const sha3 = new Sha3_384(); - return sha3.update(data).toString(); - }; - - for (const [input, output] of testSetSha3_384) { - assertEquals(sha3sum(input), output); - } -}); - -Deno.test("[hash/sha3] testSha3-512Raw", () => { - const sha3sum = (data: ArrayBuffer): ArrayBuffer => { - const sha3 = new Sha3_512(); - return sha3.update(data).digest(); - }; - - for (const [input, output] of testSetSha3_512) { - const rawOutput = hex.decodeString(output); - assertEquals(sha3sum(s2b(input)), rawOutput); - } -}); - -Deno.test("[hash/sha3] testSha3-512String", () => { - const sha3sum = (data: string): string => { - const sha3 = new Sha3_512(); - return sha3.update(data).toString(); - }; - - for (const [input, output] of testSetSha3_512) { - assertEquals(sha3sum(input), output); - } -}); - -Deno.test("[hash/sha3] testKeccak-224Raw", () => { - const keccakSum = (data: ArrayBuffer): ArrayBuffer => { - const keccak = new Keccak224(); - return keccak.update(data).digest(); - }; - - for (const [input, output] of testSetKeccak224) { - const rawOutput = hex.decodeString(output); - assertEquals(keccakSum(s2b(input)), rawOutput); - } -}); - -Deno.test("[hash/sha3] testKeccak-224String", () => { - const keccakSum = (data: string): string => { - const keccak = new Keccak224(); - return keccak.update(data).toString(); - }; - - for (const [input, output] of testSetKeccak224) { - assertEquals(keccakSum(input), output); - } -}); - -Deno.test("[hash/sha3] testKeccak-256Raw", () => { - const keccakSum = (data: ArrayBuffer): ArrayBuffer => { - const keccak = new Keccak256(); - return keccak.update(data).digest(); - }; - - for (const [input, output] of testSetKeccak256) { - const rawOutput = hex.decodeString(output); - assertEquals(keccakSum(s2b(input)), rawOutput); - } -}); - -Deno.test("[hash/sha3] testKeccak-256String", () => { - const keccakSum = (data: string): string => { - const keccak = new Keccak256(); - return keccak.update(data).toString(); - }; - - for (const [input, output] of testSetKeccak256) { - assertEquals(keccakSum(input), output); - } -}); - -Deno.test("[hash/sha3] testKeccak-384Raw", () => { - const keccakSum = (data: ArrayBuffer): ArrayBuffer => { - const keccak = new Keccak384(); - return keccak.update(data).digest(); - }; - - for (const [input, output] of testSetKeccak384) { - const rawOutput = hex.decodeString(output); - assertEquals(keccakSum(s2b(input)), rawOutput); - } -}); - -Deno.test("[hash/sha3] testKeccak-384String", () => { - const keccakSum = (data: string): string => { - const keccak = new Keccak384(); - return keccak.update(data).toString(); - }; - - for (const [input, output] of testSetKeccak384) { - assertEquals(keccakSum(input), output); - } -}); - -Deno.test("[hash/sha3] testKeccak-512Raw", () => { - const keccakSum = (data: ArrayBuffer): ArrayBuffer => { - const keccak = new Keccak512(); - return keccak.update(data).digest(); - }; - - for (const [input, output] of testSetKeccak512) { - const rawOutput = hex.decodeString(output); - assertEquals(keccakSum(s2b(input)), rawOutput); - } -}); - -Deno.test("[hash/sha3] testKeccak-512String", () => { - const keccakSum = (data: string): string => { - const keccak = new Keccak512(); - return keccak.update(data).toString(); - }; - - for (const [input, output] of testSetKeccak512) { - assertEquals(keccakSum(input), output); - } -}); - -Deno.test("[hash/sha3] testSHAKE-128Raw", () => { - const shakeSum = (data: ArrayBuffer): ArrayBuffer => { - const shake = new Shake128(128); - return shake.update(data).digest(); - }; - - for (const [input, output] of testSetShake128) { - const rawOutput = hex.decodeString(output); - assertEquals(shakeSum(s2b(input)), rawOutput); - } -}); - -Deno.test("[hash/sha3] testSHAKE-128String", () => { - const shakeSum = (data: string): string => { - const shake = new Shake128(128); - return shake.update(data).toString(); - }; - - for (const [input, output] of testSetShake128) { - assertEquals(shakeSum(input), output); - } -}); - -Deno.test("[hash/sha3] testSHAKE-128-224Raw", () => { - const shakeSum = (data: ArrayBuffer): ArrayBuffer => { - const shake = new Shake128(224); - return shake.update(data).digest(); - }; - - for (const [input, output] of testSetShake128_224) { - const rawOutput = hex.decodeString(output); - assertEquals(shakeSum(s2b(input)), rawOutput); - } -}); - -Deno.test("[hash/sha3] testSHAKE-128-224String", () => { - const shakeSum = (data: string): string => { - const shake = new Shake128(224); - return shake.update(data).toString(); - }; - - for (const [input, output] of testSetShake128_224) { - assertEquals(shakeSum(input), output); - } -}); - -Deno.test("[hash/sha3] testSHAKE-128-2048", () => { - const shakeSum = (data: string): string => { - const shake = new Shake128(2048); - return shake.update(data).toString(); - }; - - for (const [input, output] of testSetShake128_2048) { - assertEquals(shakeSum(input), output); - } -}); - -Deno.test("[hash/sha3] testSHAKE-256", () => { - const shakeSum = (data: string): string => { - const shake = new Shake256(256); - return shake.update(data).toString(); - }; - - for (const [input, output] of testSetShake256) { - assertEquals(shakeSum(input), output); - } -}); - -Deno.test("[hash/sha3] testSHAKE-256-128", () => { - const shakeSum = (data: string): string => { - const shake = new Shake256(128); - return shake.update(data).toString(); - }; - - for (const [input, output] of testSetShake256_128) { - assertEquals(shakeSum(input), output); - } -}); - -Deno.test("[hash/sha3] testSHAKE-256-384", () => { - const shakeSum = (data: string): string => { - const shake = new Shake256(384); - return shake.update(data).toString(); - }; - - for (const [input, output] of testSetShake256_384) { - assertEquals(shakeSum(input), output); - } -}); - -Deno.test("[hash/sha3] testSHAKE-256-512", () => { - const shakeSum = (data: string): string => { - const shake = new Shake256(512); - return shake.update(data).toString(); - }; - - for (const [input, output] of testSetShake256_512) { - assertEquals(shakeSum(input), output); - } -}); - -Deno.test("[hash/sha3] testSha3-256Chain", () => { - const sha3 = new Sha3_256(); - const output = sha3 - .update(s2b("a")) - .update(s2b("b")) - .update(s2b("c")) - .toString(); - - assertEquals( - output, - "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", - ); -}); - -Deno.test("[hash/sha3] testSha3UpdateFinalized", () => { - assertThrows( - () => { - const sha3 = new Sha3_256(); - const hash = sha3.update(s2b("a")).digest(); - const hash2 = sha3.update(s2b("a")).digest(); - assertEquals(hash, hash2); - }, - Error, - "sha3: cannot update already finalized hash", - ); -}); diff --git a/std/hash/sha512.ts b/std/hash/sha512.ts deleted file mode 100644 index 98b0fec314..0000000000 --- a/std/hash/sha512.ts +++ /dev/null @@ -1,791 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -/* - * [js-sha512]{@link https://github.com/emn178/js-sha512} - * - * @version 0.8.0 - * @author Chen, Yi-Cyuan [emn178@gmail.com] - * @copyright Chen, Yi-Cyuan 2014-2018 - * @license MIT - */ - -export type Message = string | number[] | ArrayBuffer; - -// deno-fmt-ignore -const HEX_CHARS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"] as const; -const EXTRA = [-2147483648, 8388608, 32768, 128] as const; -const SHIFT = [24, 16, 8, 0] as const; -// deno-fmt-ignore -const K = [ - 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, 0x3956c25b, - 0xf348b538, 0x59f111f1, 0xb605d019, 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, 0xd807aa98, 0xa3030242, - 0x12835b01, 0x45706fbe, 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, 0x72be5d74, 0xf27b896f, 0x80deb1fe, - 0x3b1696b1, 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, - 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, 0x5cb0a9dc, - 0xbd41fbd4, 0x76f988da, 0x831153b5, 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, 0xb00327c8, 0x98fb213f, - 0xbf597fc7, 0xbeef0ee4, 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, 0x06ca6351, 0xe003826f, 0x14292967, - 0x0a0e6e70, 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, - 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, 0xa2bfe8a1, - 0x4cf10364, 0xa81a664b, 0xbc423001, 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, 0xd192e819, 0xd6ef5218, - 0xd6990624, 0x5565a910, 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, - 0x5141ab53, 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, - 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, 0x84c87814, - 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, 0xbef9a3f7, 0xb2c67915, - 0xc67178f2, 0xe372532b, 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, - 0xee6ed178, 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, - 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, 0x4cc5d4be, - 0xcb3e42b6, 0x597f299c, 0xfc657e2a, 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817 -] as const; - -const blocks: number[] = []; - -// deno-fmt-ignore -export class Sha512 { - #blocks!: number[]; - #block!: number; - #bits!: number; - #start!: number; - #bytes!: number; - #hBytes!: number; - #lastByteIndex = 0; - #finalized!: boolean; - #hashed!: boolean; - #h0h!: number; - #h0l!: number; - #h1h!: number; - #h1l!: number; - #h2h!: number; - #h2l!: number; - #h3h!: number; - #h3l!: number; - #h4h!: number; - #h4l!: number; - #h5h!: number; - #h5l!: number; - #h6h!: number; - #h6l!: number; - #h7h!: number; - #h7l!: number; - - constructor(bits = 512, sharedMemory = false) { - this.init(bits, sharedMemory); - } - - protected init(bits: number, sharedMemory: boolean): void { - if (sharedMemory) { - blocks[0] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = - blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = blocks[16] = - blocks[17] = blocks[18] = blocks[19] = blocks[20] = blocks[21] = blocks[22] = blocks[23] = blocks[24] = - blocks[25] = blocks[26] = blocks[27] = blocks[28] = blocks[29] = blocks[30] = blocks[31] = blocks[32] = 0; - this.#blocks = blocks; - } else { - this.#blocks = - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - } - if (bits === 224) { - this.#h0h = 0x8c3d37c8; - this.#h0l = 0x19544da2; - this.#h1h = 0x73e19966; - this.#h1l = 0x89dcd4d6; - this.#h2h = 0x1dfab7ae; - this.#h2l = 0x32ff9c82; - this.#h3h = 0x679dd514; - this.#h3l = 0x582f9fcf; - this.#h4h = 0x0f6d2b69; - this.#h4l = 0x7bd44da8; - this.#h5h = 0x77e36f73; - this.#h5l = 0x04c48942; - this.#h6h = 0x3f9d85a8; - this.#h6l = 0x6a1d36c8; - this.#h7h = 0x1112e6ad; - this.#h7l = 0x91d692a1; - } else if (bits === 256) { - this.#h0h = 0x22312194; - this.#h0l = 0xfc2bf72c; - this.#h1h = 0x9f555fa3; - this.#h1l = 0xc84c64c2; - this.#h2h = 0x2393b86b; - this.#h2l = 0x6f53b151; - this.#h3h = 0x96387719; - this.#h3l = 0x5940eabd; - this.#h4h = 0x96283ee2; - this.#h4l = 0xa88effe3; - this.#h5h = 0xbe5e1e25; - this.#h5l = 0x53863992; - this.#h6h = 0x2b0199fc; - this.#h6l = 0x2c85b8aa; - this.#h7h = 0x0eb72ddc; - this.#h7l = 0x81c52ca2; - } else if (bits === 384) { - this.#h0h = 0xcbbb9d5d; - this.#h0l = 0xc1059ed8; - this.#h1h = 0x629a292a; - this.#h1l = 0x367cd507; - this.#h2h = 0x9159015a; - this.#h2l = 0x3070dd17; - this.#h3h = 0x152fecd8; - this.#h3l = 0xf70e5939; - this.#h4h = 0x67332667; - this.#h4l = 0xffc00b31; - this.#h5h = 0x8eb44a87; - this.#h5l = 0x68581511; - this.#h6h = 0xdb0c2e0d; - this.#h6l = 0x64f98fa7; - this.#h7h = 0x47b5481d; - this.#h7l = 0xbefa4fa4; - } else { // 512 - this.#h0h = 0x6a09e667; - this.#h0l = 0xf3bcc908; - this.#h1h = 0xbb67ae85; - this.#h1l = 0x84caa73b; - this.#h2h = 0x3c6ef372; - this.#h2l = 0xfe94f82b; - this.#h3h = 0xa54ff53a; - this.#h3l = 0x5f1d36f1; - this.#h4h = 0x510e527f; - this.#h4l = 0xade682d1; - this.#h5h = 0x9b05688c; - this.#h5l = 0x2b3e6c1f; - this.#h6h = 0x1f83d9ab; - this.#h6l = 0xfb41bd6b; - this.#h7h = 0x5be0cd19; - this.#h7l = 0x137e2179; - } - this.#bits = bits; - this.#block = this.#start = this.#bytes = this.#hBytes = 0; - this.#finalized = this.#hashed = false; - } - - update(message: Message): this { - if (this.#finalized) { - return this; - } - let msg: string | number[] | Uint8Array; - if (message instanceof ArrayBuffer) { - msg = new Uint8Array(message); - } else { - msg = message; - } - const length = msg.length; - const blocks = this.#blocks; - let index = 0; - while (index < length) { - let i: number; - if (this.#hashed) { - this.#hashed = false; - blocks[0] = this.#block; - blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = - blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = blocks[16] = - blocks[17] = blocks[18] = blocks[19] = blocks[20] = blocks[21] = blocks[22] = blocks[23] = blocks[24] = - blocks[25] = blocks[26] = blocks[27] = blocks[28] = blocks[29] = blocks[30] = blocks[31] = blocks[32] = 0; - } - if (typeof msg !== "string") { - for (i = this.#start; index < length && i < 128; ++index) { - blocks[i >> 2] |= msg[index] << SHIFT[i++ & 3]; - } - } else { - for (i = this.#start; index < length && i < 128; ++index) { - let code = msg.charCodeAt(index); - if (code < 0x80) { - blocks[i >> 2] |= code << SHIFT[i++ & 3]; - } else if (code < 0x800) { - blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } else if (code < 0xd800 || code >= 0xe000) { - blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } else { - code = 0x10000 + (((code & 0x3ff) << 10) | (msg.charCodeAt(++index) & 0x3ff)); - blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } - } - } - this.#lastByteIndex = i; - this.#bytes += i - this.#start; - if (i >= 128) { - this.#block = blocks[32]; - this.#start = i - 128; - this.hash(); - this.#hashed = true; - } else { - this.#start = i; - } - } - if (this.#bytes > 4294967295) { - this.#hBytes += (this.#bytes / 4294967296) << 0; - this.#bytes = this.#bytes % 4294967296; - } - return this; - } - - protected finalize(): void { - if (this.#finalized) { - return; - } - this.#finalized = true; - const blocks = this.#blocks; - const i = this.#lastByteIndex; - blocks[32] = this.#block; - blocks[i >> 2] |= EXTRA[i & 3]; - this.#block = blocks[32]; - if (i >= 112) { - if (!this.#hashed) { - this.hash(); - } - blocks[0] = this.#block; - blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = - blocks[9] =blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = blocks[16] = - blocks[17] = blocks[18] = blocks[19] = blocks[20] = blocks[21] = blocks[22] = blocks[23] = blocks[24] = - blocks[25] = blocks[26] = blocks[27] = blocks[28] = blocks[29] = blocks[30] = blocks[31] = blocks[32] = 0; - } - blocks[30] = (this.#hBytes << 3) | (this.#bytes >>> 29); - blocks[31] = this.#bytes << 3; - this.hash(); - } - - protected hash(): void { - const - h0h = this.#h0h, h0l = this.#h0l, h1h = this.#h1h, h1l = this.#h1l, h2h = this.#h2h, h2l = this.#h2l, - h3h = this.#h3h, h3l = this.#h3l, h4h = this.#h4h, h4l = this.#h4l, h5h = this.#h5h, h5l = this.#h5l, - h6h = this.#h6h, h6l = this.#h6l, h7h = this.#h7h, h7l = this.#h7l; - - let s0h, s0l, s1h, s1l, c1, c2, c3, c4, abh, abl, dah, dal, cdh, cdl, bch, bcl, majh, majl, - t1h, t1l, t2h, t2l, chh, chl: number; - - const blocks = this.#blocks; - - for (let j = 32; j < 160; j += 2) { - t1h = blocks[j - 30]; - t1l = blocks[j - 29]; - s0h = ((t1h >>> 1) | (t1l << 31)) ^ ((t1h >>> 8) | (t1l << 24)) ^ (t1h >>> 7); - s0l = ((t1l >>> 1) | (t1h << 31)) ^ ((t1l >>> 8) | (t1h << 24)) ^ ((t1l >>> 7) | (t1h << 25)); - - t1h = blocks[j - 4]; - t1l = blocks[j - 3]; - s1h = ((t1h >>> 19) | (t1l << 13)) ^ ((t1l >>> 29) | (t1h << 3)) ^ (t1h >>> 6); - s1l = ((t1l >>> 19) | (t1h << 13)) ^ ((t1h >>> 29) | (t1l << 3)) ^ ((t1l >>> 6) | (t1h << 26)); - - t1h = blocks[j - 32]; - t1l = blocks[j - 31]; - t2h = blocks[j - 14]; - t2l = blocks[j - 13]; - - c1 = (t2l & 0xffff) + (t1l & 0xffff) + (s0l & 0xffff) + (s1l & 0xffff); - c2 = (t2l >>> 16) + (t1l >>> 16) + (s0l >>> 16) + (s1l >>> 16) + (c1 >>> 16); - c3 = (t2h & 0xffff) + (t1h & 0xffff) + (s0h & 0xffff) + (s1h & 0xffff) + (c2 >>> 16); - c4 = (t2h >>> 16) + (t1h >>> 16) + (s0h >>> 16) + (s1h >>> 16) + (c3 >>> 16); - - blocks[j] = (c4 << 16) | (c3 & 0xffff); - blocks[j + 1] = (c2 << 16) | (c1 & 0xffff); - } - - let ah = h0h, al = h0l, bh = h1h, bl = h1l, ch = h2h, cl = h2l, dh = h3h, dl = h3l, eh = h4h, el = h4l, - fh = h5h, fl = h5l, gh = h6h, gl = h6l, hh = h7h, hl = h7l; - - bch = bh & ch; - bcl = bl & cl; - - for (let j = 0; j < 160; j += 8) { - s0h = ((ah >>> 28) | (al << 4)) ^ ((al >>> 2) | (ah << 30)) ^ ((al >>> 7) | (ah << 25)); - s0l = ((al >>> 28) | (ah << 4)) ^ ((ah >>> 2) | (al << 30)) ^ ((ah >>> 7) | (al << 25)); - - s1h = ((eh >>> 14) | (el << 18)) ^ ((eh >>> 18) | (el << 14)) ^ ((el >>> 9) | (eh << 23)); - s1l = ((el >>> 14) | (eh << 18)) ^ ((el >>> 18) | (eh << 14)) ^ ((eh >>> 9) | (el << 23)); - - abh = ah & bh; - abl = al & bl; - majh = abh ^ (ah & ch) ^ bch; - majl = abl ^ (al & cl) ^ bcl; - - chh = (eh & fh) ^ (~eh & gh); - chl = (el & fl) ^ (~el & gl); - - t1h = blocks[j]; - t1l = blocks[j + 1]; - t2h = K[j]; - t2l = K[j + 1]; - - c1 = (t2l & 0xffff) + (t1l & 0xffff) + (chl & 0xffff) + (s1l & 0xffff) + (hl & 0xffff); - c2 = (t2l >>> 16) + (t1l >>> 16) + (chl >>> 16) + (s1l >>> 16) + (hl >>> 16) + (c1 >>> 16); - c3 = (t2h & 0xffff) + (t1h & 0xffff) + (chh & 0xffff) + (s1h & 0xffff) + (hh & 0xffff) + (c2 >>> 16); - c4 = (t2h >>> 16) + (t1h >>> 16) + (chh >>> 16) + (s1h >>> 16) + (hh >>> 16) + (c3 >>> 16); - - t1h = (c4 << 16) | (c3 & 0xffff); - t1l = (c2 << 16) | (c1 & 0xffff); - - c1 = (majl & 0xffff) + (s0l & 0xffff); - c2 = (majl >>> 16) + (s0l >>> 16) + (c1 >>> 16); - c3 = (majh & 0xffff) + (s0h & 0xffff) + (c2 >>> 16); - c4 = (majh >>> 16) + (s0h >>> 16) + (c3 >>> 16); - - t2h = (c4 << 16) | (c3 & 0xffff); - t2l = (c2 << 16) | (c1 & 0xffff); - - c1 = (dl & 0xffff) + (t1l & 0xffff); - c2 = (dl >>> 16) + (t1l >>> 16) + (c1 >>> 16); - c3 = (dh & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); - c4 = (dh >>> 16) + (t1h >>> 16) + (c3 >>> 16); - - hh = (c4 << 16) | (c3 & 0xffff); - hl = (c2 << 16) | (c1 & 0xffff); - - c1 = (t2l & 0xffff) + (t1l & 0xffff); - c2 = (t2l >>> 16) + (t1l >>> 16) + (c1 >>> 16); - c3 = (t2h & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); - c4 = (t2h >>> 16) + (t1h >>> 16) + (c3 >>> 16); - - dh = (c4 << 16) | (c3 & 0xffff); - dl = (c2 << 16) | (c1 & 0xffff); - - s0h = ((dh >>> 28) | (dl << 4)) ^ ((dl >>> 2) | (dh << 30)) ^ ((dl >>> 7) | (dh << 25)); - s0l = ((dl >>> 28) | (dh << 4)) ^ ((dh >>> 2) | (dl << 30)) ^ ((dh >>> 7) | (dl << 25)); - - s1h = ((hh >>> 14) | (hl << 18)) ^ ((hh >>> 18) | (hl << 14)) ^ ((hl >>> 9) | (hh << 23)); - s1l = ((hl >>> 14) | (hh << 18)) ^ ((hl >>> 18) | (hh << 14)) ^ ((hh >>> 9) | (hl << 23)); - - dah = dh & ah; - dal = dl & al; - majh = dah ^ (dh & bh) ^ abh; - majl = dal ^ (dl & bl) ^ abl; - - chh = (hh & eh) ^ (~hh & fh); - chl = (hl & el) ^ (~hl & fl); - - t1h = blocks[j + 2]; - t1l = blocks[j + 3]; - t2h = K[j + 2]; - t2l = K[j + 3]; - - c1 = (t2l & 0xffff) + (t1l & 0xffff) + (chl & 0xffff) + (s1l & 0xffff) + (gl & 0xffff); - c2 = (t2l >>> 16) + (t1l >>> 16) + (chl >>> 16) + (s1l >>> 16) + (gl >>> 16) + (c1 >>> 16); - c3 = (t2h & 0xffff) + (t1h & 0xffff) + (chh & 0xffff) + (s1h & 0xffff) + (gh & 0xffff) + (c2 >>> 16); - c4 = (t2h >>> 16) + (t1h >>> 16) + (chh >>> 16) + (s1h >>> 16) + (gh >>> 16) + (c3 >>> 16); - - t1h = (c4 << 16) | (c3 & 0xffff); - t1l = (c2 << 16) | (c1 & 0xffff); - - c1 = (majl & 0xffff) + (s0l & 0xffff); - c2 = (majl >>> 16) + (s0l >>> 16) + (c1 >>> 16); - c3 = (majh & 0xffff) + (s0h & 0xffff) + (c2 >>> 16); - c4 = (majh >>> 16) + (s0h >>> 16) + (c3 >>> 16); - - t2h = (c4 << 16) | (c3 & 0xffff); - t2l = (c2 << 16) | (c1 & 0xffff); - - c1 = (cl & 0xffff) + (t1l & 0xffff); - c2 = (cl >>> 16) + (t1l >>> 16) + (c1 >>> 16); - c3 = (ch & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); - c4 = (ch >>> 16) + (t1h >>> 16) + (c3 >>> 16); - - gh = (c4 << 16) | (c3 & 0xffff); - gl = (c2 << 16) | (c1 & 0xffff); - - c1 = (t2l & 0xffff) + (t1l & 0xffff); - c2 = (t2l >>> 16) + (t1l >>> 16) + (c1 >>> 16); - c3 = (t2h & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); - c4 = (t2h >>> 16) + (t1h >>> 16) + (c3 >>> 16); - - ch = (c4 << 16) | (c3 & 0xffff); - cl = (c2 << 16) | (c1 & 0xffff); - - s0h = ((ch >>> 28) | (cl << 4)) ^ ((cl >>> 2) | (ch << 30)) ^ ((cl >>> 7) | (ch << 25)); - s0l = ((cl >>> 28) | (ch << 4)) ^ ((ch >>> 2) | (cl << 30)) ^ ((ch >>> 7) | (cl << 25)); - - s1h = ((gh >>> 14) | (gl << 18)) ^ ((gh >>> 18) | (gl << 14)) ^ ((gl >>> 9) | (gh << 23)); - s1l = ((gl >>> 14) | (gh << 18)) ^ ((gl >>> 18) | (gh << 14)) ^ ((gh >>> 9) | (gl << 23)); - - cdh = ch & dh; - cdl = cl & dl; - majh = cdh ^ (ch & ah) ^ dah; - majl = cdl ^ (cl & al) ^ dal; - - chh = (gh & hh) ^ (~gh & eh); - chl = (gl & hl) ^ (~gl & el); - - t1h = blocks[j + 4]; - t1l = blocks[j + 5]; - t2h = K[j + 4]; - t2l = K[j + 5]; - - c1 = (t2l & 0xffff) + (t1l & 0xffff) + (chl & 0xffff) + (s1l & 0xffff) + (fl & 0xffff); - c2 = (t2l >>> 16) + (t1l >>> 16) + (chl >>> 16) + (s1l >>> 16) + (fl >>> 16) + (c1 >>> 16); - c3 = (t2h & 0xffff) + (t1h & 0xffff) + (chh & 0xffff) + (s1h & 0xffff) + (fh & 0xffff) + (c2 >>> 16); - c4 = (t2h >>> 16) + (t1h >>> 16) + (chh >>> 16) + (s1h >>> 16) + (fh >>> 16) + (c3 >>> 16); - - t1h = (c4 << 16) | (c3 & 0xffff); - t1l = (c2 << 16) | (c1 & 0xffff); - - c1 = (majl & 0xffff) + (s0l & 0xffff); - c2 = (majl >>> 16) + (s0l >>> 16) + (c1 >>> 16); - c3 = (majh & 0xffff) + (s0h & 0xffff) + (c2 >>> 16); - c4 = (majh >>> 16) + (s0h >>> 16) + (c3 >>> 16); - - t2h = (c4 << 16) | (c3 & 0xffff); - t2l = (c2 << 16) | (c1 & 0xffff); - - c1 = (bl & 0xffff) + (t1l & 0xffff); - c2 = (bl >>> 16) + (t1l >>> 16) + (c1 >>> 16); - c3 = (bh & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); - c4 = (bh >>> 16) + (t1h >>> 16) + (c3 >>> 16); - - fh = (c4 << 16) | (c3 & 0xffff); - fl = (c2 << 16) | (c1 & 0xffff); - - c1 = (t2l & 0xffff) + (t1l & 0xffff); - c2 = (t2l >>> 16) + (t1l >>> 16) + (c1 >>> 16); - c3 = (t2h & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); - c4 = (t2h >>> 16) + (t1h >>> 16) + (c3 >>> 16); - - bh = (c4 << 16) | (c3 & 0xffff); - bl = (c2 << 16) | (c1 & 0xffff); - - s0h = ((bh >>> 28) | (bl << 4)) ^ ((bl >>> 2) | (bh << 30)) ^ ((bl >>> 7) | (bh << 25)); - s0l = ((bl >>> 28) | (bh << 4)) ^ ((bh >>> 2) | (bl << 30)) ^ ((bh >>> 7) | (bl << 25)); - - s1h = ((fh >>> 14) | (fl << 18)) ^ ((fh >>> 18) | (fl << 14)) ^ ((fl >>> 9) | (fh << 23)); - s1l = ((fl >>> 14) | (fh << 18)) ^ ((fl >>> 18) | (fh << 14)) ^ ((fh >>> 9) | (fl << 23)); - - bch = bh & ch; - bcl = bl & cl; - majh = bch ^ (bh & dh) ^ cdh; - majl = bcl ^ (bl & dl) ^ cdl; - - chh = (fh & gh) ^ (~fh & hh); - chl = (fl & gl) ^ (~fl & hl); - - t1h = blocks[j + 6]; - t1l = blocks[j + 7]; - t2h = K[j + 6]; - t2l = K[j + 7]; - - c1 = (t2l & 0xffff) + (t1l & 0xffff) + (chl & 0xffff) + (s1l & 0xffff) + (el & 0xffff); - c2 = (t2l >>> 16) + (t1l >>> 16) + (chl >>> 16) + (s1l >>> 16) + (el >>> 16) + (c1 >>> 16); - c3 = (t2h & 0xffff) + (t1h & 0xffff) + (chh & 0xffff) + (s1h & 0xffff) + (eh & 0xffff) + (c2 >>> 16); - c4 = (t2h >>> 16) + (t1h >>> 16) + (chh >>> 16) + (s1h >>> 16) + (eh >>> 16) + (c3 >>> 16); - - t1h = (c4 << 16) | (c3 & 0xffff); - t1l = (c2 << 16) | (c1 & 0xffff); - - c1 = (majl & 0xffff) + (s0l & 0xffff); - c2 = (majl >>> 16) + (s0l >>> 16) + (c1 >>> 16); - c3 = (majh & 0xffff) + (s0h & 0xffff) + (c2 >>> 16); - c4 = (majh >>> 16) + (s0h >>> 16) + (c3 >>> 16); - - t2h = (c4 << 16) | (c3 & 0xffff); - t2l = (c2 << 16) | (c1 & 0xffff); - - c1 = (al & 0xffff) + (t1l & 0xffff); - c2 = (al >>> 16) + (t1l >>> 16) + (c1 >>> 16); - c3 = (ah & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); - c4 = (ah >>> 16) + (t1h >>> 16) + (c3 >>> 16); - - eh = (c4 << 16) | (c3 & 0xffff); - el = (c2 << 16) | (c1 & 0xffff); - - c1 = (t2l & 0xffff) + (t1l & 0xffff); - c2 = (t2l >>> 16) + (t1l >>> 16) + (c1 >>> 16); - c3 = (t2h & 0xffff) + (t1h & 0xffff) + (c2 >>> 16); - c4 = (t2h >>> 16) + (t1h >>> 16) + (c3 >>> 16); - - ah = (c4 << 16) | (c3 & 0xffff); - al = (c2 << 16) | (c1 & 0xffff); - } - - c1 = (h0l & 0xffff) + (al & 0xffff); - c2 = (h0l >>> 16) + (al >>> 16) + (c1 >>> 16); - c3 = (h0h & 0xffff) + (ah & 0xffff) + (c2 >>> 16); - c4 = (h0h >>> 16) + (ah >>> 16) + (c3 >>> 16); - - this.#h0h = (c4 << 16) | (c3 & 0xffff); - this.#h0l = (c2 << 16) | (c1 & 0xffff); - - c1 = (h1l & 0xffff) + (bl & 0xffff); - c2 = (h1l >>> 16) + (bl >>> 16) + (c1 >>> 16); - c3 = (h1h & 0xffff) + (bh & 0xffff) + (c2 >>> 16); - c4 = (h1h >>> 16) + (bh >>> 16) + (c3 >>> 16); - - this.#h1h = (c4 << 16) | (c3 & 0xffff); - this.#h1l = (c2 << 16) | (c1 & 0xffff); - - c1 = (h2l & 0xffff) + (cl & 0xffff); - c2 = (h2l >>> 16) + (cl >>> 16) + (c1 >>> 16); - c3 = (h2h & 0xffff) + (ch & 0xffff) + (c2 >>> 16); - c4 = (h2h >>> 16) + (ch >>> 16) + (c3 >>> 16); - - this.#h2h = (c4 << 16) | (c3 & 0xffff); - this.#h2l = (c2 << 16) | (c1 & 0xffff); - - c1 = (h3l & 0xffff) + (dl & 0xffff); - c2 = (h3l >>> 16) + (dl >>> 16) + (c1 >>> 16); - c3 = (h3h & 0xffff) + (dh & 0xffff) + (c2 >>> 16); - c4 = (h3h >>> 16) + (dh >>> 16) + (c3 >>> 16); - - this.#h3h = (c4 << 16) | (c3 & 0xffff); - this.#h3l = (c2 << 16) | (c1 & 0xffff); - - c1 = (h4l & 0xffff) + (el & 0xffff); - c2 = (h4l >>> 16) + (el >>> 16) + (c1 >>> 16); - c3 = (h4h & 0xffff) + (eh & 0xffff) + (c2 >>> 16); - c4 = (h4h >>> 16) + (eh >>> 16) + (c3 >>> 16); - - this.#h4h = (c4 << 16) | (c3 & 0xffff); - this.#h4l = (c2 << 16) | (c1 & 0xffff); - - c1 = (h5l & 0xffff) + (fl & 0xffff); - c2 = (h5l >>> 16) + (fl >>> 16) + (c1 >>> 16); - c3 = (h5h & 0xffff) + (fh & 0xffff) + (c2 >>> 16); - c4 = (h5h >>> 16) + (fh >>> 16) + (c3 >>> 16); - - this.#h5h = (c4 << 16) | (c3 & 0xffff); - this.#h5l = (c2 << 16) | (c1 & 0xffff); - - c1 = (h6l & 0xffff) + (gl & 0xffff); - c2 = (h6l >>> 16) + (gl >>> 16) + (c1 >>> 16); - c3 = (h6h & 0xffff) + (gh & 0xffff) + (c2 >>> 16); - c4 = (h6h >>> 16) + (gh >>> 16) + (c3 >>> 16); - - this.#h6h = (c4 << 16) | (c3 & 0xffff); - this.#h6l = (c2 << 16) | (c1 & 0xffff); - - c1 = (h7l & 0xffff) + (hl & 0xffff); - c2 = (h7l >>> 16) + (hl >>> 16) + (c1 >>> 16); - c3 = (h7h & 0xffff) + (hh & 0xffff) + (c2 >>> 16); - c4 = (h7h >>> 16) + (hh >>> 16) + (c3 >>> 16); - - this.#h7h = (c4 << 16) | (c3 & 0xffff); - this.#h7l = (c2 << 16) | (c1 & 0xffff); - } - - hex(): string { - this.finalize(); - const - h0h = this.#h0h, h0l = this.#h0l, h1h = this.#h1h, h1l = this.#h1l, h2h = this.#h2h, h2l = this.#h2l, - h3h = this.#h3h, h3l = this.#h3l, h4h = this.#h4h, h4l = this.#h4l, h5h = this.#h5h, h5l = this.#h5l, - h6h = this.#h6h, h6l = this.#h6l, h7h = this.#h7h, h7l = this.#h7l, bits = this.#bits; - let hex = - HEX_CHARS[(h0h >> 28) & 0x0f] + HEX_CHARS[(h0h >> 24) & 0x0f] + - HEX_CHARS[(h0h >> 20) & 0x0f] + HEX_CHARS[(h0h >> 16) & 0x0f] + - HEX_CHARS[(h0h >> 12) & 0x0f] + HEX_CHARS[(h0h >> 8) & 0x0f] + - HEX_CHARS[(h0h >> 4) & 0x0f] + HEX_CHARS[h0h & 0x0f] + - HEX_CHARS[(h0l >> 28) & 0x0f] + HEX_CHARS[(h0l >> 24) & 0x0f] + - HEX_CHARS[(h0l >> 20) & 0x0f] + HEX_CHARS[(h0l >> 16) & 0x0f] + - HEX_CHARS[(h0l >> 12) & 0x0f] + HEX_CHARS[(h0l >> 8) & 0x0f] + - HEX_CHARS[(h0l >> 4) & 0x0f] + HEX_CHARS[h0l & 0x0f] + - HEX_CHARS[(h1h >> 28) & 0x0f] + HEX_CHARS[(h1h >> 24) & 0x0f] + - HEX_CHARS[(h1h >> 20) & 0x0f] + HEX_CHARS[(h1h >> 16) & 0x0f] + - HEX_CHARS[(h1h >> 12) & 0x0f] + HEX_CHARS[(h1h >> 8) & 0x0f] + - HEX_CHARS[(h1h >> 4) & 0x0f] + HEX_CHARS[h1h & 0x0f] + - HEX_CHARS[(h1l >> 28) & 0x0f] + HEX_CHARS[(h1l >> 24) & 0x0f] + - HEX_CHARS[(h1l >> 20) & 0x0f] + HEX_CHARS[(h1l >> 16) & 0x0f] + - HEX_CHARS[(h1l >> 12) & 0x0f] + HEX_CHARS[(h1l >> 8) & 0x0f] + - HEX_CHARS[(h1l >> 4) & 0x0f] + HEX_CHARS[h1l & 0x0f] + - HEX_CHARS[(h2h >> 28) & 0x0f] + HEX_CHARS[(h2h >> 24) & 0x0f] + - HEX_CHARS[(h2h >> 20) & 0x0f] + HEX_CHARS[(h2h >> 16) & 0x0f] + - HEX_CHARS[(h2h >> 12) & 0x0f] + HEX_CHARS[(h2h >> 8) & 0x0f] + - HEX_CHARS[(h2h >> 4) & 0x0f] + HEX_CHARS[h2h & 0x0f] + - HEX_CHARS[(h2l >> 28) & 0x0f] + HEX_CHARS[(h2l >> 24) & 0x0f] + - HEX_CHARS[(h2l >> 20) & 0x0f] + HEX_CHARS[(h2l >> 16) & 0x0f] + - HEX_CHARS[(h2l >> 12) & 0x0f] + HEX_CHARS[(h2l >> 8) & 0x0f] + - HEX_CHARS[(h2l >> 4) & 0x0f] + HEX_CHARS[h2l & 0x0f] + - HEX_CHARS[(h3h >> 28) & 0x0f] + HEX_CHARS[(h3h >> 24) & 0x0f] + - HEX_CHARS[(h3h >> 20) & 0x0f] + HEX_CHARS[(h3h >> 16) & 0x0f] + - HEX_CHARS[(h3h >> 12) & 0x0f] + HEX_CHARS[(h3h >> 8) & 0x0f] + - HEX_CHARS[(h3h >> 4) & 0x0f] + HEX_CHARS[h3h & 0x0f]; - if (bits >= 256) { - hex += - HEX_CHARS[(h3l >> 28) & 0x0f] + HEX_CHARS[(h3l >> 24) & 0x0f] + - HEX_CHARS[(h3l >> 20) & 0x0f] + HEX_CHARS[(h3l >> 16) & 0x0f] + - HEX_CHARS[(h3l >> 12) & 0x0f] + HEX_CHARS[(h3l >> 8) & 0x0f] + - HEX_CHARS[(h3l >> 4) & 0x0f] + HEX_CHARS[h3l & 0x0f]; - } - if (bits >= 384) { - hex += - HEX_CHARS[(h4h >> 28) & 0x0f] + HEX_CHARS[(h4h >> 24) & 0x0f] + - HEX_CHARS[(h4h >> 20) & 0x0f] + HEX_CHARS[(h4h >> 16) & 0x0f] + - HEX_CHARS[(h4h >> 12) & 0x0f] + HEX_CHARS[(h4h >> 8) & 0x0f] + - HEX_CHARS[(h4h >> 4) & 0x0f] + HEX_CHARS[h4h & 0x0f] + - HEX_CHARS[(h4l >> 28) & 0x0f] + HEX_CHARS[(h4l >> 24) & 0x0f] + - HEX_CHARS[(h4l >> 20) & 0x0f] + HEX_CHARS[(h4l >> 16) & 0x0f] + - HEX_CHARS[(h4l >> 12) & 0x0f] + HEX_CHARS[(h4l >> 8) & 0x0f] + - HEX_CHARS[(h4l >> 4) & 0x0f] + HEX_CHARS[h4l & 0x0f] + - HEX_CHARS[(h5h >> 28) & 0x0f] + HEX_CHARS[(h5h >> 24) & 0x0f] + - HEX_CHARS[(h5h >> 20) & 0x0f] + HEX_CHARS[(h5h >> 16) & 0x0f] + - HEX_CHARS[(h5h >> 12) & 0x0f] + HEX_CHARS[(h5h >> 8) & 0x0f] + - HEX_CHARS[(h5h >> 4) & 0x0f] + HEX_CHARS[h5h & 0x0f] + - HEX_CHARS[(h5l >> 28) & 0x0f] + HEX_CHARS[(h5l >> 24) & 0x0f] + - HEX_CHARS[(h5l >> 20) & 0x0f] + HEX_CHARS[(h5l >> 16) & 0x0f] + - HEX_CHARS[(h5l >> 12) & 0x0f] + HEX_CHARS[(h5l >> 8) & 0x0f] + - HEX_CHARS[(h5l >> 4) & 0x0f] + HEX_CHARS[h5l & 0x0f]; - } - if (bits === 512) { - hex += - HEX_CHARS[(h6h >> 28) & 0x0f] + HEX_CHARS[(h6h >> 24) & 0x0f] + - HEX_CHARS[(h6h >> 20) & 0x0f] + HEX_CHARS[(h6h >> 16) & 0x0f] + - HEX_CHARS[(h6h >> 12) & 0x0f] + HEX_CHARS[(h6h >> 8) & 0x0f] + - HEX_CHARS[(h6h >> 4) & 0x0f] + HEX_CHARS[h6h & 0x0f] + - HEX_CHARS[(h6l >> 28) & 0x0f] + HEX_CHARS[(h6l >> 24) & 0x0f] + - HEX_CHARS[(h6l >> 20) & 0x0f] + HEX_CHARS[(h6l >> 16) & 0x0f] + - HEX_CHARS[(h6l >> 12) & 0x0f] + HEX_CHARS[(h6l >> 8) & 0x0f] + - HEX_CHARS[(h6l >> 4) & 0x0f] + HEX_CHARS[h6l & 0x0f] + - HEX_CHARS[(h7h >> 28) & 0x0f] + HEX_CHARS[(h7h >> 24) & 0x0f] + - HEX_CHARS[(h7h >> 20) & 0x0f] + HEX_CHARS[(h7h >> 16) & 0x0f] + - HEX_CHARS[(h7h >> 12) & 0x0f] + HEX_CHARS[(h7h >> 8) & 0x0f] + - HEX_CHARS[(h7h >> 4) & 0x0f] + HEX_CHARS[h7h & 0x0f] + - HEX_CHARS[(h7l >> 28) & 0x0f] + HEX_CHARS[(h7l >> 24) & 0x0f] + - HEX_CHARS[(h7l >> 20) & 0x0f] + HEX_CHARS[(h7l >> 16) & 0x0f] + - HEX_CHARS[(h7l >> 12) & 0x0f] + HEX_CHARS[(h7l >> 8) & 0x0f] + - HEX_CHARS[(h7l >> 4) & 0x0f] + HEX_CHARS[h7l & 0x0f]; - } - return hex; - } - - toString(): string { - return this.hex(); - } - - digest(): number[] { - this.finalize(); - const - h0h = this.#h0h, h0l = this.#h0l, h1h = this.#h1h, h1l = this.#h1l, h2h = this.#h2h, h2l = this.#h2l, - h3h = this.#h3h, h3l = this.#h3l, h4h = this.#h4h, h4l = this.#h4l, h5h = this.#h5h, h5l = this.#h5l, - h6h = this.#h6h, h6l = this.#h6l, h7h = this.#h7h, h7l = this.#h7l, bits = this.#bits; - const arr = [ - (h0h >> 24) & 0xff, (h0h >> 16) & 0xff, (h0h >> 8) & 0xff, h0h & 0xff, - (h0l >> 24) & 0xff, (h0l >> 16) & 0xff, (h0l >> 8) & 0xff, h0l & 0xff, - (h1h >> 24) & 0xff, (h1h >> 16) & 0xff, (h1h >> 8) & 0xff, h1h & 0xff, - (h1l >> 24) & 0xff, (h1l >> 16) & 0xff, (h1l >> 8) & 0xff, h1l & 0xff, - (h2h >> 24) & 0xff, (h2h >> 16) & 0xff, (h2h >> 8) & 0xff, h2h & 0xff, - (h2l >> 24) & 0xff, (h2l >> 16) & 0xff, (h2l >> 8) & 0xff, h2l & 0xff, - (h3h >> 24) & 0xff, (h3h >> 16) & 0xff, (h3h >> 8) & 0xff, h3h & 0xff - ]; - if (bits >= 256) { - arr.push((h3l >> 24) & 0xff, (h3l >> 16) & 0xff, (h3l >> 8) & 0xff, h3l & 0xff); - } - if (bits >= 384) { - arr.push( - (h4h >> 24) & 0xff, (h4h >> 16) & 0xff, (h4h >> 8) & 0xff, h4h & 0xff, - (h4l >> 24) & 0xff, (h4l >> 16) & 0xff, (h4l >> 8) & 0xff, h4l & 0xff, - (h5h >> 24) & 0xff, (h5h >> 16) & 0xff, (h5h >> 8) & 0xff, h5h & 0xff, - (h5l >> 24) & 0xff, (h5l >> 16) & 0xff, (h5l >> 8) & 0xff, h5l & 0xff - ); - } - if (bits === 512) { - arr.push( - (h6h >> 24) & 0xff, (h6h >> 16) & 0xff, (h6h >> 8) & 0xff, h6h & 0xff, - (h6l >> 24) & 0xff, (h6l >> 16) & 0xff, (h6l >> 8) & 0xff, h6l & 0xff, - (h7h >> 24) & 0xff, (h7h >> 16) & 0xff, (h7h >> 8) & 0xff, h7h & 0xff, - (h7l >> 24) & 0xff, (h7l >> 16) & 0xff, (h7l >> 8) & 0xff, h7l & 0xff - ); - } - return arr; - } - - array(): number[] { - return this.digest(); - } - - arrayBuffer(): ArrayBuffer { - this.finalize(); - const bits = this.#bits; - const buffer = new ArrayBuffer(bits / 8); - const dataView = new DataView(buffer); - dataView.setUint32(0, this.#h0h); - dataView.setUint32(4, this.#h0l); - dataView.setUint32(8, this.#h1h); - dataView.setUint32(12, this.#h1l); - dataView.setUint32(16, this.#h2h); - dataView.setUint32(20, this.#h2l); - dataView.setUint32(24, this.#h3h); - if (bits >= 256) { - dataView.setUint32(28, this.#h3l); - } - if (bits >= 384) { - dataView.setUint32(32, this.#h4h); - dataView.setUint32(36, this.#h4l); - dataView.setUint32(40, this.#h5h); - dataView.setUint32(44, this.#h5l); - } - if (bits === 512) { - dataView.setUint32(48, this.#h6h); - dataView.setUint32(52, this.#h6l); - dataView.setUint32(56, this.#h7h); - dataView.setUint32(60, this.#h7l); - } - return buffer; - } -} - -export class HmacSha512 extends Sha512 { - #inner: boolean; - #bits: number; - #oKeyPad: number[]; - #sharedMemory: boolean; - - constructor(secretKey: Message, bits = 512, sharedMemory = false) { - super(bits, sharedMemory); - - let key: number[] | Uint8Array; - - if (secretKey instanceof ArrayBuffer) { - key = new Uint8Array(secretKey); - } else if (typeof secretKey === "string") { - const bytes: number[] = []; - const length = secretKey.length; - let index = 0; - let code: number; - for (let i = 0; i < length; ++i) { - code = secretKey.charCodeAt(i); - if (code < 0x80) { - bytes[index++] = code; - } else if (code < 0x800) { - bytes[index++] = 0xc0 | (code >> 6); - bytes[index++] = 0x80 | (code & 0x3f); - } else if (code < 0xd800 || code >= 0xe000) { - bytes[index++] = 0xe0 | (code >> 12); - bytes[index++] = 0x80 | ((code >> 6) & 0x3f); - bytes[index++] = 0x80 | (code & 0x3f); - } else { - code = 0x10000 + - (((code & 0x3ff) << 10) | (secretKey.charCodeAt(++i) & 0x3ff)); - bytes[index++] = 0xf0 | (code >> 18); - bytes[index++] = 0x80 | ((code >> 12) & 0x3f); - bytes[index++] = 0x80 | ((code >> 6) & 0x3f); - bytes[index++] = 0x80 | (code & 0x3f); - } - } - key = bytes; - } else { - key = secretKey; - } - if (key.length > 128) { - key = new Sha512(bits, true).update(key).array(); - } - const oKeyPad: number[] = []; - const iKeyPad: number[] = []; - for (let i = 0; i < 128; ++i) { - const b = key[i] || 0; - oKeyPad[i] = 0x5c ^ b; - iKeyPad[i] = 0x36 ^ b; - } - this.update(iKeyPad); - this.#inner = true; - this.#bits = bits; - this.#oKeyPad = oKeyPad; - this.#sharedMemory = sharedMemory; - } - - protected finalize(): void { - super.finalize(); - if (this.#inner) { - this.#inner = false; - const innerHash = this.array(); - super.init(this.#bits, this.#sharedMemory); - this.update(this.#oKeyPad); - this.update(innerHash); - super.finalize(); - } - } -} diff --git a/std/hash/sha512_test.ts b/std/hash/sha512_test.ts deleted file mode 100644 index 97f199b4dc..0000000000 --- a/std/hash/sha512_test.ts +++ /dev/null @@ -1,400 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { HmacSha512, Message, Sha512 } from "./sha512.ts"; -import { assertEquals } from "../testing/asserts.ts"; -import { dirname, fromFileUrl, join, resolve } from "../path/mod.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); -const testdataDir = resolve(moduleDir, "testdata"); - -/** Handy function to convert an array/array buffer to a string of hex values. */ -function toHexString(value: number[] | ArrayBuffer): string { - const array = new Uint8Array(value); - let hex = ""; - for (const v of array) { - const c = v.toString(16); - hex += c.length === 1 ? `0${c}` : c; - } - return hex; -} - -// deno-fmt-ignore -const fixtures: { - sha512bits224: Record>, - sha512bits256: Record>, - sha512: Record>, - hmacSha512bits224: Record>, - hmacSha512bits256: Record>, - hmacSha512: Record> -} = { - sha512bits224: { - "ascii": { - "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4": "", - "944cd2847fb54558d4775db0485a50003111c8e5daa63fe722c6aa37": "The quick brown fox jumps over the lazy dog", - "6d6a9279495ec4061769752e7ff9c68b6b0b3c5a281b7917ce0572de": "The quick brown fox jumps over the lazy dog." - }, - "ascii more than 64 bytes": { - "2e962464977b198ee758d615bbc92251ad2e3c0960068e279fd21d2f": "The MD5 message-digest algorithm is a widely used cryptographic hash function producing a 128-bit (16-byte) hash value, typically expressed in text format as a 32 digit hexadecimal number. MD5 has been utilized in a wide variety of cryptographic applications, and is also commonly used to verify data integrity." - }, - "UTF8": { - "0f46a0ae7f226517dd66ece0ce1efa29ffb7ced05ac4566fdcaed188": "中文", - "562f2e4ee7f7451d20dcc6a0ac1a1e1c4a75f09baaf1cf19af3e15f4": "aécio", - "0533318c52b3d4ad355c2a6c7e727ae3d2efa749db480ac33560b059": "𠜎" - }, - "UTF8 more than 64 bytes": { - "f67e191a5d4ee67a272ccaf6cf597f0c4d6a0c46bd631be7cadb0944": "訊息摘要演算法第五版(英語:Message-Digest Algorithm 5,縮寫為MD5),是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一", - "009c3d1e3172d6df71344982eada855421592aea28acbf660ada7569": "訊息摘要演算法第五版(英語:Message-Digest Algorithm 5,縮寫為MD5),是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一(又譯雜湊演算法、摘要演算法等),主流程式語言普遍已有MD5的實作。" - }, - "special length": { - "6fe6ce0f03b9cd09851e05ba5e3103df56d2a3dbb379fee437e1cdd3": "0123456780123456780123456780123456780123456780123456780", - "9e6994d879f14c242dea25ebc4d03ae6fc710f5eb60c3962b9dba797": "01234567801234567801234567801234567801234567801234567801", - "204ce3b2af187fe90494cb3e4517257c44917bb7ea6578264baa4fcf": "0123456780123456780123456780123456780123456780123456780123456780", - "69ce912fd1f87e02601d6153c02769ebd7c42b29dcb7963a1c3996da": "01234567801234567801234567801234567801234567801234567801234567801234567", - "bd98be1f148dddd8a98c6ba31628c354456b9754166738fe1aba1037": "012345678012345678012345678012345678012345678012345678012345678012345678" - }, - "Array": { - "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4": [], - "6945cf025ed66055282665c546781e32c5a479b5e9b479e96b0c23fe": [211, 212], - "944cd2847fb54558d4775db0485a50003111c8e5daa63fe722c6aa37": [84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103], - "69ce912fd1f87e02601d6153c02769ebd7c42b29dcb7963a1c3996da": [48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55] - }, - "Uint8Array": { - "6945cf025ed66055282665c546781e32c5a479b5e9b479e96b0c23fe": new Uint8Array([211, 212]), - "944cd2847fb54558d4775db0485a50003111c8e5daa63fe722c6aa37": new Uint8Array([84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103]) - }, - "Int8Array": { - "944cd2847fb54558d4775db0485a50003111c8e5daa63fe722c6aa37": new Int8Array([84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103]) - }, - "ArrayBuffer": { - "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4": new ArrayBuffer(0), - "283bb59af7081ed08197227d8f65b9591ffe1155be43e9550e57f941": new ArrayBuffer(1) - } - }, - sha512bits256: { - "ascii": { - "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a": "", - "dd9d67b371519c339ed8dbd25af90e976a1eeefd4ad3d889005e532fc5bef04d": "The quick brown fox jumps over the lazy dog", - "1546741840f8a492b959d9b8b2344b9b0eb51b004bba35c0aebaac86d45264c3": "The quick brown fox jumps over the lazy dog." - }, - "ascii more than 64 bytes": { - "21e2e940930b23f1de6377086d07e22033c6bbf3fd9fbf4b62ec66e6c08c25be": "The MD5 message-digest algorithm is a widely used cryptographic hash function producing a 128-bit (16-byte) hash value, typically expressed in text format as a 32 digit hexadecimal number. MD5 has been utilized in a wide variety of cryptographic applications, and is also commonly used to verify data integrity." - }, - "UTF8": { - "b6dab29c16ec35ab34a5d92ff135b58de96741dda78b1009a2181cf8b45d2f72": "中文", - "122802ca08e39c2ef46f6a81379dc5683bd8aa074dfb54259f0add4d8b5504bc": "aécio", - "1032308151c0f4f5f8d4e0d96956352eb8ff87da98df8878d8795a858a7e7c08": "𠜎" - }, - "UTF8 more than 64 bytes": { - "d32a41d9858e45b68402f77cf9f3c3f992c36a4bffd230f78d666c87f97eaf7e": "訊息摘要演算法第五版(英語:Message-Digest Algorithm 5,縮寫為MD5),是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一", - "bd1abad59e6b8ad69bc17b6e05aa13f0cb725467fbeb45b83d3e4094332d1367": "訊息摘要演算法第五版(英語:Message-Digest Algorithm 5,縮寫為MD5),是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一(又譯雜湊演算法、摘要演算法等),主流程式語言普遍已有MD5的實作。" - }, - "special length": { - "99fb09c8564fbd52274cfaf1130ae02dad89efac9a31dc00e9bfc13db1ff4f56": "0123456780123456780123456780123456780123456780123456780", - "7a3204b58878f5a65a54f77e270d5df579a8016e0e472cc91833689c4cf8ca07": "01234567801234567801234567801234567801234567801234567801", - "f4aa5f7692e6fee7237510b9a886f7b7aa4098926b45eaf70672bdd6d316a633": "0123456780123456780123456780123456780123456780123456780123456780", - "3f8fc8ec35656592ce61bf44895b6d94077aae3bddd99236a0b04ccf936699ed": "01234567801234567801234567801234567801234567801234567801234567801234567", - "4cb330a62170d92fe3d03bcf9284b590cf08d38d3a3c1e661abba3641d0b7502": "012345678012345678012345678012345678012345678012345678012345678012345678" - }, - "Array": { - "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a": [], - "547cf572033bb67ae341d010b348691ee9c550d07b796e0c6e6ad3503fa36cb3": [211, 212], - "dd9d67b371519c339ed8dbd25af90e976a1eeefd4ad3d889005e532fc5bef04d": [84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103], - "3f8fc8ec35656592ce61bf44895b6d94077aae3bddd99236a0b04ccf936699ed": [48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55] - }, - "Uint8Array": { - "547cf572033bb67ae341d010b348691ee9c550d07b796e0c6e6ad3503fa36cb3": new Uint8Array([211, 212]), - "dd9d67b371519c339ed8dbd25af90e976a1eeefd4ad3d889005e532fc5bef04d": new Uint8Array([84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103]) - }, - "Int8Array": { - "dd9d67b371519c339ed8dbd25af90e976a1eeefd4ad3d889005e532fc5bef04d": new Int8Array([84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103]) - }, - "ArrayBuffer": { - "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a": new ArrayBuffer(0), - "10baad1713566ac2333467bddb0597dec9066120dd72ac2dcb8394221dcbe43d": new ArrayBuffer(1) - } - }, - sha512: { - "ascii": { - "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e": "", - "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6": "The quick brown fox jumps over the lazy dog", - "91ea1245f20d46ae9a037a989f54f1f790f0a47607eeb8a14d12890cea77a1bbc6c7ed9cf205e67b7f2b8fd4c7dfd3a7a8617e45f3c463d481c7e586c39ac1ed": "The quick brown fox jumps over the lazy dog." - }, - "ascii more than 64 bytes": { - "a8dedff31e3be9df6413ef5b4ecb93d62d3fbcb04297552eab5370e04afd45927854a4373037e81a50186e678d818c9ba824f4c850f3d0f02764af0252076979": "The MD5 message-digest algorithm is a widely used cryptographic hash function producing a 128-bit (16-byte) hash value, typically expressed in text format as a 32 digit hexadecimal number. MD5 has been utilized in a wide variety of cryptographic applications, and is also commonly used to verify data integrity." - }, - "UTF8": { - "8b88efc2ebbcbdad5ac2d65af05bec57bda25e71fd5fb25bbd892057a2755fbd05d8d8491cb2946febd5b0f124ffdfbaecf7e34946353c4f1b5ab29545895468": "中文", - "e1c6925243db76985abacaf9fa85e22697f549e67f65a36c88e4046a2260990ff9eefc3402396ea8dcbe8c592d8d5671bea612156eda38d3708d394bbd17d493": "aécio", - "f3e7ee9cdf7dbb52f7edd59ce3d49868c64f2b3aceceab060b8eaaebdf9de0dae5866d660e3319c5aad426a2176cb1703efc73eb24d1a90458ceda1b7f4e3940": "𠜎" - }, - "UTF8 more than 64 bytes": { - "6cb7f6d3381a187edadb43c7cdcfbbed4d2c213a7dce8ea08fe42b9882b64e643202b4974a6db94f94650ab9173d97c58bd59f6d19d27e01aab76d8d08855c65": "訊息摘要演算法第五版(英語:Message-Digest Algorithm 5,縮寫為MD5),是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一", - "d24af1901aaf1458f089a6eddf784ce61c3012aee0df98bdb67ad2dc6b41a3b4051d40caac524373930ae396a2dde99a9204871b40892eea3e5f3c8d46da0c3c": "訊息摘要演算法第五版(英語:Message-Digest Algorithm 5,縮寫為MD5),是當前電腦領域用於確保資訊傳輸完整一致而廣泛使用的雜湊演算法之一(又譯雜湊演算法、摘要演算法等),主流程式語言普遍已有MD5的實作。" - }, - "special length": { - "6b4a72eb22d2d24c0a429dd99ce5835b134144ac5fce446f66dbf2f421dcc5f8a177e4774f4a48173c5640724b186c2c4112a80937b1167f3e7bb511f4c41b6a": "0123456780123456780123456780123456780123456780123456780", - "76f3cb2ed5b0b405479495b2d3576f4b469b6ffc4b06e3b512a658b84c1b91cf72c41c54d8714ecf19d04696f09e0034632fe98ae848ffd35b83c7e72399a590": "01234567801234567801234567801234567801234567801234567801", - "56d2391faebd8d69b067cd5c0cb364ffc2e2ab87ce5bb06a562b44c8dcb0b83816ad2c0c062537838992b181fadc43ff00e1ebb92ddb1129b81b4864bafb5f63": "0123456780123456780123456780123456780123456780123456780123456780", - "317ab88f192258711b8ae0197395b7a8191796fb41140c16c596699481149b47130e26b3bfa724227202fa8371752ca92e3cb9dd202caf29334038e0848cb43f": "01234567801234567801234567801234567801234567801234567801234567801234567", - "23880e96199df52b4386d190adddaa33cbf7e0bfa7d2067c60eb44ee103667fd002c32e184195fef65fd4178853b1c661d9f260d721df85872e5f645f4388841": "012345678012345678012345678012345678012345678012345678012345678012345678" - }, - "Array": { - "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e": [], - "8df0195b2807fdc8c7674c191562e9d0db38b257cc0d3df64669878fe5bb1bbaff53cc8898edcf46cbecb945dc71b6ad738da8ca6f3a824123a54afde5d1d5b0": [211, 212], - "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6": [84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103], - "317ab88f192258711b8ae0197395b7a8191796fb41140c16c596699481149b47130e26b3bfa724227202fa8371752ca92e3cb9dd202caf29334038e0848cb43f": [48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55, 56, 48, 49, 50, 51, 52, 53, 54, 55] - }, - "Uint8Array": { - "8df0195b2807fdc8c7674c191562e9d0db38b257cc0d3df64669878fe5bb1bbaff53cc8898edcf46cbecb945dc71b6ad738da8ca6f3a824123a54afde5d1d5b0": new Uint8Array([211, 212]), - "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6": new Uint8Array([84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103]) - }, - "Int8Array": { - "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6": new Int8Array([84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103]) - }, - "ArrayBuffer": { - "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e": new ArrayBuffer(0), - "b8244d028981d693af7b456af8efa4cad63d282e19ff14942c246e50d9351d22704a802a71c3580b6370de4ceb293c324a8423342557d4e5c38438f0e36910ee": new ArrayBuffer(1) - } - }, - hmacSha512bits224: { - "Test Vectors": { - "b244ba01307c0e7a8ccaad13b1067a4cf6b961fe0c6a20bda3d92039": [ - [0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b], - "Hi There" - ], - "4a530b31a79ebcce36916546317c45f247d83241dfb818fd37254bde": [ - "Jefe", - "what do ya want for nothing?" - ], - "db34ea525c2c216ee5a6ccb6608bea870bbef12fd9b96a5109e2b6fc": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - [0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd] - ], - "c2391863cda465c6828af06ac5d4b72d0b792109952da530e11a0d26": [ - [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19], - [0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd] - ], - "29bef8ce88b54d4226c3c7718ea9e32ace2429026f089e38cea9aeda": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - "Test Using Larger Than Block-Size Key - Hash Key First" - ], - "82a9619b47af0cea73a8b9741355ce902d807ad87ee9078522a246e1": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm." - ] - }, - "UTF8": { - "24e1153464bf5ec62ad2eeeb88ff644f2441a124d1e16e8ae5fb1508": ["中文", "aécio"], - "7a08cecb4700304bc5c466acc1fb312d198374817052a03df07610c6": ["aécio", "𠜎"], - "697973678b7d0075676ec3cbbc19e343ed16fa20c14d8074b76b0861": ["𠜎", "中文"] - }, - "Uint8Array": { - "defdc4a1a6597147ea0c7d0a59ae0a5e64b9413a6400acac28aecdd1": [new Uint8Array(0), "Hi There"] - }, - "ArrayBuffer": { - "defdc4a1a6597147ea0c7d0a59ae0a5e64b9413a6400acac28aecdd1": [new ArrayBuffer(0), "Hi There"] - } - }, - hmacSha512bits256: { - "Test Vectors": { - "9f9126c3d9c3c330d760425ca8a217e31feae31bfe70196ff81642b868402eab": [ - [0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b], - "Hi There" - ], - "6df7b24630d5ccb2ee335407081a87188c221489768fa2020513b2d593359456": [ - "Jefe", - "what do ya want for nothing?" - ], - "229006391d66c8ecddf43ba5cf8f83530ef221a4e9401840d1bead5137c8a2ea": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - [0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd] - ], - "36d60c8aa1d0be856e10804cf836e821e8733cbafeae87630589fd0b9b0a2f4c": [ - [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19], - [0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd] - ], - "87123c45f7c537a404f8f47cdbedda1fc9bec60eeb971982ce7ef10e774e6539": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - "Test Using Larger Than Block-Size Key - Hash Key First" - ], - "6ea83f8e7315072c0bdaa33b93a26fc1659974637a9db8a887d06c05a7f35a66": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm." - ] - }, - "UTF8": { - "633400fa4bc12c3690efa218c90b56ab1af81b91ad62b57bdbe84988c51071e0": ["中文", "aécio"], - "80eff00e32e0c0813d4c04e296b5ac079ec896e673cc04b0ff14222e151ad0b0": ["aécio", "𠜎"], - "3f801c729e5330a0b91aecc751a26c35688a94989e2098c73bf0c6ac02b99e58": ["𠜎", "中文"] - }, - "Uint8Array": { - "1e08e33f9357abd2a3cfbc82a623d892bb6dccf175d22c0cf24269a7a59dfad6": [new Uint8Array(0), "Hi There"] - }, - "ArrayBuffer": { - "1e08e33f9357abd2a3cfbc82a623d892bb6dccf175d22c0cf24269a7a59dfad6": [new ArrayBuffer(0), "Hi There"] - } - }, - hmacSha512: { - "Test Vectors": { - "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854": [ - [0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b], - "Hi There" - ], - "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737": [ - "Jefe", - "what do ya want for nothing?" - ], - "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - [0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd] - ], - "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd": [ - [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19], - [0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd] - ], - "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - "Test Using Larger Than Block-Size Key - Hash Key First" - ], - "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58": [ - [0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm." - ] - }, - "UTF8": { - "e9e5906be0aecbc028a5fc759c9dbb86efc9a22950af8e678302a215aeee0b021edc50bbdd71c656730177b7e96c9a3bcf3cb9592bc84a5f3e8900cb67c7eca6": ["中文", "aécio"], - "d02a8d258d855967d5be47240bbedd986a31c29eb5beb35abdbe2725651bf33a195cdfaadb9e76dc4790c71dfea33f708afa04b9471d03f5f0db8440993b9612": ["aécio", "𠜎"], - "a443d463546586a5dd591ef848f0939c3a7089d63ef81d58ccc0a2611a1d374a39717d6893ea10d61ca0e87d5be7c80b29b2ed991c4a62e12d10c7f6b1b9d7ae": ["𠜎", "中文"] - }, - "Uint8Array": { - "f7688a104326d36c1940f6d28d746c0661d383e0d14fe8a04649444777610f5dd9565a36846ab9e9e734cf380d3a070d8ef021b5f3a50c481710a464968e3419": [new Uint8Array(0), "Hi There"] - }, - "ArrayBuffer": { - "f7688a104326d36c1940f6d28d746c0661d383e0d14fe8a04649444777610f5dd9565a36846ab9e9e734cf380d3a070d8ef021b5f3a50c481710a464968e3419": [new ArrayBuffer(0), "Hi There"] - } - }, -}; - -const methods = ["array", "arrayBuffer", "digest", "hex"] as const; - -for (const method of methods) { - for (const [name, tests] of Object.entries(fixtures.sha512bits224)) { - let i = 1; - for (const [expected, message] of Object.entries(tests)) { - Deno.test({ - name: `sha512/224.${method}() - ${name} - #${i++}`, - fn() { - const algorithm = new Sha512(224); - algorithm.update(message); - const actual = method === "hex" - ? algorithm[method]() - : toHexString(algorithm[method]()); - assertEquals(actual, expected); - }, - }); - } - } -} - -for (const method of methods) { - for (const [name, tests] of Object.entries(fixtures.sha512bits256)) { - let i = 1; - for (const [expected, message] of Object.entries(tests)) { - Deno.test({ - name: `sha512/256.${method}() - ${name} - #${i++}`, - fn() { - const algorithm = new Sha512(256); - algorithm.update(message); - const actual = method === "hex" - ? algorithm[method]() - : toHexString(algorithm[method]()); - assertEquals(actual, expected); - }, - }); - } - } -} - -for (const method of methods) { - for (const [name, tests] of Object.entries(fixtures.sha512)) { - let i = 1; - for (const [expected, message] of Object.entries(tests)) { - Deno.test({ - name: `sha512.${method}() - ${name} - #${i++}`, - fn() { - const algorithm = new Sha512(); - algorithm.update(message); - const actual = method === "hex" - ? algorithm[method]() - : toHexString(algorithm[method]()); - assertEquals(actual, expected); - }, - }); - } - } -} - -for (const method of methods) { - for (const [name, tests] of Object.entries(fixtures.hmacSha512bits224)) { - let i = 1; - for (const [expected, [key, message]] of Object.entries(tests)) { - Deno.test({ - name: `hmacSha512/224.${method}() - ${name} - #${i++}`, - fn() { - const algorithm = new HmacSha512(key, 224); - algorithm.update(message); - const actual = method === "hex" - ? algorithm[method]() - : toHexString(algorithm[method]()); - assertEquals(actual, expected); - }, - }); - } - } -} - -for (const method of methods) { - for (const [name, tests] of Object.entries(fixtures.hmacSha512bits256)) { - let i = 1; - for (const [expected, [key, message]] of Object.entries(tests)) { - Deno.test({ - name: `hmacSha512/256.${method}() - ${name} - #${i++}`, - fn() { - const algorithm = new HmacSha512(key, 256); - algorithm.update(message); - const actual = method === "hex" - ? algorithm[method]() - : toHexString(algorithm[method]()); - assertEquals(actual, expected); - }, - }); - } - } -} - -for (const method of methods) { - for (const [name, tests] of Object.entries(fixtures.hmacSha512)) { - let i = 1; - for (const [expected, [key, message]] of Object.entries(tests)) { - Deno.test({ - name: `hmacSha512.${method}() - ${name} - #${i++}`, - fn() { - const algorithm = new HmacSha512(key); - algorithm.update(message); - const actual = method === "hex" - ? algorithm[method]() - : toHexString(algorithm[method]()); - assertEquals(actual, expected); - }, - }); - } - } -} - -Deno.test("[hash/sha512] test Uint8Array from Reader", async () => { - const data = await Deno.readFile(join(testdataDir, "hashtest")); - const hash = new Sha512().update(data).hex(); - assertEquals( - hash, - "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff", - ); -}); diff --git a/std/hash/test.ts b/std/hash/test.ts deleted file mode 100644 index 31540e384d..0000000000 --- a/std/hash/test.ts +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals, assertThrows } from "../testing/asserts.ts"; -import { createHash, SupportedAlgorithm } from "./mod.ts"; - -const millionAs = "a".repeat(1000000); - -const testSetHex: Record = { - md5: [ - ["", "d41d8cd98f00b204e9800998ecf8427e"], - ["abc", "900150983cd24fb0d6963f7d28e17f72"], - ["deno", "c8772b401bc911da102a5291cc4ec83b"], - [ - "The quick brown fox jumps over the lazy dog", - "9e107d9d372bb6826bd81d3542a419d6", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "3b0c8ac703f828b04c6c197006d17218", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "014842d480b571495a4a0363793f7367", - ], - [millionAs, "7707d6ae4e027c70eea2a935c2296f21"], - ], - sha1: [ - ["", "da39a3ee5e6b4b0d3255bfef95601890afd80709"], - ["abc", "a9993e364706816aba3e25717850c26c9cd0d89d"], - ["deno", "bb3d8e712d9e7ad4af08d4a38f3f52d9683d58eb"], - [ - "The quick brown fox jumps over the lazy dog", - "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "c2db330f6083854c99d4b5bfb6e8f29f201be699", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "0098ba824b5c16427bd7a1122a5a442a25ec644d", - ], - [millionAs, "34aa973cd4c4daa4f61eeb2bdbad27316534016f"], - ], - sha256: [ - ["", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"], - ["abc", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"], - [ - "deno", - "e872e7bd2ae6abcf13a4c834029a342c882c1162ebf77b6720968b2000312ffb", - ], - [ - "The quick brown fox jumps over the lazy dog", - "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "b35439a4ac6f0948b6d6f9e3c6af0f5f590ce20f1bde7090ef7970686ec6738a", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "ffe054fe7ae0cb6dc65c3af9b61d5209f439851db43d0ba5997337df154668eb", - ], - [ - millionAs, - "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0", - ], - ], - sha512: [ - [ - "", - "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", - ], - [ - "abc", - "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", - ], - [ - "deno", - "05b6ef7b13673c57c455d968cb7acbdd4fe10e24f25520763d69025d768d14124987b14e3aff8ff1565aaeba1c405fc89cc435938ff46a426f697b0f509e3799", - ], - [ - "The quick brown fox jumps over the lazy dog", - "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "962b64aae357d2a4fee3ded8b539bdc9d325081822b0bfc55583133aab44f18bafe11d72a7ae16c79ce2ba620ae2242d5144809161945f1367f41b3972e26e04", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "01d35c10c6c38c2dcf48f7eebb3235fb5ad74a65ec4cd016e2354c637a8fb49b695ef3c1d6f7ae4cd74d78cc9c9bcac9d4f23a73019998a7f73038a5c9b2dbde", - ], - [ - millionAs, - "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b", - ], - ], - "sha3-256": [ - ["", "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"], - ["abc", "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"], - [ - "deno", - "74a6286af90f8775d74080f864cf80b11eecf6f14d325c5ef8c9f7ccc8055517", - ], - [ - "The quick brown fox jumps over the lazy dog", - "69070dda01975c8c120c3aada1b282394e7f032fa9cf32f4cb2259a0897dfc04", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "f6fe8de5c8f5014786f07e9f7b08130f920dd55e587d47021686b26cf2323deb", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "043d104b5480439c7acff8831ee195183928d9b7f8fcb0c655a086a87923ffee", - ], - [ - millionAs, - "5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1", - ], - ], - "sha3-512": [ - [ - "", - "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26", - ], - [ - "abc", - "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0", - ], - [ - "deno", - "9e248199d744a8d810e7fda8207f98f27453bd6cb5a02965b5477d3d07516bbac6831009eedddadc8901d742dbfe3fd4afa770230a84e4d51bf30a0c99efa03c", - ], - [ - "The quick brown fox jumps over the lazy dog", - "01dedd5de4ef14642445ba5f5b97c15e47b9ad931326e4b0727cd94cefc44fff23f07bf543139939b49128caf436dc1bdee54fcb24023a08d9403f9b4bf0d450", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "302d75b7947aa354a54872df954dc0dfe673cf60faedebdea7e9b22263a3bdf39e346a4f2868639836955396f186a67b02ec8e3365bdf59867070f81849c2c35", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "2141e94c719955872c455c83eb83e7618a9b523a0ee9f118e794fbff8b148545c8e8caabef08d8cfdb1dfb36b4dd81cc48bfc77e7f85632197b882fd9c4384e0", - ], - [ - millionAs, - "3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87", - ], - ], -}; - -const testSetBase64: Record = { - md5: [ - ["", "1B2M2Y8AsgTpgAmY7PhCfg=="], - ["abc", "kAFQmDzST7DWlj99KOF/cg=="], - ["deno", "yHcrQBvJEdoQKlKRzE7IOw=="], - ["The quick brown fox jumps over the lazy dog", "nhB9nTcrtoJr2B01QqQZ1g=="], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "OwyKxwP4KLBMbBlwBtFyGA==", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "AUhC1IC1cUlaSgNjeT9zZw==", - ], - [millionAs, "dwfWrk4CfHDuoqk1wilvIQ=="], - ], - sha1: [ - ["", "2jmj7l5rSw0yVb/vlWAYkK/YBwk="], - ["abc", "qZk+NkcGgWq6PiVxeFDCbJzQ2J0="], - ["deno", "uz2OcS2eetSvCNSjjz9S2Wg9WOs="], - [ - "The quick brown fox jumps over the lazy dog", - "L9ThxnotKPzthJ7hu3bnORuT6xI=", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "wtszD2CDhUyZ1LW/tujynyAb5pk=", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "AJi6gktcFkJ716ESKlpEKiXsZE0=", - ], - [millionAs, "NKqXPNTE2qT2Husr260nMWU0AW8="], - ], - sha256: [ - ["", "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="], - ["abc", "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0="], - ["deno", "6HLnvSrmq88TpMg0Apo0LIgsEWLr93tnIJaLIAAxL/s="], - [ - "The quick brown fox jumps over the lazy dog", - "16j7swfXgJRpypq8sAguT41WUeRtPNt2LQLQvzfJ5ZI=", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "s1Q5pKxvCUi21vnjxq8PX1kM4g8b3nCQ73lwaG7Gc4o=", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "/+BU/nrgy23GXDr5th1SCfQ5hR20PQulmXM33xVGaOs=", - ], - [millionAs, "zcduXJkU+5KBocfihNc+Z/GAmkiklyAOBG05zMcRLNA="], - ], - sha512: [ - [ - "", - "z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==", - ], - [ - "abc", - "3a81oZNherrMQXNJriBBMRLm+k6JqX6iCp7u5ktV05ohkpkqJ0/BqDa6PCOj/uu9RU1EI2Q86A4qmslPpUyknw==", - ], - [ - "deno", - "BbbvexNnPFfEVdloy3rL3U/hDiTyVSB2PWkCXXaNFBJJh7FOOv+P8VZarrocQF/InMQ1k4/0akJvaXsPUJ43mQ==", - ], - [ - "The quick brown fox jumps over the lazy dog", - "B+VH2VhvanP3P7rAQ17XaVEhj7fQyNeIownXhUNru2Quk6JSqVTyORJUfR6KO17W4b/XCXghIz+gU489uFT+5g==", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "litkquNX0qT+497YtTm9ydMlCBgisL/FVYMTOqtE8Yuv4R1yp64Wx5ziumIK4iQtUUSAkWGUXxNn9Bs5cuJuBA==", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "AdNcEMbDjC3PSPfuuzI1+1rXSmXsTNAW4jVMY3qPtJtpXvPB1veuTNdNeMycm8rJ1PI6cwGZmKf3MDilybLb3g==", - ], - [ - millionAs, - "5xhIPQznaWROLkLHvBW0Y44fmLE7IEQoVjKoA6+pc+veD/JEh36mCkywQyzld8Mb6wCcXCxJqi5OrbIXrYzAmw==", - ], - ], - "sha3-256": [ - ["", "p//G+L8e12ZRwUdWoGHWYvWA/03kO0n6gtgKS4D4Q0o="], - ["abc", "Ophdp0/iJbIEXBcta9OQvYVfCG4+nVJbRr/iRRFDFTI="], - ["deno", "dKYoavkPh3XXQID4ZM+AsR7s9vFNMlxe+Mn3zMgFVRc="], - [ - "The quick brown fox jumps over the lazy dog", - "aQcN2gGXXIwSDDqtobKCOU5/Ay+pzzL0yyJZoIl9/AQ=", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "9v6N5cj1AUeG8H6fewgTD5IN1V5YfUcCFoaybPIyPes=", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "BD0QS1SAQ5x6z/iDHuGVGDko2bf4/LDGVaCGqHkj/+4=", - ], - [millionAs, "XIh1rkdKNjS6T9VeyFv/1mHzKsp1xtaZ0M3LbBFYkcE="], - ], - "sha3-512": [ - [ - "", - "pp9zzKI6msXItWfcGFp1bpfJghZP4lhZ4NHcwUdcgKYVshI68fX5TBHj6UAsOsVY9QAZnZW20+MBdYWGKB3NJg==", - ], - [ - "abc", - "t1GFCxpXFopWk82SS2sJbgj2IYJ0RPcNiE9dAkDScS4Q4RbpGSrzyRp+xXZH45NAVzQLTPQI1aVlkvgnTuxT8A==", - ], - [ - "deno", - "niSBmddEqNgQ5/2oIH+Y8nRTvWy1oClltUd9PQdRa7rGgxAJ7t3a3IkB10Lb/j/Ur6dwIwqE5NUb8woMme+gPA==", - ], - [ - "The quick brown fox jumps over the lazy dog", - "Ad7dXeTvFGQkRbpfW5fBXke5rZMTJuSwcnzZTO/ET/8j8Hv1QxOZObSRKMr0Ntwb3uVPyyQCOgjZQD+bS/DUUA==", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "MC11t5R6o1SlSHLflU3A3+Zzz2D67evep+myImOjvfOeNGpPKGhjmDaVU5bxhqZ7AuyOM2W99ZhnBw+BhJwsNQ==", - ], - [ - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "IUHpTHGZVYcsRVyD64PnYYqbUjoO6fEY55T7/4sUhUXI6Mqr7wjYz9sd+za03YHMSL/Hfn+FYyGXuIL9nEOE4A==", - ], - [ - millionAs, - "PDqHbaFANKtgYnwHe7mPfhIKKlNwIS3/szhaGNTziFntMR0KnVFBzpzFxm7mibJmqKoYrOgoKg4NtZbJCwp7hw==", - ], - ], -}; - -Deno.test("[hash/all/hex] testAllHex", () => { - for (const algorithm in testSetHex) { - for (const [input, output] of testSetHex[algorithm]) { - const hash = createHash(algorithm as SupportedAlgorithm); - assertEquals(hash.update(input).toString(), output); - } - } -}); - -Deno.test("[hash/all/base64] testAllHex", () => { - for (const algorithm in testSetBase64) { - for (const [input, output] of testSetBase64[algorithm]) { - const hash = createHash(algorithm as SupportedAlgorithm); - assertEquals(hash.update(input).toString("base64"), output); - } - } -}); - -Deno.test("[hash/double_digest] testDoubleDigest", () => { - assertThrows( - (): void => { - const hash = createHash("md5"); - hash.update("test"); - const h1 = hash.digest(); - const h2 = hash.digest(); - assertEquals(h1, h2); - }, - Error, - "hash: already digested", - ); -}); diff --git a/std/hash/testdata/hashtest b/std/hash/testdata/hashtest deleted file mode 100644 index 30d74d2584..0000000000 --- a/std/hash/testdata/hashtest +++ /dev/null @@ -1 +0,0 @@ -test \ No newline at end of file diff --git a/std/http/README.md b/std/http/README.md deleted file mode 100644 index c2c4c8ce6d..0000000000 --- a/std/http/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# http - -```typescript -import { serve } from "https://deno.land/std@$STD_VERSION/http/server.ts"; -const server = serve({ port: 8000 }); -console.log("http://localhost:8000/"); -for await (const req of server) { - req.respond({ body: "Hello World\n" }); -} -``` - -### File Server - -A small program for serving local files over HTTP. - -```sh -deno run --allow-net --allow-read https://deno.land/std/http/file_server.ts -> HTTP server listening on http://0.0.0.0:4507/ -``` - -## Cookie - -Helper to manipulate `Cookie` through `ServerRequest` and `Response`. - -```ts -import { ServerRequest } from "https://deno.land/std@$STD_VERSION/http/server.ts"; -import { getCookies } from "https://deno.land/std@$STD_VERSION/http/cookie.ts"; - -let request = new ServerRequest(); -request.headers = new Headers(); -request.headers.set("Cookie", "full=of; tasty=chocolate"); - -const cookies = getCookies(request); -console.log("cookies:", cookies); -// cookies: { full: "of", tasty: "chocolate" } -``` - -To set a `Cookie` you can add `CookieOptions` to properly set your `Cookie`: - -```ts -import { Response } from "https://deno.land/std@$STD_VERSION/http/server.ts"; -import { - Cookie, - setCookie, -} from "https://deno.land/std@$STD_VERSION/http/cookie.ts"; - -let response: Response = {}; -const cookie: Cookie = { name: "Space", value: "Cat" }; -setCookie(response, cookie); - -const cookieHeader = response.headers.get("set-cookie"); -console.log("Set-Cookie:", cookieHeader); -// Set-Cookie: Space=Cat -``` - -Deleting a `Cookie` will set its expiration date before now. Forcing the browser -to delete it. - -```ts -import { Response } from "https://deno.land/std@$STD_VERSION/http/server.ts"; -import { deleteCookie } from "https://deno.land/std@$STD_VERSION/http/cookie.ts"; - -let response: Response = {}; -deleteCookie(response, "deno"); - -const cookieHeader = response.headers.get("set-cookie"); -console.log("Set-Cookie:", cookieHeader); -// Set-Cookie: deno=; Expires=Thus, 01 Jan 1970 00:00:00 GMT -``` - -**Note**: At the moment multiple `Set-Cookie` in a `Response` is not handled. diff --git a/std/http/_io.ts b/std/http/_io.ts deleted file mode 100644 index 529f59cb52..0000000000 --- a/std/http/_io.ts +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { BufReader, BufWriter } from "../io/bufio.ts"; -import { TextProtoReader } from "../textproto/mod.ts"; -import { assert } from "../_util/assert.ts"; -import { encoder } from "../encoding/utf8.ts"; -import { Response, ServerRequest } from "./server.ts"; -import { STATUS_TEXT } from "./http_status.ts"; - -export function emptyReader(): Deno.Reader { - return { - read(_: Uint8Array): Promise { - return Promise.resolve(null); - }, - }; -} - -export function bodyReader(contentLength: number, r: BufReader): Deno.Reader { - let totalRead = 0; - let finished = false; - async function read(buf: Uint8Array): Promise { - if (finished) return null; - let result: number | null; - const remaining = contentLength - totalRead; - if (remaining >= buf.byteLength) { - result = await r.read(buf); - } else { - const readBuf = buf.subarray(0, remaining); - result = await r.read(readBuf); - } - if (result !== null) { - totalRead += result; - } - finished = totalRead === contentLength; - return result; - } - return { read }; -} - -export function chunkedBodyReader(h: Headers, r: BufReader): Deno.Reader { - // Based on https://tools.ietf.org/html/rfc2616#section-19.4.6 - const tp = new TextProtoReader(r); - let finished = false; - const chunks: Array<{ - offset: number; - data: Uint8Array; - }> = []; - async function read(buf: Uint8Array): Promise { - if (finished) return null; - const [chunk] = chunks; - if (chunk) { - const chunkRemaining = chunk.data.byteLength - chunk.offset; - const readLength = Math.min(chunkRemaining, buf.byteLength); - for (let i = 0; i < readLength; i++) { - buf[i] = chunk.data[chunk.offset + i]; - } - chunk.offset += readLength; - if (chunk.offset === chunk.data.byteLength) { - chunks.shift(); - // Consume \r\n; - if ((await tp.readLine()) === null) { - throw new Deno.errors.UnexpectedEof(); - } - } - return readLength; - } - const line = await tp.readLine(); - if (line === null) throw new Deno.errors.UnexpectedEof(); - // TODO(bartlomieju): handle chunk extension - const [chunkSizeString] = line.split(";"); - const chunkSize = parseInt(chunkSizeString, 16); - if (Number.isNaN(chunkSize) || chunkSize < 0) { - throw new Deno.errors.InvalidData("Invalid chunk size"); - } - if (chunkSize > 0) { - if (chunkSize > buf.byteLength) { - let eof = await r.readFull(buf); - if (eof === null) { - throw new Deno.errors.UnexpectedEof(); - } - const restChunk = new Uint8Array(chunkSize - buf.byteLength); - eof = await r.readFull(restChunk); - if (eof === null) { - throw new Deno.errors.UnexpectedEof(); - } else { - chunks.push({ - offset: 0, - data: restChunk, - }); - } - return buf.byteLength; - } else { - const bufToFill = buf.subarray(0, chunkSize); - const eof = await r.readFull(bufToFill); - if (eof === null) { - throw new Deno.errors.UnexpectedEof(); - } - // Consume \r\n - if ((await tp.readLine()) === null) { - throw new Deno.errors.UnexpectedEof(); - } - return chunkSize; - } - } else { - assert(chunkSize === 0); - // Consume \r\n - if ((await r.readLine()) === null) { - throw new Deno.errors.UnexpectedEof(); - } - await readTrailers(h, r); - finished = true; - return null; - } - } - return { read }; -} - -function isProhibidedForTrailer(key: string): boolean { - const s = new Set(["transfer-encoding", "content-length", "trailer"]); - return s.has(key.toLowerCase()); -} - -/** Read trailer headers from reader and append values to headers. "trailer" - * field will be deleted. */ -export async function readTrailers( - headers: Headers, - r: BufReader, -): Promise { - const trailers = parseTrailer(headers.get("trailer")); - if (trailers == null) return; - const trailerNames = [...trailers.keys()]; - const tp = new TextProtoReader(r); - const result = await tp.readMIMEHeader(); - if (result == null) { - throw new Deno.errors.InvalidData("Missing trailer header."); - } - const undeclared = [...result.keys()].filter( - (k) => !trailerNames.includes(k), - ); - if (undeclared.length > 0) { - throw new Deno.errors.InvalidData( - `Undeclared trailers: ${Deno.inspect(undeclared)}.`, - ); - } - for (const [k, v] of result) { - headers.append(k, v); - } - const missingTrailers = trailerNames.filter((k) => !result.has(k)); - if (missingTrailers.length > 0) { - throw new Deno.errors.InvalidData( - `Missing trailers: ${Deno.inspect(missingTrailers)}.`, - ); - } - headers.delete("trailer"); -} - -function parseTrailer(field: string | null): Headers | undefined { - if (field == null) { - return undefined; - } - const trailerNames = field.split(",").map((v) => v.trim().toLowerCase()); - if (trailerNames.length === 0) { - throw new Deno.errors.InvalidData("Empty trailer header."); - } - const prohibited = trailerNames.filter((k) => isProhibidedForTrailer(k)); - if (prohibited.length > 0) { - throw new Deno.errors.InvalidData( - `Prohibited trailer names: ${Deno.inspect(prohibited)}.`, - ); - } - return new Headers(trailerNames.map((key) => [key, ""])); -} - -export async function writeChunkedBody( - w: BufWriter, - r: Deno.Reader, -): Promise { - for await (const chunk of Deno.iter(r)) { - if (chunk.byteLength <= 0) continue; - const start = encoder.encode(`${chunk.byteLength.toString(16)}\r\n`); - const end = encoder.encode("\r\n"); - await w.write(start); - await w.write(chunk); - await w.write(end); - await w.flush(); - } - - const endChunk = encoder.encode("0\r\n\r\n"); - await w.write(endChunk); -} - -/** Write trailer headers to writer. It should mostly should be called after - * `writeResponse()`. */ -export async function writeTrailers( - w: Deno.Writer, - headers: Headers, - trailers: Headers, -): Promise { - const trailer = headers.get("trailer"); - if (trailer === null) { - throw new TypeError("Missing trailer header."); - } - const transferEncoding = headers.get("transfer-encoding"); - if (transferEncoding === null || !transferEncoding.match(/^chunked/)) { - throw new TypeError( - `Trailers are only allowed for "transfer-encoding: chunked", got "transfer-encoding: ${transferEncoding}".`, - ); - } - const writer = BufWriter.create(w); - const trailerNames = trailer.split(",").map((s) => s.trim().toLowerCase()); - const prohibitedTrailers = trailerNames.filter((k) => - isProhibidedForTrailer(k) - ); - if (prohibitedTrailers.length > 0) { - throw new TypeError( - `Prohibited trailer names: ${Deno.inspect(prohibitedTrailers)}.`, - ); - } - const undeclared = [...trailers.keys()].filter( - (k) => !trailerNames.includes(k), - ); - if (undeclared.length > 0) { - throw new TypeError(`Undeclared trailers: ${Deno.inspect(undeclared)}.`); - } - for (const [key, value] of trailers) { - await writer.write(encoder.encode(`${key}: ${value}\r\n`)); - } - await writer.write(encoder.encode("\r\n")); - await writer.flush(); -} - -export async function writeResponse( - w: Deno.Writer, - r: Response, -): Promise { - const protoMajor = 1; - const protoMinor = 1; - const statusCode = r.status || 200; - const statusText = STATUS_TEXT.get(statusCode); - const writer = BufWriter.create(w); - if (!statusText) { - throw new Deno.errors.InvalidData("Bad status code"); - } - if (!r.body) { - r.body = new Uint8Array(); - } - if (typeof r.body === "string") { - r.body = encoder.encode(r.body); - } - - let out = `HTTP/${protoMajor}.${protoMinor} ${statusCode} ${statusText}\r\n`; - - const headers = r.headers ?? new Headers(); - - if (r.body && !headers.get("content-length")) { - if (r.body instanceof Uint8Array) { - out += `content-length: ${r.body.byteLength}\r\n`; - } else if (!headers.get("transfer-encoding")) { - out += "transfer-encoding: chunked\r\n"; - } - } - - for (const [key, value] of headers) { - out += `${key}: ${value}\r\n`; - } - - out += `\r\n`; - - const header = encoder.encode(out); - const n = await writer.write(header); - assert(n === header.byteLength); - - if (r.body instanceof Uint8Array) { - const n = await writer.write(r.body); - assert(n === r.body.byteLength); - } else if (headers.has("content-length")) { - const contentLength = headers.get("content-length"); - assert(contentLength != null); - const bodyLength = parseInt(contentLength); - const n = await Deno.copy(r.body, writer); - assert(n === bodyLength); - } else { - await writeChunkedBody(writer, r.body); - } - if (r.trailers) { - const t = await r.trailers(); - await writeTrailers(writer, headers, t); - } - await writer.flush(); -} - -/** - * ParseHTTPVersion parses a HTTP version string. - * "HTTP/1.0" returns (1, 0). - * Ported from https://github.com/golang/go/blob/f5c43b9/src/net/http/request.go#L766-L792 - */ -export function parseHTTPVersion(vers: string): [number, number] { - switch (vers) { - case "HTTP/1.1": - return [1, 1]; - - case "HTTP/1.0": - return [1, 0]; - - default: { - const Big = 1000000; // arbitrary upper bound - - if (!vers.startsWith("HTTP/")) { - break; - } - - const dot = vers.indexOf("."); - if (dot < 0) { - break; - } - - const majorStr = vers.substring(vers.indexOf("/") + 1, dot); - const major = Number(majorStr); - if (!Number.isInteger(major) || major < 0 || major > Big) { - break; - } - - const minorStr = vers.substring(dot + 1); - const minor = Number(minorStr); - if (!Number.isInteger(minor) || minor < 0 || minor > Big) { - break; - } - - return [major, minor]; - } - } - - throw new Error(`malformed HTTP version ${vers}`); -} - -export async function readRequest( - conn: Deno.Conn, - bufr: BufReader, -): Promise { - const tp = new TextProtoReader(bufr); - const firstLine = await tp.readLine(); // e.g. GET /index.html HTTP/1.0 - if (firstLine === null) return null; - const headers = await tp.readMIMEHeader(); - if (headers === null) throw new Deno.errors.UnexpectedEof(); - - const req = new ServerRequest(); - req.conn = conn; - req.r = bufr; - [req.method, req.url, req.proto] = firstLine.split(" ", 3); - [req.protoMajor, req.protoMinor] = parseHTTPVersion(req.proto); - req.headers = headers; - fixLength(req); - return req; -} - -function fixLength(req: ServerRequest): void { - const contentLength = req.headers.get("Content-Length"); - if (contentLength) { - const arrClen = contentLength.split(","); - if (arrClen.length > 1) { - const distinct = [...new Set(arrClen.map((e): string => e.trim()))]; - if (distinct.length > 1) { - throw Error("cannot contain multiple Content-Length headers"); - } else { - req.headers.set("Content-Length", distinct[0]); - } - } - const c = req.headers.get("Content-Length"); - if (req.method === "HEAD" && c && c !== "0") { - throw Error("http: method cannot contain a Content-Length"); - } - if (c && req.headers.has("transfer-encoding")) { - // A sender MUST NOT send a Content-Length header field in any message - // that contains a Transfer-Encoding header field. - // rfc: https://tools.ietf.org/html/rfc7230#section-3.3.2 - throw new Error( - "http: Transfer-Encoding and Content-Length cannot be send together", - ); - } - } -} diff --git a/std/http/_io_test.ts b/std/http/_io_test.ts deleted file mode 100644 index ea3d282b83..0000000000 --- a/std/http/_io_test.ts +++ /dev/null @@ -1,494 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertNotEquals, - assertThrowsAsync, -} from "../testing/asserts.ts"; -import { - bodyReader, - chunkedBodyReader, - parseHTTPVersion, - readRequest, - readTrailers, - writeResponse, - writeTrailers, -} from "./_io.ts"; -import { decode, encode } from "../encoding/utf8.ts"; -import { BufReader, ReadLineResult } from "../io/bufio.ts"; -import { Response, ServerRequest } from "./server.ts"; -import { StringReader } from "../io/readers.ts"; -import { mockConn } from "./_mock_conn.ts"; - -Deno.test("bodyReader", async () => { - const text = "Hello, Deno"; - const r = bodyReader( - text.length, - new BufReader(new Deno.Buffer(encode(text))), - ); - assertEquals(decode(await Deno.readAll(r)), text); -}); -function chunkify(n: number, char: string): string { - const v = Array.from({ length: n }) - .map(() => `${char}`) - .join(""); - return `${n.toString(16)}\r\n${v}\r\n`; -} -Deno.test("chunkedBodyReader", async () => { - const body = [ - chunkify(3, "a"), - chunkify(5, "b"), - chunkify(11, "c"), - chunkify(22, "d"), - chunkify(0, ""), - ].join(""); - const h = new Headers(); - const r = chunkedBodyReader(h, new BufReader(new Deno.Buffer(encode(body)))); - let result: number | null; - // Use small buffer as some chunks exceed buffer size - const buf = new Uint8Array(5); - const dest = new Deno.Buffer(); - while ((result = await r.read(buf)) !== null) { - const len = Math.min(buf.byteLength, result); - await dest.write(buf.subarray(0, len)); - } - const exp = "aaabbbbbcccccccccccdddddddddddddddddddddd"; - assertEquals(new TextDecoder().decode(dest.bytes()), exp); -}); - -Deno.test("chunkedBodyReader with trailers", async () => { - const body = [ - chunkify(3, "a"), - chunkify(5, "b"), - chunkify(11, "c"), - chunkify(22, "d"), - chunkify(0, ""), - "deno: land\r\n", - "node: js\r\n", - "\r\n", - ].join(""); - const h = new Headers({ - trailer: "deno,node", - }); - const r = chunkedBodyReader(h, new BufReader(new Deno.Buffer(encode(body)))); - assertEquals(h.has("trailer"), true); - assertEquals(h.has("deno"), false); - assertEquals(h.has("node"), false); - const act = decode(await Deno.readAll(r)); - const exp = "aaabbbbbcccccccccccdddddddddddddddddddddd"; - assertEquals(act, exp); - assertEquals(h.has("trailer"), false); - assertEquals(h.get("deno"), "land"); - assertEquals(h.get("node"), "js"); -}); - -Deno.test("readTrailers", async () => { - const h = new Headers({ - trailer: "Deno, Node", - }); - const trailer = ["deno: land", "node: js", "", ""].join("\r\n"); - await readTrailers(h, new BufReader(new Deno.Buffer(encode(trailer)))); - assertEquals(h.has("trailer"), false); - assertEquals(h.get("deno"), "land"); - assertEquals(h.get("node"), "js"); -}); - -Deno.test( - "readTrailer should throw if undeclared headers found in trailer", - async () => { - const patterns = [ - ["deno,node", "deno: land\r\nnode: js\r\ngo: lang\r\n\r\n"], - ["deno", "node: js\r\n\r\n"], - ["deno", "node:js\r\ngo: lang\r\n\r\n"], - ]; - for (const [header, trailer] of patterns) { - const h = new Headers({ - trailer: header, - }); - await assertThrowsAsync( - async () => { - await readTrailers( - h, - new BufReader(new Deno.Buffer(encode(trailer))), - ); - }, - Deno.errors.InvalidData, - `Undeclared trailers: [ "`, - ); - } - }, -); - -Deno.test( - "readTrailer should throw if trailer contains prohibited fields", - async () => { - for (const f of ["Content-Length", "Trailer", "Transfer-Encoding"]) { - const h = new Headers({ - trailer: f, - }); - await assertThrowsAsync( - async () => { - await readTrailers(h, new BufReader(new Deno.Buffer())); - }, - Deno.errors.InvalidData, - `Prohibited trailer names: [ "`, - ); - } - }, -); - -Deno.test("writeTrailer", async () => { - const w = new Deno.Buffer(); - await writeTrailers( - w, - new Headers({ "transfer-encoding": "chunked", trailer: "deno,node" }), - new Headers({ deno: "land", node: "js" }), - ); - assertEquals( - new TextDecoder().decode(w.bytes()), - "deno: land\r\nnode: js\r\n\r\n", - ); -}); - -Deno.test("writeTrailer should throw", async () => { - const w = new Deno.Buffer(); - await assertThrowsAsync( - () => { - return writeTrailers(w, new Headers(), new Headers()); - }, - TypeError, - "Missing trailer header.", - ); - await assertThrowsAsync( - () => { - return writeTrailers(w, new Headers({ trailer: "deno" }), new Headers()); - }, - TypeError, - `Trailers are only allowed for "transfer-encoding: chunked", got "transfer-encoding: null".`, - ); - for (const f of ["content-length", "trailer", "transfer-encoding"]) { - await assertThrowsAsync( - () => { - return writeTrailers( - w, - new Headers({ "transfer-encoding": "chunked", trailer: f }), - new Headers({ [f]: "1" }), - ); - }, - TypeError, - `Prohibited trailer names: [ "`, - ); - } - await assertThrowsAsync( - () => { - return writeTrailers( - w, - new Headers({ "transfer-encoding": "chunked", trailer: "deno" }), - new Headers({ node: "js" }), - ); - }, - TypeError, - `Undeclared trailers: [ "node" ].`, - ); -}); - -// Ported from https://github.com/golang/go/blob/f5c43b9/src/net/http/request_test.go#L535-L565 -Deno.test("parseHttpVersion", (): void => { - const testCases = [ - { in: "HTTP/0.9", want: [0, 9] }, - { in: "HTTP/1.0", want: [1, 0] }, - { in: "HTTP/1.1", want: [1, 1] }, - { in: "HTTP/3.14", want: [3, 14] }, - { in: "HTTP", err: true }, - { in: "HTTP/one.one", err: true }, - { in: "HTTP/1.1/", err: true }, - { in: "HTTP/-1.0", err: true }, - { in: "HTTP/0.-1", err: true }, - { in: "HTTP/", err: true }, - { in: "HTTP/1,0", err: true }, - { in: "HTTP/1.1000001", err: true }, - ]; - for (const t of testCases) { - let r, err; - try { - r = parseHTTPVersion(t.in); - } catch (e) { - err = e; - } - if (t.err) { - assert(err instanceof Error, t.in); - } else { - assertEquals(err, undefined); - assertEquals(r, t.want, t.in); - } - } -}); - -Deno.test("writeUint8ArrayResponse", async function (): Promise { - const shortText = "Hello"; - - const body = new TextEncoder().encode(shortText); - const res: Response = { body }; - - const buf = new Deno.Buffer(); - await writeResponse(buf, res); - - const decoder = new TextDecoder("utf-8"); - const reader = new BufReader(buf); - - let r: ReadLineResult | null = await reader.readLine(); - assert(r !== null); - assertEquals(decoder.decode(r.line), "HTTP/1.1 200 OK"); - assertEquals(r.more, false); - - r = await reader.readLine(); - assert(r !== null); - assertEquals(decoder.decode(r.line), `content-length: ${shortText.length}`); - assertEquals(r.more, false); - - r = await reader.readLine(); - assert(r !== null); - assertEquals(r.line.byteLength, 0); - assertEquals(r.more, false); - - r = await reader.readLine(); - assert(r !== null); - assertEquals(decoder.decode(r.line), shortText); - assertEquals(r.more, false); - - const eof = await reader.readLine(); - assertEquals(eof, null); -}); - -Deno.test("writeStringResponse", async function (): Promise { - const body = "Hello"; - - const res: Response = { body }; - - const buf = new Deno.Buffer(); - await writeResponse(buf, res); - - const decoder = new TextDecoder("utf-8"); - const reader = new BufReader(buf); - - let r: ReadLineResult | null = await reader.readLine(); - assert(r !== null); - assertEquals(decoder.decode(r.line), "HTTP/1.1 200 OK"); - assertEquals(r.more, false); - - r = await reader.readLine(); - assert(r !== null); - assertEquals(decoder.decode(r.line), `content-length: ${body.length}`); - assertEquals(r.more, false); - - r = await reader.readLine(); - assert(r !== null); - assertEquals(r.line.byteLength, 0); - assertEquals(r.more, false); - - r = await reader.readLine(); - assert(r !== null); - assertEquals(decoder.decode(r.line), body); - assertEquals(r.more, false); - - const eof = await reader.readLine(); - assertEquals(eof, null); -}); - -Deno.test("writeStringReaderResponse", async function (): Promise { - const shortText = "Hello"; - - const body = new StringReader(shortText); - const res: Response = { body }; - - const buf = new Deno.Buffer(); - await writeResponse(buf, res); - - const decoder = new TextDecoder("utf-8"); - const reader = new BufReader(buf); - - let r: ReadLineResult | null = await reader.readLine(); - assert(r !== null); - assertEquals(decoder.decode(r.line), "HTTP/1.1 200 OK"); - assertEquals(r.more, false); - - r = await reader.readLine(); - assert(r !== null); - assertEquals(decoder.decode(r.line), "transfer-encoding: chunked"); - assertEquals(r.more, false); - - r = await reader.readLine(); - assert(r !== null); - assertEquals(r.line.byteLength, 0); - assertEquals(r.more, false); - - r = await reader.readLine(); - assert(r !== null); - assertEquals(decoder.decode(r.line), shortText.length.toString()); - assertEquals(r.more, false); - - r = await reader.readLine(); - assert(r !== null); - assertEquals(decoder.decode(r.line), shortText); - assertEquals(r.more, false); - - r = await reader.readLine(); - assert(r !== null); - assertEquals(decoder.decode(r.line), "0"); - assertEquals(r.more, false); -}); - -Deno.test("writeResponse with trailer", async () => { - const w = new Deno.Buffer(); - const body = new StringReader("Hello"); - await writeResponse(w, { - status: 200, - headers: new Headers({ - "transfer-encoding": "chunked", - trailer: "deno,node", - }), - body, - trailers: () => new Headers({ deno: "land", node: "js" }), - }); - const ret = new TextDecoder().decode(w.bytes()); - const exp = [ - "HTTP/1.1 200 OK", - "transfer-encoding: chunked", - "trailer: deno,node", - "", - "5", - "Hello", - "0", - "", - "deno: land", - "node: js", - "", - "", - ].join("\r\n"); - assertEquals(ret, exp); -}); - -Deno.test("writeResponseShouldNotModifyOriginHeaders", async () => { - const headers = new Headers(); - const buf = new Deno.Buffer(); - - await writeResponse(buf, { body: "foo", headers }); - assert(decode(await Deno.readAll(buf)).includes("content-length: 3")); - - await writeResponse(buf, { body: "hello", headers }); - assert(decode(await Deno.readAll(buf)).includes("content-length: 5")); -}); - -Deno.test("readRequestError", async function (): Promise { - const input = `GET / HTTP/1.1 -malformedHeader -`; - const reader = new BufReader(new StringReader(input)); - let err; - try { - await readRequest(mockConn(), reader); - } catch (e) { - err = e; - } - assert(err instanceof Error); - assertEquals(err.message, "malformed MIME header line: malformedHeader"); -}); - -// Ported from Go -// https://github.com/golang/go/blob/go1.12.5/src/net/http/request_test.go#L377-L443 -// TODO(zekth) fix tests -Deno.test("testReadRequestError", async function (): Promise { - const testCases = [ - { - in: "GET / HTTP/1.1\r\nheader: foo\r\n\r\n", - headers: [{ key: "header", value: "foo" }], - }, - { - in: "GET / HTTP/1.1\r\nheader:foo\r\n", - err: Deno.errors.UnexpectedEof, - }, - { - in: "POST / HTTP/1.0\r\n\r\n", - headers: [], - version: true, - }, - { in: "", eof: true }, - { - in: "HEAD / HTTP/1.1\r\nContent-Length:4\r\n\r\n", - err: "http: method cannot contain a Content-Length", - }, - { - in: "HEAD / HTTP/1.1\r\n\r\n", - headers: [], - }, - // Multiple Content-Length values should either be - // deduplicated if same or reject otherwise - // See Issue 16490. - { - in: "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 0\r\n\r\n" + - "Gopher hey\r\n", - err: "cannot contain multiple Content-Length headers", - }, - { - in: "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 6\r\n\r\n" + - "Gopher\r\n", - err: "cannot contain multiple Content-Length headers", - }, - { - in: "PUT / HTTP/1.1\r\nContent-Length: 6 \r\nContent-Length: 6\r\n" + - "Content-Length:6\r\n\r\nGopher\r\n", - headers: [{ key: "Content-Length", value: "6" }], - }, - { - in: "PUT / HTTP/1.1\r\nContent-Length: 1\r\nContent-Length: 6 \r\n\r\n", - err: "cannot contain multiple Content-Length headers", - }, - // Setting an empty header is swallowed by textproto - // see: readMIMEHeader() - // { - // in: "POST / HTTP/1.1\r\nContent-Length:\r\nContent-Length: 3\r\n\r\n", - // err: "cannot contain multiple Content-Length headers" - // }, - { - in: "HEAD / HTTP/1.1\r\nContent-Length:0\r\nContent-Length: 0\r\n\r\n", - headers: [{ key: "Content-Length", value: "0" }], - }, - { - in: "POST / HTTP/1.1\r\nContent-Length:0\r\ntransfer-encoding: " + - "chunked\r\n\r\n", - headers: [], - err: "http: Transfer-Encoding and Content-Length cannot be send together", - }, - ]; - for (const test of testCases) { - const reader = new BufReader(new StringReader(test.in)); - let err; - let req: ServerRequest | null = null; - try { - req = await readRequest(mockConn(), reader); - } catch (e) { - err = e; - } - if (test.eof) { - assertEquals(req, null); - } else if (typeof test.err === "string") { - assertEquals(err.message, test.err); - } else if (test.err) { - assert(err instanceof (test.err as typeof Deno.errors.UnexpectedEof)); - } else { - assert(req instanceof ServerRequest); - if (test.version) { - // return value order of parseHTTPVersion() function have to match with [req.protoMajor, req.protoMinor]; - const version = parseHTTPVersion(test.in.split(" ", 3)[2]); - assertEquals(req.protoMajor, version[0]); - assertEquals(req.protoMinor, version[1]); - } - assert(test.headers); - assertEquals(err, undefined); - assertNotEquals(req, null); - for (const h of test.headers) { - assertEquals(req.headers.get(h.key), h.value); - } - } - } -}); diff --git a/std/http/_mock_conn.ts b/std/http/_mock_conn.ts deleted file mode 100644 index 8f3396dbdc..0000000000 --- a/std/http/_mock_conn.ts +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -/** Create dummy Deno.Conn object with given base properties */ -export function mockConn(base: Partial = {}): Deno.Conn { - return { - localAddr: { - transport: "tcp", - hostname: "", - port: 0, - }, - remoteAddr: { - transport: "tcp", - hostname: "", - port: 0, - }, - rid: -1, - closeWrite: (): Promise => { - return Promise.resolve(); - }, - read: (): Promise => { - return Promise.resolve(0); - }, - write: (): Promise => { - return Promise.resolve(-1); - }, - close: (): void => {}, - ...base, - }; -} diff --git a/std/http/bench.ts b/std/http/bench.ts deleted file mode 100644 index 5ba95ef0a4..0000000000 --- a/std/http/bench.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { serve } from "./server.ts"; - -const addr = Deno.args[0] || "127.0.0.1:4500"; -const server = serve(addr); -const body = new TextEncoder().encode("Hello World"); - -console.log(`http://${addr}/`); -for await (const req of server) { - const res = { - body, - headers: new Headers(), - }; - res.headers.set("Date", new Date().toUTCString()); - res.headers.set("Connection", "keep-alive"); - req.respond(res).catch(() => {}); -} diff --git a/std/http/cookie.ts b/std/http/cookie.ts deleted file mode 100644 index 486afd6c64..0000000000 --- a/std/http/cookie.ts +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// Structured similarly to Go's cookie.go -// https://github.com/golang/go/blob/master/src/net/http/cookie.go -import { assert } from "../_util/assert.ts"; -import { toIMF } from "../datetime/mod.ts"; - -export type Cookies = Record; - -export interface Cookie { - /** Name of the cookie. */ - name: string; - /** Value of the cookie. */ - value: string; - /** Expiration date of the cookie. */ - expires?: Date; - /** Max-Age of the Cookie. Must be integer superior to 0. */ - maxAge?: number; - /** Specifies those hosts to which the cookie will be sent. */ - domain?: string; - /** Indicates a URL path that must exist in the request. */ - path?: string; - /** Indicates if the cookie is made using SSL & HTTPS. */ - secure?: boolean; - /** Indicates that cookie is not accessible via JavaScript. **/ - httpOnly?: boolean; - /** Allows servers to assert that a cookie ought not to - * be sent along with cross-site requests. */ - sameSite?: SameSite; - /** Additional key value pairs with the form "key=value" */ - unparsed?: string[]; -} - -export type SameSite = "Strict" | "Lax" | "None"; - -const FIELD_CONTENT_REGEXP = /^(?=[\x20-\x7E]*$)[^()@<>,;:\\"\[\]?={}\s]+$/; - -function toString(cookie: Cookie): string { - if (!cookie.name) { - return ""; - } - const out: string[] = []; - validateCookieName(cookie.name); - validateCookieValue(cookie.name, cookie.value); - out.push(`${cookie.name}=${cookie.value}`); - - // Fallback for invalid Set-Cookie - // ref: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.1 - if (cookie.name.startsWith("__Secure")) { - cookie.secure = true; - } - if (cookie.name.startsWith("__Host")) { - cookie.path = "/"; - cookie.secure = true; - delete cookie.domain; - } - - if (cookie.secure) { - out.push("Secure"); - } - if (cookie.httpOnly) { - out.push("HttpOnly"); - } - if (typeof cookie.maxAge === "number" && Number.isInteger(cookie.maxAge)) { - assert(cookie.maxAge > 0, "Max-Age must be an integer superior to 0"); - out.push(`Max-Age=${cookie.maxAge}`); - } - if (cookie.domain) { - out.push(`Domain=${cookie.domain}`); - } - if (cookie.sameSite) { - out.push(`SameSite=${cookie.sameSite}`); - } - if (cookie.path) { - validatePath(cookie.path); - out.push(`Path=${cookie.path}`); - } - if (cookie.expires) { - const dateString = toIMF(cookie.expires); - out.push(`Expires=${dateString}`); - } - if (cookie.unparsed) { - out.push(cookie.unparsed.join("; ")); - } - return out.join("; "); -} - -/** - * Validate Cookie Name. - * @param name Cookie name. - */ -function validateCookieName(name: string | undefined | null): void { - if (name && !FIELD_CONTENT_REGEXP.test(name)) { - throw new TypeError(`Invalid cookie name: "${name}".`); - } -} - -/** - * Validate Path Value. - * @see https://tools.ietf.org/html/rfc6265#section-4.1.2.4 - * @param path Path value. - */ -function validatePath(path: string | null): void { - if (path == null) { - return; - } - for (let i = 0; i < path.length; i++) { - const c = path.charAt(i); - if ( - c < String.fromCharCode(0x20) || c > String.fromCharCode(0x7E) || c == ";" - ) { - throw new Error( - path + ": Invalid cookie path char '" + c + "'", - ); - } - } -} - -/** - *Validate Cookie Value. - * @see https://tools.ietf.org/html/rfc6265#section-4.1 - * @param value Cookie value. - */ -function validateCookieValue(name: string, value: string | null): void { - if (value == null || name == null) return; - for (let i = 0; i < value.length; i++) { - const c = value.charAt(i); - if ( - c < String.fromCharCode(0x21) || c == String.fromCharCode(0x22) || - c == String.fromCharCode(0x2c) || c == String.fromCharCode(0x3b) || - c == String.fromCharCode(0x5c) || c == String.fromCharCode(0x7f) - ) { - throw new Error( - "RFC2616 cookie '" + name + "' cannot have '" + c + "' as value", - ); - } - if (c > String.fromCharCode(0x80)) { - throw new Error( - "RFC2616 cookie '" + name + "' can only have US-ASCII chars as value" + - c.charCodeAt(0).toString(16), - ); - } - } -} - -/** - * Parse the cookies of the Server Request - * @param req An object which has a `headers` property - */ -export function getCookies(req: { headers: Headers }): Cookies { - const cookie = req.headers.get("Cookie"); - if (cookie != null) { - const out: Cookies = {}; - const c = cookie.split(";"); - for (const kv of c) { - const [cookieKey, ...cookieVal] = kv.split("="); - assert(cookieKey != null); - const key = cookieKey.trim(); - out[key] = cookieVal.join("="); - } - return out; - } - return {}; -} - -/** - * Set the cookie header properly in the Response - * @param res An object which has a headers property - * @param cookie Cookie to set - * - * Example: - * - * ```ts - * setCookie(response, { name: 'deno', value: 'runtime', - * httpOnly: true, secure: true, maxAge: 2, domain: "deno.land" }); - * ``` - */ -export function setCookie(res: { headers?: Headers }, cookie: Cookie): void { - if (!res.headers) { - res.headers = new Headers(); - } - // TODO(zekth) : Add proper parsing of Set-Cookie headers - // Parsing cookie headers to make consistent set-cookie header - // ref: https://tools.ietf.org/html/rfc6265#section-4.1.1 - const v = toString(cookie); - if (v) { - res.headers.append("Set-Cookie", v); - } -} - -/** - * Set the cookie header properly in the Response to delete it - * @param res Server Response - * @param name Name of the cookie to Delete - * Example: - * - * deleteCookie(res,'foo'); - */ -export function deleteCookie(res: { headers?: Headers }, name: string): void { - setCookie(res, { - name: name, - value: "", - expires: new Date(0), - }); -} diff --git a/std/http/cookie_test.ts b/std/http/cookie_test.ts deleted file mode 100644 index 1973eed01e..0000000000 --- a/std/http/cookie_test.ts +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { Response, ServerRequest } from "./server.ts"; -import { deleteCookie, getCookies, setCookie } from "./cookie.ts"; -import { assert, assertEquals, assertThrows } from "../testing/asserts.ts"; - -Deno.test({ - name: "Cookie parser", - fn(): void { - const req = new ServerRequest(); - req.headers = new Headers(); - assertEquals(getCookies(req), {}); - req.headers = new Headers(); - req.headers.set("Cookie", "foo=bar"); - assertEquals(getCookies(req), { foo: "bar" }); - - req.headers = new Headers(); - req.headers.set("Cookie", "full=of ; tasty=chocolate"); - assertEquals(getCookies(req), { full: "of ", tasty: "chocolate" }); - - req.headers = new Headers(); - req.headers.set("Cookie", "igot=99; problems=but..."); - assertEquals(getCookies(req), { igot: "99", problems: "but..." }); - - req.headers = new Headers(); - req.headers.set("Cookie", "PREF=al=en-GB&f1=123; wide=1; SID=123"); - assertEquals(getCookies(req), { - PREF: "al=en-GB&f1=123", - wide: "1", - SID: "123", - }); - }, -}); - -Deno.test({ - name: "Cookie Name Validation", - fn(): void { - const res: Response = {}; - const tokens = [ - '"id"', - "id\t", - "i\td", - "i d", - "i;d", - "{id}", - "[id]", - '"', - "id\u0091", - ]; - res.headers = new Headers(); - tokens.forEach((name) => { - assertThrows( - (): void => { - setCookie(res, { - name, - value: "Cat", - httpOnly: true, - secure: true, - maxAge: 3, - }); - }, - Error, - 'Invalid cookie name: "' + name + '".', - ); - }); - }, -}); - -Deno.test({ - name: "Cookie Value Validation", - fn(): void { - const res: Response = {}; - const tokens = [ - "1f\tWa", - "\t", - "1f Wa", - "1f;Wa", - '"1fWa', - "1f\\Wa", - '1f"Wa', - '"', - "1fWa\u0005", - "1f\u0091Wa", - ]; - res.headers = new Headers(); - tokens.forEach((value) => { - assertThrows( - (): void => { - setCookie( - res, - { - name: "Space", - value, - httpOnly: true, - secure: true, - maxAge: 3, - }, - ); - }, - Error, - "RFC2616 cookie 'Space'", - ); - }); - }, -}); - -Deno.test({ - name: "Cookie Path Validation", - fn(): void { - const res: Response = {}; - const path = "/;domain=sub.domain.com"; - res.headers = new Headers(); - assertThrows( - (): void => { - setCookie(res, { - name: "Space", - value: "Cat", - httpOnly: true, - secure: true, - path, - maxAge: 3, - }); - }, - Error, - path + ": Invalid cookie path char ';'", - ); - }, -}); - -Deno.test({ - name: "Cookie Delete", - fn(): void { - const res: Response = {}; - deleteCookie(res, "deno"); - assertEquals( - res.headers?.get("Set-Cookie"), - "deno=; Expires=Thu, 01 Jan 1970 00:00:00 GMT", - ); - }, -}); - -Deno.test({ - name: "Cookie Set", - fn(): void { - const res: Response = {}; - - res.headers = new Headers(); - setCookie(res, { name: "Space", value: "Cat" }); - assertEquals(res.headers.get("Set-Cookie"), "Space=Cat"); - - res.headers = new Headers(); - setCookie(res, { name: "Space", value: "Cat", secure: true }); - assertEquals(res.headers.get("Set-Cookie"), "Space=Cat; Secure"); - - res.headers = new Headers(); - setCookie(res, { name: "Space", value: "Cat", httpOnly: true }); - assertEquals(res.headers.get("Set-Cookie"), "Space=Cat; HttpOnly"); - - res.headers = new Headers(); - setCookie(res, { - name: "Space", - value: "Cat", - httpOnly: true, - secure: true, - }); - assertEquals(res.headers.get("Set-Cookie"), "Space=Cat; Secure; HttpOnly"); - - res.headers = new Headers(); - setCookie(res, { - name: "Space", - value: "Cat", - httpOnly: true, - secure: true, - maxAge: 2, - }); - assertEquals( - res.headers.get("Set-Cookie"), - "Space=Cat; Secure; HttpOnly; Max-Age=2", - ); - - let error = false; - res.headers = new Headers(); - try { - setCookie(res, { - name: "Space", - value: "Cat", - httpOnly: true, - secure: true, - maxAge: 0, - }); - } catch (e) { - error = true; - } - assert(error); - - res.headers = new Headers(); - setCookie(res, { - name: "Space", - value: "Cat", - httpOnly: true, - secure: true, - maxAge: 2, - domain: "deno.land", - }); - assertEquals( - res.headers.get("Set-Cookie"), - "Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land", - ); - - res.headers = new Headers(); - setCookie(res, { - name: "Space", - value: "Cat", - httpOnly: true, - secure: true, - maxAge: 2, - domain: "deno.land", - sameSite: "Strict", - }); - assertEquals( - res.headers.get("Set-Cookie"), - "Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; " + - "SameSite=Strict", - ); - - res.headers = new Headers(); - setCookie(res, { - name: "Space", - value: "Cat", - httpOnly: true, - secure: true, - maxAge: 2, - domain: "deno.land", - sameSite: "Lax", - }); - assertEquals( - res.headers.get("Set-Cookie"), - "Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; SameSite=Lax", - ); - - res.headers = new Headers(); - setCookie(res, { - name: "Space", - value: "Cat", - httpOnly: true, - secure: true, - maxAge: 2, - domain: "deno.land", - path: "/", - }); - assertEquals( - res.headers.get("Set-Cookie"), - "Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; Path=/", - ); - - res.headers = new Headers(); - setCookie(res, { - name: "Space", - value: "Cat", - httpOnly: true, - secure: true, - maxAge: 2, - domain: "deno.land", - path: "/", - unparsed: ["unparsed=keyvalue", "batman=Bruce"], - }); - assertEquals( - res.headers.get("Set-Cookie"), - "Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; Path=/; " + - "unparsed=keyvalue; batman=Bruce", - ); - - res.headers = new Headers(); - setCookie(res, { - name: "Space", - value: "Cat", - httpOnly: true, - secure: true, - maxAge: 2, - domain: "deno.land", - path: "/", - expires: new Date(Date.UTC(1983, 0, 7, 15, 32)), - }); - assertEquals( - res.headers.get("Set-Cookie"), - "Space=Cat; Secure; HttpOnly; Max-Age=2; Domain=deno.land; Path=/; " + - "Expires=Fri, 07 Jan 1983 15:32:00 GMT", - ); - - res.headers = new Headers(); - setCookie(res, { name: "__Secure-Kitty", value: "Meow" }); - assertEquals(res.headers.get("Set-Cookie"), "__Secure-Kitty=Meow; Secure"); - - res.headers = new Headers(); - setCookie(res, { - name: "__Host-Kitty", - value: "Meow", - domain: "deno.land", - }); - assertEquals( - res.headers.get("Set-Cookie"), - "__Host-Kitty=Meow; Secure; Path=/", - ); - - res.headers = new Headers(); - setCookie(res, { name: "cookie-1", value: "value-1", secure: true }); - setCookie(res, { name: "cookie-2", value: "value-2", maxAge: 3600 }); - assertEquals( - res.headers.get("Set-Cookie"), - "cookie-1=value-1; Secure, cookie-2=value-2; Max-Age=3600", - ); - - res.headers = new Headers(); - setCookie(res, { name: "", value: "" }); - assertEquals(res.headers.get("Set-Cookie"), null); - }, -}); diff --git a/std/http/file_server.ts b/std/http/file_server.ts deleted file mode 100644 index 8fd2e74842..0000000000 --- a/std/http/file_server.ts +++ /dev/null @@ -1,454 +0,0 @@ -#!/usr/bin/env -S deno run --allow-net --allow-read -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -// This program serves files in the current directory over HTTP. -// TODO(bartlomieju): Stream responses instead of reading them into memory. -// TODO(bartlomieju): Add tests like these: -// https://github.com/indexzero/http-server/blob/master/test/http-server-test.js - -import { extname, posix } from "../path/mod.ts"; -import { - HTTPSOptions, - listenAndServe, - listenAndServeTLS, - Response, - ServerRequest, -} from "./server.ts"; -import { parse } from "../flags/mod.ts"; -import { assert } from "../_util/assert.ts"; - -interface EntryInfo { - mode: string; - size: string; - url: string; - name: string; -} - -export interface FileServerArgs { - _: string[]; - // -p --port - p?: number; - port?: number; - // --cors - cors?: boolean; - // --no-dir-listing - "dir-listing"?: boolean; - // --host - host?: string; - // -c --cert - c?: string; - cert?: string; - // -k --key - k?: string; - key?: string; - // -h --help - h?: boolean; - help?: boolean; -} - -const encoder = new TextEncoder(); - -const serverArgs = parse(Deno.args) as FileServerArgs; -const target = posix.resolve(serverArgs._[0] ?? ""); - -const MEDIA_TYPES: Record = { - ".md": "text/markdown", - ".html": "text/html", - ".htm": "text/html", - ".json": "application/json", - ".map": "application/json", - ".txt": "text/plain", - ".ts": "text/typescript", - ".tsx": "text/tsx", - ".js": "application/javascript", - ".jsx": "text/jsx", - ".gz": "application/gzip", - ".css": "text/css", - ".wasm": "application/wasm", - ".mjs": "application/javascript", -}; - -/** Returns the content-type based on the extension of a path. */ -function contentType(path: string): string | undefined { - return MEDIA_TYPES[extname(path)]; -} - -function modeToString(isDir: boolean, maybeMode: number | null): string { - const modeMap = ["---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"]; - - if (maybeMode === null) { - return "(unknown mode)"; - } - const mode = maybeMode.toString(8); - if (mode.length < 3) { - return "(unknown mode)"; - } - let output = ""; - mode - .split("") - .reverse() - .slice(0, 3) - .forEach((v): void => { - output = modeMap[+v] + output; - }); - output = `(${isDir ? "d" : "-"}${output})`; - return output; -} - -function fileLenToString(len: number): string { - const multiplier = 1024; - let base = 1; - const suffix = ["B", "K", "M", "G", "T"]; - let suffixIndex = 0; - - while (base * multiplier < len) { - if (suffixIndex >= suffix.length - 1) { - break; - } - base *= multiplier; - suffixIndex++; - } - - return `${(len / base).toFixed(2)}${suffix[suffixIndex]}`; -} - -/** - * Returns an HTTP Response with the requested file as the body - * @param req The server request context used to cleanup the file handle - * @param filePath Path of the file to serve - */ -export async function serveFile( - req: ServerRequest, - filePath: string, -): Promise { - const [file, fileInfo] = await Promise.all([ - Deno.open(filePath), - Deno.stat(filePath), - ]); - const headers = new Headers(); - headers.set("content-length", fileInfo.size.toString()); - const contentTypeValue = contentType(filePath); - if (contentTypeValue) { - headers.set("content-type", contentTypeValue); - } - req.done.then(() => { - file.close(); - }); - return { - status: 200, - body: file, - headers, - }; -} - -// TODO(bartlomieju): simplify this after deno.stat and deno.readDir are fixed -async function serveDir( - req: ServerRequest, - dirPath: string, -): Promise { - const dirUrl = `/${posix.relative(target, dirPath)}`; - const listEntry: EntryInfo[] = []; - for await (const entry of Deno.readDir(dirPath)) { - const filePath = posix.join(dirPath, entry.name); - const fileUrl = posix.join(dirUrl, entry.name); - if (entry.name === "index.html" && entry.isFile) { - // in case index.html as dir... - return serveFile(req, filePath); - } - // Yuck! - let fileInfo = null; - try { - fileInfo = await Deno.stat(filePath); - } catch (e) { - // Pass - } - listEntry.push({ - mode: modeToString(entry.isDirectory, fileInfo?.mode ?? null), - size: entry.isFile ? fileLenToString(fileInfo?.size ?? 0) : "", - name: entry.name, - url: fileUrl, - }); - } - listEntry.sort((a, b) => - a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1 - ); - const formattedDirUrl = `${dirUrl.replace(/\/$/, "")}/`; - const page = encoder.encode(dirViewerTemplate(formattedDirUrl, listEntry)); - - const headers = new Headers(); - headers.set("content-type", "text/html"); - - const res = { - status: 200, - body: page, - headers, - }; - return res; -} - -function serveFallback(req: ServerRequest, e: Error): Promise { - if (e instanceof URIError) { - return Promise.resolve({ - status: 400, - body: encoder.encode("Bad Request"), - }); - } else if (e instanceof Deno.errors.NotFound) { - return Promise.resolve({ - status: 404, - body: encoder.encode("Not Found"), - }); - } else { - return Promise.resolve({ - status: 500, - body: encoder.encode("Internal server error"), - }); - } -} - -function serverLog(req: ServerRequest, res: Response): void { - const d = new Date().toISOString(); - const dateFmt = `[${d.slice(0, 10)} ${d.slice(11, 19)}]`; - const s = `${dateFmt} "${req.method} ${req.url} ${req.proto}" ${res.status}`; - console.log(s); -} - -function setCORS(res: Response): void { - if (!res.headers) { - res.headers = new Headers(); - } - res.headers.append("access-control-allow-origin", "*"); - res.headers.append( - "access-control-allow-headers", - "Origin, X-Requested-With, Content-Type, Accept, Range", - ); -} - -function dirViewerTemplate(dirname: string, entries: EntryInfo[]): string { - return html` - - - - - - - Deno File Server - - - -
    -

    Index of ${dirname}

    - - - - - - - ${ - entries.map( - (entry) => - html` - - - - - - `, - ) - } -
    ModeSizeName
    - ${entry.mode} - - ${entry.size} - - ${entry.name} -
    -
    - - - `; -} - -function html(strings: TemplateStringsArray, ...values: unknown[]): string { - const l = strings.length - 1; - let html = ""; - - for (let i = 0; i < l; i++) { - let v = values[i]; - if (v instanceof Array) { - v = v.join(""); - } - const s = strings[i] + v; - html += s; - } - html += strings[l]; - return html; -} - -function normalizeURL(url: string): string { - let normalizedUrl = url; - try { - normalizedUrl = decodeURI(normalizedUrl); - } catch (e) { - if (!(e instanceof URIError)) { - throw e; - } - } - - try { - //allowed per https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html - const absoluteURI = new URL(normalizedUrl); - normalizedUrl = absoluteURI.pathname; - } catch (e) { //wasn't an absoluteURI - if (!(e instanceof TypeError)) { - throw e; - } - } - - if (normalizedUrl[0] !== "/") { - throw new URIError("The request URI is malformed."); - } - - normalizedUrl = posix.normalize(normalizedUrl); - const startOfParams = normalizedUrl.indexOf("?"); - return startOfParams > -1 - ? normalizedUrl.slice(0, startOfParams) - : normalizedUrl; -} - -function main(): void { - const CORSEnabled = serverArgs.cors ? true : false; - const port = serverArgs.port ?? serverArgs.p ?? 4507; - const host = serverArgs.host ?? "0.0.0.0"; - const addr = `${host}:${port}`; - const tlsOpts = {} as HTTPSOptions; - tlsOpts.certFile = serverArgs.cert ?? serverArgs.c ?? ""; - tlsOpts.keyFile = serverArgs.key ?? serverArgs.k ?? ""; - const dirListingEnabled = serverArgs["dir-listing"] ?? true; - - if (tlsOpts.keyFile || tlsOpts.certFile) { - if (tlsOpts.keyFile === "" || tlsOpts.certFile === "") { - console.log("--key and --cert are required for TLS"); - serverArgs.h = true; - } - } - - if (serverArgs.h ?? serverArgs.help) { - console.log(`Deno File Server - Serves a local directory in HTTP. - - INSTALL: - deno install --allow-net --allow-read https://deno.land/std/http/file_server.ts - - USAGE: - file_server [path] [options] - - OPTIONS: - -h, --help Prints help information - -p, --port Set port - --cors Enable CORS via the "Access-Control-Allow-Origin" header - --host Hostname (default is 0.0.0.0) - -c, --cert TLS certificate file (enables TLS) - -k, --key TLS key file (enables TLS) - --no-dir-listing Disable directory listing - - All TLS options are required when one is provided.`); - Deno.exit(); - } - - const handler = async (req: ServerRequest): Promise => { - let response: Response | undefined; - try { - const normalizedUrl = normalizeURL(req.url); - let fsPath = posix.join(target, normalizedUrl); - if (fsPath.indexOf(target) !== 0) { - fsPath = target; - } - const fileInfo = await Deno.stat(fsPath); - if (fileInfo.isDirectory) { - if (dirListingEnabled) { - response = await serveDir(req, fsPath); - } else { - throw new Deno.errors.NotFound(); - } - } else { - response = await serveFile(req, fsPath); - } - } catch (e) { - console.error(e.message); - response = await serveFallback(req, e); - } finally { - if (CORSEnabled) { - assert(response); - setCORS(response); - } - serverLog(req, response!); - try { - await req.respond(response!); - } catch (e) { - console.error(e.message); - } - } - }; - - let proto = "http"; - if (tlsOpts.keyFile || tlsOpts.certFile) { - proto += "s"; - tlsOpts.hostname = host; - tlsOpts.port = port; - listenAndServeTLS(tlsOpts, handler); - } else { - listenAndServe(addr, handler); - } - console.log(`${proto.toUpperCase()} server listening on ${proto}://${addr}/`); -} - -if (import.meta.main) { - main(); -} diff --git a/std/http/file_server_test.ts b/std/http/file_server_test.ts deleted file mode 100644 index 638121b45a..0000000000 --- a/std/http/file_server_test.ts +++ /dev/null @@ -1,465 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertNotEquals, - assertStringIncludes, -} from "../testing/asserts.ts"; -import { BufReader } from "../io/bufio.ts"; -import { TextProtoReader } from "../textproto/mod.ts"; -import { Response, ServerRequest } from "./server.ts"; -import { FileServerArgs, serveFile } from "./file_server.ts"; -import { dirname, fromFileUrl, join, resolve } from "../path/mod.ts"; -let fileServer: Deno.Process; - -type FileServerCfg = Omit & { target?: string }; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); -const testdataDir = resolve(moduleDir, "testdata"); - -async function startFileServer({ - target = ".", - port = 4507, - "dir-listing": dirListing = true, -}: FileServerCfg = {}): Promise { - fileServer = Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - "--allow-read", - "--allow-net", - "file_server.ts", - target, - "--cors", - "-p", - `${port}`, - `${dirListing ? "" : "--no-dir-listing"}`, - ], - cwd: moduleDir, - stdout: "piped", - stderr: "null", - }); - // Once fileServer is ready it will write to its stdout. - assert(fileServer.stdout != null); - const r = new TextProtoReader(new BufReader(fileServer.stdout)); - const s = await r.readLine(); - assert(s !== null && s.includes("server listening")); -} - -async function startFileServerAsLibrary({}: FileServerCfg = {}): Promise { - fileServer = await Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - "--allow-read", - "--allow-net", - "testdata/file_server_as_library.ts", - ], - cwd: moduleDir, - stdout: "piped", - stderr: "null", - }); - assert(fileServer.stdout != null); - const r = new TextProtoReader(new BufReader(fileServer.stdout)); - const s = await r.readLine(); - assert(s !== null && s.includes("Server running...")); -} - -async function killFileServer(): Promise { - fileServer.close(); - // Process.close() kills the file server process. However this termination - // happens asynchronously, and since we've just closed the process resource, - // we can't use `await fileServer.status()` to wait for the process to have - // exited. As a workaround, wait for its stdout to close instead. - // TODO(piscisaureus): when `Process.kill()` is stable and works on Windows, - // switch to calling `kill()` followed by `await fileServer.status()`. - await Deno.readAll(fileServer.stdout!); - fileServer.stdout!.close(); -} - -interface StringResponse extends Response { - body: string; -} - -/* HTTP GET request allowing arbitrary paths */ -async function fetchExactPath( - hostname: string, - port: number, - path: string, -): Promise { - const encoder = new TextEncoder(); - const decoder = new TextDecoder(); - const request = encoder.encode("GET " + path + " HTTP/1.1\r\n\r\n"); - let conn: void | Deno.Conn; - try { - conn = await Deno.connect( - { hostname: hostname, port: port, transport: "tcp" }, - ); - await Deno.writeAll(conn, request); - let currentResult = ""; - let contentLength = -1; - let startOfBody = -1; - for await (const chunk of Deno.iter(conn)) { - currentResult += decoder.decode(chunk); - if (contentLength === -1) { - const match = /^content-length: (.*)$/m.exec(currentResult); - if (match && match[1]) { - contentLength = Number(match[1]); - } - } - if (startOfBody === -1) { - const ind = currentResult.indexOf("\r\n\r\n"); - if (ind !== -1) { - startOfBody = ind + 4; - } - } - if (startOfBody !== -1 && contentLength !== -1) { - const byteLen = encoder.encode(currentResult).length; - if (byteLen >= contentLength + startOfBody) { - break; - } - } - } - const status = /^HTTP\/1.1 (...)/.exec(currentResult); - let statusCode = 0; - if (status && status[1]) { - statusCode = Number(status[1]); - } - - const body = currentResult.slice(startOfBody); - const headersStr = currentResult.slice(0, startOfBody); - const headersReg = /^(.*): (.*)$/mg; - const headersObj: { [i: string]: string } = {}; - let match = headersReg.exec(headersStr); - while (match !== null) { - if (match[1] && match[2]) { - headersObj[match[1]] = match[2]; - } - match = headersReg.exec(headersStr); - } - return { - status: statusCode, - headers: new Headers(headersObj), - body: body, - }; - } finally { - if (conn) { - Deno.close(conn.rid); - } - } -} - -Deno.test( - "file_server serveFile", - async (): Promise => { - await startFileServer(); - try { - const res = await fetch("http://localhost:4507/README.md"); - assert(res.headers.has("access-control-allow-origin")); - assert(res.headers.has("access-control-allow-headers")); - assertEquals(res.headers.get("content-type"), "text/markdown"); - const downloadedFile = await res.text(); - const localFile = new TextDecoder().decode( - await Deno.readFile(join(moduleDir, "README.md")), - ); - assertEquals(downloadedFile, localFile); - } finally { - await killFileServer(); - } - }, -); - -Deno.test( - "file_server serveFile in testdata", - async (): Promise => { - await startFileServer({ target: "./testdata" }); - try { - const res = await fetch("http://localhost:4507/hello.html"); - assert(res.headers.has("access-control-allow-origin")); - assert(res.headers.has("access-control-allow-headers")); - assertEquals(res.headers.get("content-type"), "text/html"); - const downloadedFile = await res.text(); - const localFile = new TextDecoder().decode( - await Deno.readFile(join(testdataDir, "hello.html")), - ); - assertEquals(downloadedFile, localFile); - } finally { - await killFileServer(); - } - }, -); - -Deno.test("serveDirectory", async function (): Promise { - await startFileServer(); - try { - const res = await fetch("http://localhost:4507/"); - assert(res.headers.has("access-control-allow-origin")); - assert(res.headers.has("access-control-allow-headers")); - const page = await res.text(); - assert(page.includes("README.md")); - - // `Deno.FileInfo` is not completely compatible with Windows yet - // TODO(bartlomieju): `mode` should work correctly in the future. - // Correct this test case accordingly. - Deno.build.os !== "windows" && - assert(/(\s)*\([a-zA-Z-]{10}\)(\s)*<\/td>/.test(page)); - Deno.build.os === "windows" && - assert(/(\s)*\(unknown mode\)(\s)*<\/td>/.test(page)); - assert(page.includes(`README.md`)); - } finally { - await killFileServer(); - } -}); - -Deno.test("serveFallback", async function (): Promise { - await startFileServer(); - try { - const res = await fetch("http://localhost:4507/badfile.txt"); - assert(res.headers.has("access-control-allow-origin")); - assert(res.headers.has("access-control-allow-headers")); - assertEquals(res.status, 404); - const _ = await res.text(); - } finally { - await killFileServer(); - } -}); - -Deno.test("checkPathTraversal", async function (): Promise { - await startFileServer(); - try { - const res = await fetch( - "http://localhost:4507/../../../../../../../..", - ); - assert(res.headers.has("access-control-allow-origin")); - assert(res.headers.has("access-control-allow-headers")); - assertEquals(res.status, 200); - const listing = await res.text(); - assertStringIncludes(listing, "README.md"); - } finally { - await killFileServer(); - } -}); - -Deno.test("checkPathTraversalNoLeadingSlash", async function (): Promise { - await startFileServer(); - try { - const res = await fetchExactPath("127.0.0.1", 4507, "../../../.."); - assertEquals(res.status, 400); - } finally { - await killFileServer(); - } -}); - -Deno.test("checkPathTraversalAbsoluteURI", async function (): Promise { - await startFileServer(); - try { - //allowed per https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html - const res = await fetchExactPath( - "127.0.0.1", - 4507, - "http://localhost/../../../..", - ); - assertEquals(res.status, 200); - assertStringIncludes(res.body, "README.md"); - } finally { - await killFileServer(); - } -}); - -Deno.test("checkURIEncodedPathTraversal", async function (): Promise { - await startFileServer(); - try { - const res = await fetch( - "http://localhost:4507/%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..", - ); - assert(res.headers.has("access-control-allow-origin")); - assert(res.headers.has("access-control-allow-headers")); - assertEquals(res.status, 404); - const _ = await res.text(); - } finally { - await killFileServer(); - } -}); - -Deno.test("serveWithUnorthodoxFilename", async function (): Promise { - await startFileServer(); - try { - let res = await fetch("http://localhost:4507/testdata/%"); - assert(res.headers.has("access-control-allow-origin")); - assert(res.headers.has("access-control-allow-headers")); - assertEquals(res.status, 200); - let _ = await res.text(); - res = await fetch("http://localhost:4507/testdata/test%20file.txt"); - assert(res.headers.has("access-control-allow-origin")); - assert(res.headers.has("access-control-allow-headers")); - assertEquals(res.status, 200); - _ = await res.text(); - } finally { - await killFileServer(); - } -}); - -Deno.test("printHelp", async function (): Promise { - const helpProcess = Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - // TODO(ry) It ought to be possible to get the help output without - // --allow-read. - "--allow-read", - "file_server.ts", - "--help", - ], - cwd: moduleDir, - stdout: "piped", - }); - assert(helpProcess.stdout != null); - const r = new TextProtoReader(new BufReader(helpProcess.stdout)); - const s = await r.readLine(); - assert(s !== null && s.includes("Deno File Server")); - helpProcess.close(); - helpProcess.stdout.close(); -}); - -Deno.test("contentType", async () => { - const request = new ServerRequest(); - const response = await serveFile(request, join(testdataDir, "hello.html")); - const contentType = response.headers!.get("content-type"); - assertEquals(contentType, "text/html"); - (response.body as Deno.File).close(); -}); - -Deno.test("file_server running as library", async function (): Promise { - await startFileServerAsLibrary(); - try { - const res = await fetch("http://localhost:8000"); - assertEquals(res.status, 200); - const _ = await res.text(); - } finally { - await killFileServer(); - } -}); - -Deno.test("file_server should ignore query params", async () => { - await startFileServer(); - try { - const res = await fetch("http://localhost:4507/README.md?key=value"); - assertEquals(res.status, 200); - const downloadedFile = await res.text(); - const localFile = new TextDecoder().decode( - await Deno.readFile(join(moduleDir, "README.md")), - ); - assertEquals(downloadedFile, localFile); - } finally { - await killFileServer(); - } -}); - -async function startTlsFileServer({ - target = ".", - port = 4577, -}: FileServerCfg = {}): Promise { - fileServer = Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - "--allow-read", - "--allow-net", - "file_server.ts", - target, - "--host", - "localhost", - "--cert", - "./testdata/tls/localhost.crt", - "--key", - "./testdata/tls/localhost.key", - "--cors", - "-p", - `${port}`, - ], - cwd: moduleDir, - stdout: "piped", - stderr: "null", - }); - // Once fileServer is ready it will write to its stdout. - assert(fileServer.stdout != null); - const r = new TextProtoReader(new BufReader(fileServer.stdout)); - const s = await r.readLine(); - assert(s !== null && s.includes("server listening")); -} - -Deno.test("serveDirectory TLS", async function (): Promise { - await startTlsFileServer(); - try { - // Valid request after invalid - const conn = await Deno.connectTls({ - hostname: "localhost", - port: 4577, - certFile: join(testdataDir, "tls/RootCA.pem"), - }); - - await Deno.writeAll( - conn, - new TextEncoder().encode("GET / HTTP/1.0\r\n\r\n"), - ); - const res = new Uint8Array(128 * 1024); - const nread = await conn.read(res); - assert(nread !== null); - conn.close(); - const page = new TextDecoder().decode(res.subarray(0, nread)); - assert(page.includes("Deno File Server")); - } finally { - await killFileServer(); - } -}); - -Deno.test("partial TLS arguments fail", async function (): Promise { - fileServer = Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - "--allow-read", - "--allow-net", - "file_server.ts", - ".", - "--host", - "localhost", - "--cert", - "./testdata/tls/localhost.crt", - "-p", - `4578`, - ], - cwd: moduleDir, - stdout: "piped", - stderr: "null", - }); - try { - // Once fileServer is ready it will write to its stdout. - assert(fileServer.stdout != null); - const r = new TextProtoReader(new BufReader(fileServer.stdout)); - const s = await r.readLine(); - assert( - s !== null && s.includes("--key and --cert are required for TLS"), - ); - } finally { - await killFileServer(); - } -}); - -Deno.test("file_server disable dir listings", async function (): Promise { - await startFileServer({ "dir-listing": false }); - try { - const res = await fetch("http://localhost:4507/"); - assert(res.headers.has("access-control-allow-origin")); - assert(res.headers.has("access-control-allow-headers")); - assertEquals(res.status, 404); - const _ = await res.text(); - } finally { - await killFileServer(); - } -}); diff --git a/std/http/http_status.ts b/std/http/http_status.ts deleted file mode 100644 index a6148b2f79..0000000000 --- a/std/http/http_status.ts +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -/** HTTP status codes */ -export enum Status { - /** RFC 7231, 6.2.1 */ - Continue = 100, - /** RFC 7231, 6.2.2 */ - SwitchingProtocols = 101, - /** RFC 2518, 10.1 */ - Processing = 102, - /** RFC 8297 **/ - EarlyHints = 103, - /** RFC 7231, 6.3.1 */ - OK = 200, - /** RFC 7231, 6.3.2 */ - Created = 201, - /** RFC 7231, 6.3.3 */ - Accepted = 202, - /** RFC 7231, 6.3.4 */ - NonAuthoritativeInfo = 203, - /** RFC 7231, 6.3.5 */ - NoContent = 204, - /** RFC 7231, 6.3.6 */ - ResetContent = 205, - /** RFC 7233, 4.1 */ - PartialContent = 206, - /** RFC 4918, 11.1 */ - MultiStatus = 207, - /** RFC 5842, 7.1 */ - AlreadyReported = 208, - /** RFC 3229, 10.4.1 */ - IMUsed = 226, - - /** RFC 7231, 6.4.1 */ - MultipleChoices = 300, - /** RFC 7231, 6.4.2 */ - MovedPermanently = 301, - /** RFC 7231, 6.4.3 */ - Found = 302, - /** RFC 7231, 6.4.4 */ - SeeOther = 303, - /** RFC 7232, 4.1 */ - NotModified = 304, - /** RFC 7231, 6.4.5 */ - UseProxy = 305, - /** RFC 7231, 6.4.7 */ - TemporaryRedirect = 307, - /** RFC 7538, 3 */ - PermanentRedirect = 308, - - /** RFC 7231, 6.5.1 */ - BadRequest = 400, - /** RFC 7235, 3.1 */ - Unauthorized = 401, - /** RFC 7231, 6.5.2 */ - PaymentRequired = 402, - /** RFC 7231, 6.5.3 */ - Forbidden = 403, - /** RFC 7231, 6.5.4 */ - NotFound = 404, - /** RFC 7231, 6.5.5 */ - MethodNotAllowed = 405, - /** RFC 7231, 6.5.6 */ - NotAcceptable = 406, - /** RFC 7235, 3.2 */ - ProxyAuthRequired = 407, - /** RFC 7231, 6.5.7 */ - RequestTimeout = 408, - /** RFC 7231, 6.5.8 */ - Conflict = 409, - /** RFC 7231, 6.5.9 */ - Gone = 410, - /** RFC 7231, 6.5.10 */ - LengthRequired = 411, - /** RFC 7232, 4.2 */ - PreconditionFailed = 412, - /** RFC 7231, 6.5.11 */ - RequestEntityTooLarge = 413, - /** RFC 7231, 6.5.12 */ - RequestURITooLong = 414, - /** RFC 7231, 6.5.13 */ - UnsupportedMediaType = 415, - /** RFC 7233, 4.4 */ - RequestedRangeNotSatisfiable = 416, - /** RFC 7231, 6.5.14 */ - ExpectationFailed = 417, - /** RFC 7168, 2.3.3 */ - Teapot = 418, - /** RFC 7540, 9.1.2 */ - MisdirectedRequest = 421, - /** RFC 4918, 11.2 */ - UnprocessableEntity = 422, - /** RFC 4918, 11.3 */ - Locked = 423, - /** RFC 4918, 11.4 */ - FailedDependency = 424, - /** RFC 8470, 5.2 */ - TooEarly = 425, - /** RFC 7231, 6.5.15 */ - UpgradeRequired = 426, - /** RFC 6585, 3 */ - PreconditionRequired = 428, - /** RFC 6585, 4 */ - TooManyRequests = 429, - /** RFC 6585, 5 */ - RequestHeaderFieldsTooLarge = 431, - /** RFC 7725, 3 */ - UnavailableForLegalReasons = 451, - - /** RFC 7231, 6.6.1 */ - InternalServerError = 500, - /** RFC 7231, 6.6.2 */ - NotImplemented = 501, - /** RFC 7231, 6.6.3 */ - BadGateway = 502, - /** RFC 7231, 6.6.4 */ - ServiceUnavailable = 503, - /** RFC 7231, 6.6.5 */ - GatewayTimeout = 504, - /** RFC 7231, 6.6.6 */ - HTTPVersionNotSupported = 505, - /** RFC 2295, 8.1 */ - VariantAlsoNegotiates = 506, - /** RFC 4918, 11.5 */ - InsufficientStorage = 507, - /** RFC 5842, 7.2 */ - LoopDetected = 508, - /** RFC 2774, 7 */ - NotExtended = 510, - /** RFC 6585, 6 */ - NetworkAuthenticationRequired = 511, -} - -export const STATUS_TEXT = new Map([ - [Status.Continue, "Continue"], - [Status.SwitchingProtocols, "Switching Protocols"], - [Status.Processing, "Processing"], - [Status.EarlyHints, "Early Hints"], - [Status.OK, "OK"], - [Status.Created, "Created"], - [Status.Accepted, "Accepted"], - [Status.NonAuthoritativeInfo, "Non-Authoritative Information"], - [Status.NoContent, "No Content"], - [Status.ResetContent, "Reset Content"], - [Status.PartialContent, "Partial Content"], - [Status.MultiStatus, "Multi-Status"], - [Status.AlreadyReported, "Already Reported"], - [Status.IMUsed, "IM Used"], - [Status.MultipleChoices, "Multiple Choices"], - [Status.MovedPermanently, "Moved Permanently"], - [Status.Found, "Found"], - [Status.SeeOther, "See Other"], - [Status.NotModified, "Not Modified"], - [Status.UseProxy, "Use Proxy"], - [Status.TemporaryRedirect, "Temporary Redirect"], - [Status.PermanentRedirect, "Permanent Redirect"], - [Status.BadRequest, "Bad Request"], - [Status.Unauthorized, "Unauthorized"], - [Status.PaymentRequired, "Payment Required"], - [Status.Forbidden, "Forbidden"], - [Status.NotFound, "Not Found"], - [Status.MethodNotAllowed, "Method Not Allowed"], - [Status.NotAcceptable, "Not Acceptable"], - [Status.ProxyAuthRequired, "Proxy Authentication Required"], - [Status.RequestTimeout, "Request Timeout"], - [Status.Conflict, "Conflict"], - [Status.Gone, "Gone"], - [Status.LengthRequired, "Length Required"], - [Status.PreconditionFailed, "Precondition Failed"], - [Status.RequestEntityTooLarge, "Request Entity Too Large"], - [Status.RequestURITooLong, "Request URI Too Long"], - [Status.UnsupportedMediaType, "Unsupported Media Type"], - [Status.RequestedRangeNotSatisfiable, "Requested Range Not Satisfiable"], - [Status.ExpectationFailed, "Expectation Failed"], - [Status.Teapot, "I'm a teapot"], - [Status.MisdirectedRequest, "Misdirected Request"], - [Status.UnprocessableEntity, "Unprocessable Entity"], - [Status.Locked, "Locked"], - [Status.FailedDependency, "Failed Dependency"], - [Status.TooEarly, "Too Early"], - [Status.UpgradeRequired, "Upgrade Required"], - [Status.PreconditionRequired, "Precondition Required"], - [Status.TooManyRequests, "Too Many Requests"], - [Status.RequestHeaderFieldsTooLarge, "Request Header Fields Too Large"], - [Status.UnavailableForLegalReasons, "Unavailable For Legal Reasons"], - [Status.InternalServerError, "Internal Server Error"], - [Status.NotImplemented, "Not Implemented"], - [Status.BadGateway, "Bad Gateway"], - [Status.ServiceUnavailable, "Service Unavailable"], - [Status.GatewayTimeout, "Gateway Timeout"], - [Status.HTTPVersionNotSupported, "HTTP Version Not Supported"], - [Status.VariantAlsoNegotiates, "Variant Also Negotiates"], - [Status.InsufficientStorage, "Insufficient Storage"], - [Status.LoopDetected, "Loop Detected"], - [Status.NotExtended, "Not Extended"], - [Status.NetworkAuthenticationRequired, "Network Authentication Required"], -]); diff --git a/std/http/mod.ts b/std/http/mod.ts deleted file mode 100644 index 827eaebf80..0000000000 --- a/std/http/mod.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -export * from "./cookie.ts"; -export * from "./http_status.ts"; -export * from "./server.ts"; diff --git a/std/http/racing_server.ts b/std/http/racing_server.ts deleted file mode 100644 index b5cf69298a..0000000000 --- a/std/http/racing_server.ts +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { serve, ServerRequest } from "./server.ts"; -import { delay } from "../async/delay.ts"; - -const addr = Deno.args[1] || "127.0.0.1:4501"; -const server = serve(addr); - -function body(i: number): string { - return `Step${i}\n`; -} -async function delayedRespond( - request: ServerRequest, - step: number, -): Promise { - await delay(3000); - await request.respond({ status: 200, body: body(step) }); -} - -async function largeRespond(request: ServerRequest, c: string): Promise { - const b = new Uint8Array(1024 * 1024); - b.fill(c.charCodeAt(0)); - await request.respond({ status: 200, body: b }); -} - -async function ignoreToConsume( - request: ServerRequest, - step: number, -): Promise { - await request.respond({ status: 200, body: body(step) }); -} - -console.log("Racing server listening...\n"); - -let step = 1; -for await (const request of server) { - switch (step) { - case 1: - // Try to wait long enough. - // For pipelining, this should cause all the following response - // to block. - delayedRespond(request, step); - break; - case 2: - // HUGE body. - largeRespond(request, "a"); - break; - case 3: - // HUGE body. - largeRespond(request, "b"); - break; - case 4: - // Ignore to consume body (content-length) - ignoreToConsume(request, step); - break; - case 5: - // Ignore to consume body (chunked) - ignoreToConsume(request, step); - break; - case 6: - // Ignore to consume body (chunked + trailers) - ignoreToConsume(request, step); - break; - default: - request.respond({ status: 200, body: body(step) }); - break; - } - step++; -} diff --git a/std/http/racing_server_test.ts b/std/http/racing_server_test.ts deleted file mode 100644 index 8018a43129..0000000000 --- a/std/http/racing_server_test.ts +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "../testing/asserts.ts"; -import { BufReader, BufWriter } from "../io/bufio.ts"; -import { TextProtoReader } from "../textproto/mod.ts"; -import { dirname, fromFileUrl } from "../path/mod.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); - -let server: Deno.Process; -async function startServer(): Promise { - server = Deno.run({ - cmd: [Deno.execPath(), "run", "--quiet", "-A", "racing_server.ts"], - cwd: moduleDir, - stdout: "piped", - }); - // Once racing server is ready it will write to its stdout. - assert(server.stdout != null); - const r = new TextProtoReader(new BufReader(server.stdout)); - const s = await r.readLine(); - assert(s !== null && s.includes("Racing server listening...")); -} -function killServer(): void { - server.close(); - server.stdout.close(); -} - -const input = [ - "GET / HTTP/1.1\r\n\r\n", - "GET / HTTP/1.1\r\n\r\n", - "GET / HTTP/1.1\r\n\r\n", - "POST / HTTP/1.1\r\ncontent-length: 4\r\n\r\ndeno", - "POST / HTTP/1.1\r\ntransfer-encoding: chunked\r\n\r\n4\r\ndeno\r\n0\r\n\r\n", - "POST / HTTP/1.1\r\ntransfer-encoding: chunked\r\ntrailer: deno\r\n\r\n4\r\ndeno\r\n0\r\n\r\ndeno: land\r\n\r\n", - "GET / HTTP/1.1\r\n\r\n", -].join(""); -const HUGE_BODY_SIZE = 1024 * 1024; -const output = `HTTP/1.1 200 OK -content-length: 6 - -Step1 -HTTP/1.1 200 OK -content-length: ${HUGE_BODY_SIZE} - -${"a".repeat(HUGE_BODY_SIZE)}HTTP/1.1 200 OK -content-length: ${HUGE_BODY_SIZE} - -${"b".repeat(HUGE_BODY_SIZE)}HTTP/1.1 200 OK -content-length: 6 - -Step4 -HTTP/1.1 200 OK -content-length: 6 - -Step5 -HTTP/1.1 200 OK -content-length: 6 - -Step6 -HTTP/1.1 200 OK -content-length: 6 - -Step7 -`; - -Deno.test("serverPipelineRace", async function (): Promise { - await startServer(); - - const conn = await Deno.connect({ port: 4501 }); - const r = new TextProtoReader(new BufReader(conn)); - const w = new BufWriter(conn); - await w.write(new TextEncoder().encode(input)); - await w.flush(); - const outLines = output.split("\n"); - // length - 1 to disregard last empty line - for (let i = 0; i < outLines.length - 1; i++) { - const s = await r.readLine(); - assertEquals(s, outLines[i]); - } - killServer(); - conn.close(); -}); diff --git a/std/http/server.ts b/std/http/server.ts deleted file mode 100644 index f17c759c47..0000000000 --- a/std/http/server.ts +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { encode } from "../encoding/utf8.ts"; -import { BufReader, BufWriter } from "../io/bufio.ts"; -import { assert } from "../_util/assert.ts"; -import { Deferred, deferred, MuxAsyncIterator } from "../async/mod.ts"; -import { - bodyReader, - chunkedBodyReader, - emptyReader, - readRequest, - writeResponse, -} from "./_io.ts"; - -export class ServerRequest { - url!: string; - method!: string; - proto!: string; - protoMinor!: number; - protoMajor!: number; - headers!: Headers; - conn!: Deno.Conn; - r!: BufReader; - w!: BufWriter; - - #done: Deferred = deferred(); - #contentLength?: number | null = undefined; - #body?: Deno.Reader = undefined; - #finalized = false; - - get done(): Promise { - return this.#done.then((e) => e); - } - - /** - * Value of Content-Length header. - * If null, then content length is invalid or not given (e.g. chunked encoding). - */ - get contentLength(): number | null { - // undefined means not cached. - // null means invalid or not provided. - if (this.#contentLength === undefined) { - const cl = this.headers.get("content-length"); - if (cl) { - this.#contentLength = parseInt(cl); - // Convert NaN to null (as NaN harder to test) - if (Number.isNaN(this.#contentLength)) { - this.#contentLength = null; - } - } else { - this.#contentLength = null; - } - } - return this.#contentLength; - } - - /** - * Body of the request. The easiest way to consume the body is: - * - * const buf: Uint8Array = await Deno.readAll(req.body); - */ - get body(): Deno.Reader { - if (!this.#body) { - if (this.contentLength != null) { - this.#body = bodyReader(this.contentLength, this.r); - } else { - const transferEncoding = this.headers.get("transfer-encoding"); - if (transferEncoding != null) { - const parts = transferEncoding - .split(",") - .map((e): string => e.trim().toLowerCase()); - assert( - parts.includes("chunked"), - 'transfer-encoding must include "chunked" if content-length is not set', - ); - this.#body = chunkedBodyReader(this.headers, this.r); - } else { - // Neither content-length nor transfer-encoding: chunked - this.#body = emptyReader(); - } - } - } - return this.#body; - } - - async respond(r: Response): Promise { - let err: Error | undefined; - try { - // Write our response! - await writeResponse(this.w, r); - } catch (e) { - try { - // Eagerly close on error. - this.conn.close(); - } catch { - // Pass - } - err = e; - } - // Signal that this request has been processed and the next pipelined - // request on the same connection can be accepted. - this.#done.resolve(err); - if (err) { - // Error during responding, rethrow. - throw err; - } - } - - async finalize(): Promise { - if (this.#finalized) return; - // Consume unread body - const body = this.body; - const buf = new Uint8Array(1024); - while ((await body.read(buf)) !== null) { - // Pass - } - this.#finalized = true; - } -} - -export class Server implements AsyncIterable { - #closing = false; - #connections: Deno.Conn[] = []; - - constructor(public listener: Deno.Listener) {} - - close(): void { - this.#closing = true; - this.listener.close(); - for (const conn of this.#connections) { - try { - conn.close(); - } catch (e) { - // Connection might have been already closed - if (!(e instanceof Deno.errors.BadResource)) { - throw e; - } - } - } - } - - // Yields all HTTP requests on a single TCP connection. - private async *iterateHttpRequests( - conn: Deno.Conn, - ): AsyncIterableIterator { - const reader = new BufReader(conn); - const writer = new BufWriter(conn); - - while (!this.#closing) { - let request: ServerRequest | null; - try { - request = await readRequest(conn, reader); - } catch (error) { - if ( - error instanceof Deno.errors.InvalidData || - error instanceof Deno.errors.UnexpectedEof - ) { - // An error was thrown while parsing request headers. - // Try to send the "400 Bad Request" before closing the connection. - try { - await writeResponse(writer, { - status: 400, - body: encode(`${error.message}\r\n\r\n`), - }); - } catch (error) { - // The connection is broken. - } - } - break; - } - if (request === null) { - break; - } - - request.w = writer; - yield request; - - // Wait for the request to be processed before we accept a new request on - // this connection. - const responseError = await request.done; - if (responseError) { - // Something bad happened during response. - // (likely other side closed during pipelined req) - // req.done implies this connection already closed, so we can just return. - this.untrackConnection(request.conn); - return; - } - - try { - // Consume unread body and trailers if receiver didn't consume those data - await request.finalize(); - } catch (error) { - // Invalid data was received or the connection was closed. - break; - } - } - - this.untrackConnection(conn); - try { - conn.close(); - } catch (e) { - // might have been already closed - } - } - - private trackConnection(conn: Deno.Conn): void { - this.#connections.push(conn); - } - - private untrackConnection(conn: Deno.Conn): void { - const index = this.#connections.indexOf(conn); - if (index !== -1) { - this.#connections.splice(index, 1); - } - } - - // Accepts a new TCP connection and yields all HTTP requests that arrive on - // it. When a connection is accepted, it also creates a new iterator of the - // same kind and adds it to the request multiplexer so that another TCP - // connection can be accepted. - private async *acceptConnAndIterateHttpRequests( - mux: MuxAsyncIterator, - ): AsyncIterableIterator { - if (this.#closing) return; - // Wait for a new connection. - let conn: Deno.Conn; - try { - conn = await this.listener.accept(); - } catch (error) { - if ( - // The listener is closed: - error instanceof Deno.errors.BadResource || - // TLS handshake errors: - error instanceof Deno.errors.InvalidData || - error instanceof Deno.errors.UnexpectedEof || - error instanceof Deno.errors.ConnectionReset - ) { - return mux.add(this.acceptConnAndIterateHttpRequests(mux)); - } - throw error; - } - this.trackConnection(conn); - // Try to accept another connection and add it to the multiplexer. - mux.add(this.acceptConnAndIterateHttpRequests(mux)); - // Yield the requests that arrive on the just-accepted connection. - yield* this.iterateHttpRequests(conn); - } - - [Symbol.asyncIterator](): AsyncIterableIterator { - const mux: MuxAsyncIterator = new MuxAsyncIterator(); - mux.add(this.acceptConnAndIterateHttpRequests(mux)); - return mux.iterate(); - } -} - -/** Options for creating an HTTP server. */ -export type HTTPOptions = Omit; - -/** - * Parse addr from string - * - * const addr = "::1:8000"; - * parseAddrFromString(addr); - * - * @param addr Address string - */ -export function _parseAddrFromStr(addr: string): HTTPOptions { - let url: URL; - try { - const host = addr.startsWith(":") ? `0.0.0.0${addr}` : addr; - url = new URL(`http://${host}`); - } catch { - throw new TypeError("Invalid address."); - } - if ( - url.username || - url.password || - url.pathname != "/" || - url.search || - url.hash - ) { - throw new TypeError("Invalid address."); - } - - return { - hostname: url.hostname, - port: url.port === "" ? 80 : Number(url.port), - }; -} - -/** - * Create a HTTP server - * - * import { serve } from "https://deno.land/std/http/server.ts"; - * const body = "Hello World\n"; - * const server = serve({ port: 8000 }); - * for await (const req of server) { - * req.respond({ body }); - * } - */ -export function serve(addr: string | HTTPOptions): Server { - if (typeof addr === "string") { - addr = _parseAddrFromStr(addr); - } - - const listener = Deno.listen(addr); - return new Server(listener); -} - -/** - * Start an HTTP server with given options and request handler - * - * const body = "Hello World\n"; - * const options = { port: 8000 }; - * listenAndServe(options, (req) => { - * req.respond({ body }); - * }); - * - * @param options Server configuration - * @param handler Request handler - */ -export async function listenAndServe( - addr: string | HTTPOptions, - handler: (req: ServerRequest) => void, -): Promise { - const server = serve(addr); - - for await (const request of server) { - handler(request); - } -} - -/** Options for creating an HTTPS server. */ -export type HTTPSOptions = Omit; - -/** - * Create an HTTPS server with given options - * - * const body = "Hello HTTPS"; - * const options = { - * hostname: "localhost", - * port: 443, - * certFile: "./path/to/localhost.crt", - * keyFile: "./path/to/localhost.key", - * }; - * for await (const req of serveTLS(options)) { - * req.respond({ body }); - * } - * - * @param options Server configuration - * @return Async iterable server instance for incoming requests - */ -export function serveTLS(options: HTTPSOptions): Server { - const tlsOptions: Deno.ListenTlsOptions = { - ...options, - transport: "tcp", - }; - const listener = Deno.listenTls(tlsOptions); - return new Server(listener); -} - -/** - * Start an HTTPS server with given options and request handler - * - * const body = "Hello HTTPS"; - * const options = { - * hostname: "localhost", - * port: 443, - * certFile: "./path/to/localhost.crt", - * keyFile: "./path/to/localhost.key", - * }; - * listenAndServeTLS(options, (req) => { - * req.respond({ body }); - * }); - * - * @param options Server configuration - * @param handler Request handler - */ -export async function listenAndServeTLS( - options: HTTPSOptions, - handler: (req: ServerRequest) => void, -): Promise { - const server = serveTLS(options); - - for await (const request of server) { - handler(request); - } -} - -/** - * Interface of HTTP server response. - * If body is a Reader, response would be chunked. - * If body is a string, it would be UTF-8 encoded by default. - */ -export interface Response { - status?: number; - headers?: Headers; - body?: Uint8Array | Deno.Reader | string; - trailers?: () => Promise | Headers; -} diff --git a/std/http/server_test.ts b/std/http/server_test.ts deleted file mode 100644 index 8a3be71c20..0000000000 --- a/std/http/server_test.ts +++ /dev/null @@ -1,784 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Ported from -// https://github.com/golang/go/blob/master/src/net/http/responsewrite_test.go - -import { TextProtoReader } from "../textproto/mod.ts"; -import { - assert, - assertEquals, - assertMatch, - assertStringIncludes, - assertThrowsAsync, -} from "../testing/asserts.ts"; -import { - _parseAddrFromStr, - Response, - serve, - Server, - ServerRequest, - serveTLS, -} from "./server.ts"; -import { BufReader, BufWriter } from "../io/bufio.ts"; -import { delay } from "../async/delay.ts"; -import { decode, encode } from "../encoding/utf8.ts"; -import { mockConn } from "./_mock_conn.ts"; -import { dirname, fromFileUrl, join, resolve } from "../path/mod.ts"; - -const moduleDir = dirname(fromFileUrl(import.meta.url)); -const testdataDir = resolve(moduleDir, "testdata"); - -interface ResponseTest { - response: Response; - raw: string; -} - -const responseTests: ResponseTest[] = [ - // Default response - { - response: {}, - raw: "HTTP/1.1 200 OK\r\n" + "content-length: 0" + "\r\n\r\n", - }, - // Empty body with status - { - response: { - status: 404, - }, - raw: "HTTP/1.1 404 Not Found\r\n" + "content-length: 0" + "\r\n\r\n", - }, - // HTTP/1.1, chunked coding; empty trailer; close - { - response: { - status: 200, - body: new Deno.Buffer(new TextEncoder().encode("abcdef")), - }, - - raw: "HTTP/1.1 200 OK\r\n" + - "transfer-encoding: chunked\r\n\r\n" + - "6\r\nabcdef\r\n0\r\n\r\n", - }, -]; - -Deno.test("responseWrite", async function (): Promise { - for (const testCase of responseTests) { - const buf = new Deno.Buffer(); - const bufw = new BufWriter(buf); - const request = new ServerRequest(); - request.w = bufw; - - request.conn = mockConn(); - - await request.respond(testCase.response); - assertEquals(new TextDecoder().decode(buf.bytes()), testCase.raw); - await request.done; - } -}); - -Deno.test("requestContentLength", function (): void { - // Has content length - { - const req = new ServerRequest(); - req.headers = new Headers(); - req.headers.set("content-length", "5"); - const buf = new Deno.Buffer(encode("Hello")); - req.r = new BufReader(buf); - assertEquals(req.contentLength, 5); - } - // No content length - { - const shortText = "Hello"; - const req = new ServerRequest(); - req.headers = new Headers(); - req.headers.set("transfer-encoding", "chunked"); - let chunksData = ""; - let chunkOffset = 0; - const maxChunkSize = 70; - while (chunkOffset < shortText.length) { - const chunkSize = Math.min(maxChunkSize, shortText.length - chunkOffset); - chunksData += `${chunkSize.toString(16)}\r\n${ - shortText.substr(chunkOffset, chunkSize) - }\r\n`; - chunkOffset += chunkSize; - } - chunksData += "0\r\n\r\n"; - const buf = new Deno.Buffer(encode(chunksData)); - req.r = new BufReader(buf); - assertEquals(req.contentLength, null); - } -}); - -interface TotalReader extends Deno.Reader { - total: number; -} -function totalReader(r: Deno.Reader): TotalReader { - let _total = 0; - async function read(p: Uint8Array): Promise { - const result = await r.read(p); - if (typeof result === "number") { - _total += result; - } - return result; - } - return { - read, - get total(): number { - return _total; - }, - }; -} -Deno.test("requestBodyWithContentLength", async function (): Promise { - { - const req = new ServerRequest(); - req.headers = new Headers(); - req.headers.set("content-length", "5"); - const buf = new Deno.Buffer(encode("Hello")); - req.r = new BufReader(buf); - const body = decode(await Deno.readAll(req.body)); - assertEquals(body, "Hello"); - } - - // Larger than internal buf - { - const longText = "1234\n".repeat(1000); - const req = new ServerRequest(); - req.headers = new Headers(); - req.headers.set("Content-Length", "5000"); - const buf = new Deno.Buffer(encode(longText)); - req.r = new BufReader(buf); - const body = decode(await Deno.readAll(req.body)); - assertEquals(body, longText); - } - // Handler ignored to consume body -}); -Deno.test( - "ServerRequest.finalize() should consume unread body / content-length", - async () => { - const text = "deno.land"; - const req = new ServerRequest(); - req.headers = new Headers(); - req.headers.set("content-length", "" + text.length); - const tr = totalReader(new Deno.Buffer(encode(text))); - req.r = new BufReader(tr); - req.w = new BufWriter(new Deno.Buffer()); - await req.respond({ status: 200, body: "ok" }); - assertEquals(tr.total, 0); - await req.finalize(); - assertEquals(tr.total, text.length); - }, -); -Deno.test( - "ServerRequest.finalize() should consume unread body / chunked, trailers", - async () => { - const text = [ - "5", - "Hello", - "4", - "Deno", - "0", - "", - "deno: land", - "node: js", - "", - "", - ].join("\r\n"); - const req = new ServerRequest(); - req.headers = new Headers(); - req.headers.set("transfer-encoding", "chunked"); - req.headers.set("trailer", "deno,node"); - const body = encode(text); - const tr = totalReader(new Deno.Buffer(body)); - req.r = new BufReader(tr); - req.w = new BufWriter(new Deno.Buffer()); - await req.respond({ status: 200, body: "ok" }); - assertEquals(tr.total, 0); - assertEquals(req.headers.has("trailer"), true); - assertEquals(req.headers.has("deno"), false); - assertEquals(req.headers.has("node"), false); - await req.finalize(); - assertEquals(tr.total, body.byteLength); - assertEquals(req.headers.has("trailer"), false); - assertEquals(req.headers.get("deno"), "land"); - assertEquals(req.headers.get("node"), "js"); - }, -); -Deno.test("requestBodyWithTransferEncoding", async function (): Promise { - { - const shortText = "Hello"; - const req = new ServerRequest(); - req.headers = new Headers(); - req.headers.set("transfer-encoding", "chunked"); - let chunksData = ""; - let chunkOffset = 0; - const maxChunkSize = 70; - while (chunkOffset < shortText.length) { - const chunkSize = Math.min(maxChunkSize, shortText.length - chunkOffset); - chunksData += `${chunkSize.toString(16)}\r\n${ - shortText.substr(chunkOffset, chunkSize) - }\r\n`; - chunkOffset += chunkSize; - } - chunksData += "0\r\n\r\n"; - const buf = new Deno.Buffer(encode(chunksData)); - req.r = new BufReader(buf); - const body = decode(await Deno.readAll(req.body)); - assertEquals(body, shortText); - } - - // Larger than internal buf - { - const longText = "1234\n".repeat(1000); - const req = new ServerRequest(); - req.headers = new Headers(); - req.headers.set("transfer-encoding", "chunked"); - let chunksData = ""; - let chunkOffset = 0; - const maxChunkSize = 70; - while (chunkOffset < longText.length) { - const chunkSize = Math.min(maxChunkSize, longText.length - chunkOffset); - chunksData += `${chunkSize.toString(16)}\r\n${ - longText.substr(chunkOffset, chunkSize) - }\r\n`; - chunkOffset += chunkSize; - } - chunksData += "0\r\n\r\n"; - const buf = new Deno.Buffer(encode(chunksData)); - req.r = new BufReader(buf); - const body = decode(await Deno.readAll(req.body)); - assertEquals(body, longText); - } -}); - -Deno.test("requestBodyReaderWithContentLength", async function (): Promise< - void -> { - { - const shortText = "Hello"; - const req = new ServerRequest(); - req.headers = new Headers(); - req.headers.set("content-length", "" + shortText.length); - const buf = new Deno.Buffer(encode(shortText)); - req.r = new BufReader(buf); - const readBuf = new Uint8Array(6); - let offset = 0; - while (offset < shortText.length) { - const nread = await req.body.read(readBuf); - assert(nread !== null); - const s = decode(readBuf.subarray(0, nread as number)); - assertEquals(shortText.substr(offset, nread as number), s); - offset += nread as number; - } - const nread = await req.body.read(readBuf); - assertEquals(nread, null); - } - - // Larger than given buf - { - const longText = "1234\n".repeat(1000); - const req = new ServerRequest(); - req.headers = new Headers(); - req.headers.set("Content-Length", "5000"); - const buf = new Deno.Buffer(encode(longText)); - req.r = new BufReader(buf); - const readBuf = new Uint8Array(1000); - let offset = 0; - while (offset < longText.length) { - const nread = await req.body.read(readBuf); - assert(nread !== null); - const s = decode(readBuf.subarray(0, nread as number)); - assertEquals(longText.substr(offset, nread as number), s); - offset += nread as number; - } - const nread = await req.body.read(readBuf); - assertEquals(nread, null); - } -}); - -Deno.test("requestBodyReaderWithTransferEncoding", async function (): Promise< - void -> { - { - const shortText = "Hello"; - const req = new ServerRequest(); - req.headers = new Headers(); - req.headers.set("transfer-encoding", "chunked"); - let chunksData = ""; - let chunkOffset = 0; - const maxChunkSize = 70; - while (chunkOffset < shortText.length) { - const chunkSize = Math.min(maxChunkSize, shortText.length - chunkOffset); - chunksData += `${chunkSize.toString(16)}\r\n${ - shortText.substr(chunkOffset, chunkSize) - }\r\n`; - chunkOffset += chunkSize; - } - chunksData += "0\r\n\r\n"; - const buf = new Deno.Buffer(encode(chunksData)); - req.r = new BufReader(buf); - const readBuf = new Uint8Array(6); - let offset = 0; - while (offset < shortText.length) { - const nread = await req.body.read(readBuf); - assert(nread !== null); - const s = decode(readBuf.subarray(0, nread as number)); - assertEquals(shortText.substr(offset, nread as number), s); - offset += nread as number; - } - const nread = await req.body.read(readBuf); - assertEquals(nread, null); - } - - // Larger than internal buf - { - const longText = "1234\n".repeat(1000); - const req = new ServerRequest(); - req.headers = new Headers(); - req.headers.set("transfer-encoding", "chunked"); - let chunksData = ""; - let chunkOffset = 0; - const maxChunkSize = 70; - while (chunkOffset < longText.length) { - const chunkSize = Math.min(maxChunkSize, longText.length - chunkOffset); - chunksData += `${chunkSize.toString(16)}\r\n${ - longText.substr(chunkOffset, chunkSize) - }\r\n`; - chunkOffset += chunkSize; - } - chunksData += "0\r\n\r\n"; - const buf = new Deno.Buffer(encode(chunksData)); - req.r = new BufReader(buf); - const readBuf = new Uint8Array(1000); - let offset = 0; - while (offset < longText.length) { - const nread = await req.body.read(readBuf); - assert(nread !== null); - const s = decode(readBuf.subarray(0, nread as number)); - assertEquals(longText.substr(offset, nread as number), s); - offset += nread as number; - } - const nread = await req.body.read(readBuf); - assertEquals(nread, null); - } -}); - -Deno.test({ - name: "destroyed connection", - fn: async (): Promise => { - // Runs a simple server as another process - const p = Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - "--allow-net", - "testdata/simple_server.ts", - ], - cwd: moduleDir, - stdout: "piped", - }); - - let serverIsRunning = true; - const statusPromise = p - .status() - .then((): void => { - serverIsRunning = false; - }) - .catch((_): void => {}); // Ignores the error when closing the process. - - try { - const r = new TextProtoReader(new BufReader(p.stdout)); - const s = await r.readLine(); - assert(s !== null && s.includes("server listening")); - await delay(100); - // Reqeusts to the server and immediately closes the connection - const conn = await Deno.connect({ port: 4502 }); - await conn.write(new TextEncoder().encode("GET / HTTP/1.0\n\n")); - conn.close(); - // Waits for the server to handle the above (broken) request - await delay(100); - assert(serverIsRunning); - } finally { - // Stops the sever and allows `p.status()` promise to resolve - Deno.kill(p.pid, Deno.Signal.SIGKILL); - await statusPromise; - p.stdout.close(); - p.close(); - } - }, -}); - -Deno.test({ - name: "serveTLS", - fn: async (): Promise => { - // Runs a simple server as another process - const p = Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "--quiet", - "--allow-net", - "--allow-read", - "testdata/simple_https_server.ts", - ], - cwd: moduleDir, - stdout: "piped", - }); - - let serverIsRunning = true; - const statusPromise = p - .status() - .then((): void => { - serverIsRunning = false; - }) - .catch((_): void => {}); // Ignores the error when closing the process. - - try { - const r = new TextProtoReader(new BufReader(p.stdout)); - const s = await r.readLine(); - assert( - s !== null && s.includes("server listening"), - "server must be started", - ); - // Requests to the server and immediately closes the connection - const conn = await Deno.connectTls({ - hostname: "localhost", - port: 4503, - certFile: join(testdataDir, "tls/RootCA.pem"), - }); - await Deno.writeAll( - conn, - new TextEncoder().encode("GET / HTTP/1.0\r\n\r\n"), - ); - const res = new Uint8Array(100); - const nread = await conn.read(res); - assert(nread !== null); - conn.close(); - const resStr = new TextDecoder().decode(res.subarray(0, nread)); - assert(resStr.includes("Hello HTTPS")); - assert(serverIsRunning); - } finally { - // Stops the sever and allows `p.status()` promise to resolve - Deno.kill(p.pid, Deno.Signal.SIGKILL); - await statusPromise; - p.stdout.close(); - p.close(); - } - }, -}); - -Deno.test( - "close server while iterating", - async (): Promise => { - const server = serve(":8123"); - const nextWhileClosing = server[Symbol.asyncIterator]().next(); - server.close(); - assertEquals(await nextWhileClosing, { value: undefined, done: true }); - - const nextAfterClosing = server[Symbol.asyncIterator]().next(); - assertEquals(await nextAfterClosing, { value: undefined, done: true }); - }, -); - -Deno.test({ - name: "[http] close server while connection is open", - async fn(): Promise { - async function iteratorReq(server: Server): Promise { - for await (const req of server) { - await req.respond({ body: new TextEncoder().encode(req.url) }); - } - } - - const server = serve(":8123"); - const p = iteratorReq(server); - const conn = await Deno.connect({ hostname: "127.0.0.1", port: 8123 }); - await Deno.writeAll( - conn, - new TextEncoder().encode("GET /hello HTTP/1.1\r\n\r\n"), - ); - const res = new Uint8Array(100); - const nread = await conn.read(res); - assert(nread !== null); - const resStr = new TextDecoder().decode(res.subarray(0, nread)); - assertStringIncludes(resStr, "/hello"); - server.close(); - await p; - // Client connection should still be open, verify that - // it's visible in resource table. - const resources = Deno.resources(); - assertEquals(resources[conn.rid], "tcpStream"); - conn.close(); - }, -}); - -Deno.test({ - name: "respond error closes connection", - async fn(): Promise { - const serverRoutine = async (): Promise => { - const server = serve(":8124"); - for await (const req of server) { - await assertThrowsAsync(async () => { - await req.respond({ - status: 12345, - body: new TextEncoder().encode("Hello World"), - }); - }, Deno.errors.InvalidData); - // The connection should be destroyed - assert(!(req.conn.rid in Deno.resources())); - server.close(); - } - }; - const p = serverRoutine(); - const conn = await Deno.connect({ - hostname: "127.0.0.1", - port: 8124, - }); - await Deno.writeAll( - conn, - new TextEncoder().encode("GET / HTTP/1.1\r\n\r\n"), - ); - conn.close(); - await p; - }, -}); - -Deno.test({ - name: "[http] request error gets 400 response", - async fn(): Promise { - const server = serve(":8124"); - const entry = server[Symbol.asyncIterator]().next(); - const conn = await Deno.connect({ - hostname: "127.0.0.1", - port: 8124, - }); - await Deno.writeAll( - conn, - encode("GET / HTTP/1.1\r\nmalformedHeader\r\n\r\n\r\n\r\n"), - ); - const responseString = decode(await Deno.readAll(conn)); - assertMatch( - responseString, - /^HTTP\/1\.1 400 Bad Request\r\ncontent-length: \d+\r\n\r\n.*\r\n\r\n$/ms, - ); - conn.close(); - server.close(); - assert((await entry).done); - }, -}); - -Deno.test({ - name: "[http] finalizing invalid chunked data closes connection", - async fn(): Promise { - const serverRoutine = async (): Promise => { - const server = serve(":8124"); - for await (const req of server) { - await req.respond({ status: 200, body: "Hello, world!" }); - break; - } - server.close(); - }; - const p = serverRoutine(); - const conn = await Deno.connect({ - hostname: "127.0.0.1", - port: 8124, - }); - await Deno.writeAll( - conn, - encode( - "PUT / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\nzzzzzzz\r\nhello", - ), - ); - await conn.closeWrite(); - const responseString = decode(await Deno.readAll(conn)); - assertEquals( - responseString, - "HTTP/1.1 200 OK\r\ncontent-length: 13\r\n\r\nHello, world!", - ); - conn.close(); - await p; - }, -}); - -Deno.test({ - name: "[http] finalizing chunked unexpected EOF closes connection", - async fn(): Promise { - const serverRoutine = async (): Promise => { - const server = serve(":8124"); - for await (const req of server) { - await req.respond({ status: 200, body: "Hello, world!" }); - break; - } - server.close(); - }; - const p = serverRoutine(); - const conn = await Deno.connect({ - hostname: "127.0.0.1", - port: 8124, - }); - await Deno.writeAll( - conn, - encode("PUT / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHello"), - ); - conn.closeWrite(); - const responseString = decode(await Deno.readAll(conn)); - assertEquals( - responseString, - "HTTP/1.1 200 OK\r\ncontent-length: 13\r\n\r\nHello, world!", - ); - conn.close(); - await p; - }, -}); - -Deno.test({ - name: - "[http] receiving bad request from a closed connection should not throw", - async fn(): Promise { - const server = serve(":8124"); - const serverRoutine = async (): Promise => { - for await (const req of server) { - await req.respond({ status: 200, body: "Hello, world!" }); - } - }; - const p = serverRoutine(); - const conn = await Deno.connect({ - hostname: "127.0.0.1", - port: 8124, - }); - await Deno.writeAll( - conn, - encode([ - // A normal request is required: - "GET / HTTP/1.1", - "Host: localhost", - "", - // The bad request: - "GET / HTTP/1.1", - "Host: localhost", - "INVALID!HEADER!", - "", - "", - ].join("\r\n")), - ); - // After sending the two requests, don't receive the reponses. - - // Closing the connection now. - conn.close(); - - // The server will write responses to the closed connection, - // the first few `write()` calls will not throws, until the server received - // the TCP RST. So we need the normal request before the bad request to - // make the server do a few writes before it writes that `400` response. - - // Wait for server to handle requests. - await delay(10); - - server.close(); - await p; - }, -}); - -Deno.test({ - name: "serveTLS Invalid Cert", - fn: async (): Promise => { - async function iteratorReq(server: Server): Promise { - for await (const req of server) { - await req.respond({ body: new TextEncoder().encode("Hello HTTPS") }); - } - } - const port = 9122; - const tlsOptions = { - hostname: "localhost", - port, - certFile: join(testdataDir, "tls/localhost.crt"), - keyFile: join(testdataDir, "tls/localhost.key"), - }; - const server = serveTLS(tlsOptions); - const p = iteratorReq(server); - - try { - // Invalid certificate, connection should throw - // but should not crash the server - assertThrowsAsync( - () => - Deno.connectTls({ - hostname: "localhost", - port, - // certFile - }), - Deno.errors.InvalidData, - ); - - // Valid request after invalid - const conn = await Deno.connectTls({ - hostname: "localhost", - port, - certFile: join(testdataDir, "tls/RootCA.pem"), - }); - - await Deno.writeAll( - conn, - new TextEncoder().encode("GET / HTTP/1.0\r\n\r\n"), - ); - const res = new Uint8Array(100); - const nread = await conn.read(res); - assert(nread !== null); - conn.close(); - const resStr = new TextDecoder().decode(res.subarray(0, nread)); - assert(resStr.includes("Hello HTTPS")); - } finally { - // Stops the sever and allows `p.status()` promise to resolve - server.close(); - await p; - } - }, -}); - -Deno.test({ - name: "server.serve() should be able to parse IPV4 address", - fn: (): void => { - const server = serve("127.0.0.1:8124"); - const expected = { - hostname: "127.0.0.1", - port: 8124, - transport: "tcp", - }; - assertEquals(server.listener.addr, expected); - server.close(); - }, -}); - -Deno.test({ - name: "server._parseAddrFromStr() should be able to parse IPV6 address", - fn: (): void => { - const addr = _parseAddrFromStr("[::1]:8124"); - const expected = { - hostname: "[::1]", - port: 8124, - }; - assertEquals(addr, expected); - }, -}); - -Deno.test({ - name: "server.serve() should be able to parse IPV6 address", - fn: (): void => { - const server = serve("[::1]:8124"); - const expected = { - hostname: "::1", - port: 8124, - transport: "tcp", - }; - assertEquals(server.listener.addr, expected); - server.close(); - }, -}); - -Deno.test({ - name: "server._parseAddrFromStr() port 80", - fn: (): void => { - const addr = _parseAddrFromStr(":80"); - assertEquals(addr.port, 80); - assertEquals(addr.hostname, "0.0.0.0"); - }, -}); diff --git a/std/http/test.ts b/std/http/test.ts deleted file mode 100644 index 590417055f..0000000000 --- a/std/http/test.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import "./mod.ts"; diff --git a/std/http/testdata/% b/std/http/testdata/% deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/std/http/testdata/file_server_as_library.ts b/std/http/testdata/file_server_as_library.ts deleted file mode 100644 index cd4bf68dbf..0000000000 --- a/std/http/testdata/file_server_as_library.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { serve } from "../server.ts"; -import { serveFile } from "../file_server.ts"; - -const server = serve({ port: 8000 }); - -console.log("Server running..."); - -for await (const req of server) { - serveFile(req, "./testdata/hello.html").then((response) => { - req.respond(response); - }); -} diff --git a/std/http/testdata/hello.html b/std/http/testdata/hello.html deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/std/http/testdata/simple_https_server.ts b/std/http/testdata/simple_https_server.ts deleted file mode 100644 index 84dfb39abf..0000000000 --- a/std/http/testdata/simple_https_server.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This is an example of a https server -import { serveTLS } from "../server.ts"; - -const tlsOptions = { - hostname: "localhost", - port: 4503, - certFile: "./testdata/tls/localhost.crt", - keyFile: "./testdata/tls/localhost.key", -}; -const s = serveTLS(tlsOptions); -console.log( - `Simple HTTPS server listening on ${tlsOptions.hostname}:${tlsOptions.port}`, -); -const body = new TextEncoder().encode("Hello HTTPS"); -for await (const req of s) { - req.respond({ body }); -} diff --git a/std/http/testdata/simple_server.ts b/std/http/testdata/simple_server.ts deleted file mode 100644 index ff2a0b5ac3..0000000000 --- a/std/http/testdata/simple_server.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This is an example of a server that responds with an empty body -import { serve } from "../server.ts"; - -const addr = "0.0.0.0:4502"; -console.log(`Simple server listening on ${addr}`); -for await (const req of serve(addr)) { - req.respond({}); -} diff --git a/std/http/testdata/test file.txt b/std/http/testdata/test file.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/std/http/testdata/tls b/std/http/testdata/tls deleted file mode 120000 index f6fd22ed86..0000000000 --- a/std/http/testdata/tls +++ /dev/null @@ -1 +0,0 @@ -../../../cli/tests/tls \ No newline at end of file diff --git a/std/io/README.md b/std/io/README.md deleted file mode 100644 index 4f5d7454fb..0000000000 --- a/std/io/README.md +++ /dev/null @@ -1,159 +0,0 @@ -# std/io - -## Bufio - -**Uses:** - -### readLines - -Read reader[like file], line by line: - -```ts title="readLines" -import { readLines } from "https://deno.land/std@$STD_VERSION/io/mod.ts"; -import * as path from "https://deno.land/std@$STD_VERSION/path/mod.ts"; - -const filename = path.join(Deno.cwd(), "std/io/README.md"); -let fileReader = await Deno.open(filename); - -for await (let line of readLines(fileReader)) { - console.log(line); -} -``` - -**Output:** - -````text -# std/io - -## readLines - -```ts -import * as path from "https://deno.land/std@$STD_VERSION/path/mod.ts"; - -## Rest of the file -```` - -### readStringDelim - -Read reader`[like file]` chunk by chunk, splitting based on delimiter. - -```ts title="readStringDelim" -import { readStringDelim } from "https://deno.land/std@$STD_VERSION/io/mod.ts"; -import * as path from "https://deno.land/std@$STD_VERSION/path/mod.ts"; - -const filename = path.join(Deno.cwd(), "std/io/README.md"); -let fileReader = await Deno.open(filename); - -for await (let line of readStringDelim(fileReader, "\n")) { - console.log(line); -} -``` - -**Output:** - -````text -# std/io - -## readLines - -```ts -import * as path from "https://deno.land/std@$STD_VERSION/path/mod.ts"; - -## Rest of the file -```` - -## Reader - -### StringReader - -Create a `Reader` object for `string`. - -```ts -import { StringReader } from "https://deno.land/std@$STD_VERSION/io/mod.ts"; - -const data = new Uint8Array(6); -const r = new StringReader("abcdef"); -const res0 = await r.read(data); -const res1 = await r.read(new Uint8Array(6)); - -// Number of bytes read -console.log(res0); // 6 -console.log(res1); // null, no byte left to read. EOL - -// text - -console.log(new TextDecoder().decode(data)); // abcdef -``` - -**Output:** - -```text -6 -null -abcdef -``` - -## Writer - -### StringWriter - -Create a `Writer` object for `string`. - -```ts -import { - copyN, - StringReader, - StringWriter, -} from "https://deno.land/std@$STD_VERSION/io/mod.ts"; - -const w = new StringWriter("base"); -const r = new StringReader("0123456789"); -await copyN(r, w, 4); // copy 4 bytes - -// Number of bytes read -console.log(w.toString()); //base0123 - -await Deno.copy(r, w); // copy all -console.log(w.toString()); // base0123456789 -``` - -**Output:** - -```text -base0123 -base0123456789 -``` - -## Streams - -### readerFromStreamReader - -Creates a `Reader` from a `ReadableStreamDefaultReader`. - -```ts -import { readerFromStreamReader } from "https://deno.land/std@$STD_VERSION/io/mod.ts"; -const res = await fetch("https://deno.land"); -const file = await Deno.open("./deno.land.html", { create: true, write: true }); - -const reader = readerFromStreamReader(res.body!.getReader()); -await Deno.copy(reader, file); -file.close(); -``` - -### writerFromStreamWriter - -Creates a `Writer` from a `WritableStreamDefaultWriter`. - -```ts -import { writerFromStreamWriter } from "https://deno.land/std@$STD_VERSION/io/mod.ts"; -const file = await Deno.open("./deno.land.html", { read: true }); - -const writableStream = new WritableStream({ - write(chunk): void { - console.log(chunk); - }, -}); -const writer = writerFromStreamWriter(writableStream.getWriter()); -await Deno.copy(file, writer); -file.close(); -``` diff --git a/std/io/_iotest.ts b/std/io/_iotest.ts deleted file mode 100644 index 5905d2552f..0000000000 --- a/std/io/_iotest.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.// Ported to Deno from -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -type Reader = Deno.Reader; - -/** OneByteReader returns a Reader that implements - * each non-empty Read by reading one byte from r. - */ -export class OneByteReader implements Reader { - constructor(readonly r: Reader) {} - - read(p: Uint8Array): Promise { - if (p.byteLength === 0) { - return Promise.resolve(0); - } - if (!(p instanceof Uint8Array)) { - throw Error("expected Uint8Array"); - } - return Promise.resolve(this.r.read(p.subarray(0, 1))); - } -} - -/** HalfReader returns a Reader that implements Read - * by reading half as many requested bytes from r. - */ -export class HalfReader implements Reader { - constructor(readonly r: Reader) {} - - read(p: Uint8Array): Promise { - if (!(p instanceof Uint8Array)) { - throw Error("expected Uint8Array"); - } - const half = Math.floor((p.byteLength + 1) / 2); - return Promise.resolve(this.r.read(p.subarray(0, half))); - } -} - -/** TimeoutReader returns `Deno.errors.TimedOut` on the second read - * with no data. Subsequent calls to read succeed. - */ -export class TimeoutReader implements Reader { - count = 0; - constructor(readonly r: Reader) {} - - read(p: Uint8Array): Promise { - this.count++; - if (this.count === 2) { - throw new Deno.errors.TimedOut(); - } - return Promise.resolve(this.r.read(p)); - } -} diff --git a/std/io/bufio.ts b/std/io/bufio.ts deleted file mode 100644 index f3b1893cbb..0000000000 --- a/std/io/bufio.ts +++ /dev/null @@ -1,718 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// Based on https://github.com/golang/go/blob/891682/src/bufio/bufio.go -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -type Reader = Deno.Reader; -type Writer = Deno.Writer; -type WriterSync = Deno.WriterSync; -import { copy } from "../bytes/mod.ts"; -import { assert } from "../_util/assert.ts"; - -const DEFAULT_BUF_SIZE = 4096; -const MIN_BUF_SIZE = 16; -const MAX_CONSECUTIVE_EMPTY_READS = 100; -const CR = "\r".charCodeAt(0); -const LF = "\n".charCodeAt(0); - -export class BufferFullError extends Error { - name = "BufferFullError"; - constructor(public partial: Uint8Array) { - super("Buffer full"); - } -} - -export class PartialReadError extends Deno.errors.UnexpectedEof { - name = "PartialReadError"; - partial?: Uint8Array; - constructor() { - super("Encountered UnexpectedEof, data only partially read"); - } -} - -/** Result type returned by of BufReader.readLine(). */ -export interface ReadLineResult { - line: Uint8Array; - more: boolean; -} - -/** BufReader implements buffering for a Reader object. */ -export class BufReader implements Reader { - private buf!: Uint8Array; - private rd!: Reader; // Reader provided by caller. - private r = 0; // buf read position. - private w = 0; // buf write position. - private eof = false; - // private lastByte: number; - // private lastCharSize: number; - - /** return new BufReader unless r is BufReader */ - static create(r: Reader, size: number = DEFAULT_BUF_SIZE): BufReader { - return r instanceof BufReader ? r : new BufReader(r, size); - } - - constructor(rd: Reader, size: number = DEFAULT_BUF_SIZE) { - if (size < MIN_BUF_SIZE) { - size = MIN_BUF_SIZE; - } - this._reset(new Uint8Array(size), rd); - } - - /** Returns the size of the underlying buffer in bytes. */ - size(): number { - return this.buf.byteLength; - } - - buffered(): number { - return this.w - this.r; - } - - // Reads a new chunk into the buffer. - private async _fill(): Promise { - // Slide existing data to beginning. - if (this.r > 0) { - this.buf.copyWithin(0, this.r, this.w); - this.w -= this.r; - this.r = 0; - } - - if (this.w >= this.buf.byteLength) { - throw Error("bufio: tried to fill full buffer"); - } - - // Read new data: try a limited number of times. - for (let i = MAX_CONSECUTIVE_EMPTY_READS; i > 0; i--) { - const rr = await this.rd.read(this.buf.subarray(this.w)); - if (rr === null) { - this.eof = true; - return; - } - assert(rr >= 0, "negative read"); - this.w += rr; - if (rr > 0) { - return; - } - } - - throw new Error( - `No progress after ${MAX_CONSECUTIVE_EMPTY_READS} read() calls`, - ); - } - - /** Discards any buffered data, resets all state, and switches - * the buffered reader to read from r. - */ - reset(r: Reader): void { - this._reset(this.buf, r); - } - - private _reset(buf: Uint8Array, rd: Reader): void { - this.buf = buf; - this.rd = rd; - this.eof = false; - // this.lastByte = -1; - // this.lastCharSize = -1; - } - - /** reads data into p. - * It returns the number of bytes read into p. - * The bytes are taken from at most one Read on the underlying Reader, - * hence n may be less than len(p). - * To read exactly len(p) bytes, use io.ReadFull(b, p). - */ - async read(p: Uint8Array): Promise { - let rr: number | null = p.byteLength; - if (p.byteLength === 0) return rr; - - if (this.r === this.w) { - if (p.byteLength >= this.buf.byteLength) { - // Large read, empty buffer. - // Read directly into p to avoid copy. - const rr = await this.rd.read(p); - const nread = rr ?? 0; - assert(nread >= 0, "negative read"); - // if (rr.nread > 0) { - // this.lastByte = p[rr.nread - 1]; - // this.lastCharSize = -1; - // } - return rr; - } - - // One read. - // Do not use this.fill, which will loop. - this.r = 0; - this.w = 0; - rr = await this.rd.read(this.buf); - if (rr === 0 || rr === null) return rr; - assert(rr >= 0, "negative read"); - this.w += rr; - } - - // copy as much as we can - const copied = copy(this.buf.subarray(this.r, this.w), p, 0); - this.r += copied; - // this.lastByte = this.buf[this.r - 1]; - // this.lastCharSize = -1; - return copied; - } - - /** reads exactly `p.length` bytes into `p`. - * - * If successful, `p` is returned. - * - * If the end of the underlying stream has been reached, and there are no more - * bytes available in the buffer, `readFull()` returns `null` instead. - * - * An error is thrown if some bytes could be read, but not enough to fill `p` - * entirely before the underlying stream reported an error or EOF. Any error - * thrown will have a `partial` property that indicates the slice of the - * buffer that has been successfully filled with data. - * - * Ported from https://golang.org/pkg/io/#ReadFull - */ - async readFull(p: Uint8Array): Promise { - let bytesRead = 0; - while (bytesRead < p.length) { - try { - const rr = await this.read(p.subarray(bytesRead)); - if (rr === null) { - if (bytesRead === 0) { - return null; - } else { - throw new PartialReadError(); - } - } - bytesRead += rr; - } catch (err) { - err.partial = p.subarray(0, bytesRead); - throw err; - } - } - return p; - } - - /** Returns the next byte [0, 255] or `null`. */ - async readByte(): Promise { - while (this.r === this.w) { - if (this.eof) return null; - await this._fill(); // buffer is empty. - } - const c = this.buf[this.r]; - this.r++; - // this.lastByte = c; - return c; - } - - /** readString() reads until the first occurrence of delim in the input, - * returning a string containing the data up to and including the delimiter. - * If ReadString encounters an error before finding a delimiter, - * it returns the data read before the error and the error itself - * (often `null`). - * ReadString returns err != nil if and only if the returned data does not end - * in delim. - * For simple uses, a Scanner may be more convenient. - */ - async readString(delim: string): Promise { - if (delim.length !== 1) { - throw new Error("Delimiter should be a single character"); - } - const buffer = await this.readSlice(delim.charCodeAt(0)); - if (buffer === null) return null; - return new TextDecoder().decode(buffer); - } - - /** `readLine()` is a low-level line-reading primitive. Most callers should - * use `readString('\n')` instead or use a Scanner. - * - * `readLine()` tries to return a single line, not including the end-of-line - * bytes. If the line was too long for the buffer then `more` is set and the - * beginning of the line is returned. The rest of the line will be returned - * from future calls. `more` will be false when returning the last fragment - * of the line. The returned buffer is only valid until the next call to - * `readLine()`. - * - * The text returned from ReadLine does not include the line end ("\r\n" or - * "\n"). - * - * When the end of the underlying stream is reached, the final bytes in the - * stream are returned. No indication or error is given if the input ends - * without a final line end. When there are no more trailing bytes to read, - * `readLine()` returns `null`. - * - * Calling `unreadByte()` after `readLine()` will always unread the last byte - * read (possibly a character belonging to the line end) even if that byte is - * not part of the line returned by `readLine()`. - */ - async readLine(): Promise { - let line: Uint8Array | null; - - try { - line = await this.readSlice(LF); - } catch (err) { - let { partial } = err; - assert( - partial instanceof Uint8Array, - "bufio: caught error from `readSlice()` without `partial` property", - ); - - // Don't throw if `readSlice()` failed with `BufferFullError`, instead we - // just return whatever is available and set the `more` flag. - if (!(err instanceof BufferFullError)) { - throw err; - } - - // Handle the case where "\r\n" straddles the buffer. - if ( - !this.eof && - partial.byteLength > 0 && - partial[partial.byteLength - 1] === CR - ) { - // Put the '\r' back on buf and drop it from line. - // Let the next call to ReadLine check for "\r\n". - assert(this.r > 0, "bufio: tried to rewind past start of buffer"); - this.r--; - partial = partial.subarray(0, partial.byteLength - 1); - } - - return { line: partial, more: !this.eof }; - } - - if (line === null) { - return null; - } - - if (line.byteLength === 0) { - return { line, more: false }; - } - - if (line[line.byteLength - 1] == LF) { - let drop = 1; - if (line.byteLength > 1 && line[line.byteLength - 2] === CR) { - drop = 2; - } - line = line.subarray(0, line.byteLength - drop); - } - return { line, more: false }; - } - - /** `readSlice()` reads until the first occurrence of `delim` in the input, - * returning a slice pointing at the bytes in the buffer. The bytes stop - * being valid at the next read. - * - * If `readSlice()` encounters an error before finding a delimiter, or the - * buffer fills without finding a delimiter, it throws an error with a - * `partial` property that contains the entire buffer. - * - * If `readSlice()` encounters the end of the underlying stream and there are - * any bytes left in the buffer, the rest of the buffer is returned. In other - * words, EOF is always treated as a delimiter. Once the buffer is empty, - * it returns `null`. - * - * Because the data returned from `readSlice()` will be overwritten by the - * next I/O operation, most clients should use `readString()` instead. - */ - async readSlice(delim: number): Promise { - let s = 0; // search start index - let slice: Uint8Array | undefined; - - while (true) { - // Search buffer. - let i = this.buf.subarray(this.r + s, this.w).indexOf(delim); - if (i >= 0) { - i += s; - slice = this.buf.subarray(this.r, this.r + i + 1); - this.r += i + 1; - break; - } - - // EOF? - if (this.eof) { - if (this.r === this.w) { - return null; - } - slice = this.buf.subarray(this.r, this.w); - this.r = this.w; - break; - } - - // Buffer full? - if (this.buffered() >= this.buf.byteLength) { - this.r = this.w; - // #4521 The internal buffer should not be reused across reads because it causes corruption of data. - const oldbuf = this.buf; - const newbuf = this.buf.slice(0); - this.buf = newbuf; - throw new BufferFullError(oldbuf); - } - - s = this.w - this.r; // do not rescan area we scanned before - - // Buffer is not full. - try { - await this._fill(); - } catch (err) { - err.partial = slice; - throw err; - } - } - - // Handle last byte, if any. - // const i = slice.byteLength - 1; - // if (i >= 0) { - // this.lastByte = slice[i]; - // this.lastCharSize = -1 - // } - - return slice; - } - - /** `peek()` returns the next `n` bytes without advancing the reader. The - * bytes stop being valid at the next read call. - * - * When the end of the underlying stream is reached, but there are unread - * bytes left in the buffer, those bytes are returned. If there are no bytes - * left in the buffer, it returns `null`. - * - * If an error is encountered before `n` bytes are available, `peek()` throws - * an error with the `partial` property set to a slice of the buffer that - * contains the bytes that were available before the error occurred. - */ - async peek(n: number): Promise { - if (n < 0) { - throw Error("negative count"); - } - - let avail = this.w - this.r; - while (avail < n && avail < this.buf.byteLength && !this.eof) { - try { - await this._fill(); - } catch (err) { - err.partial = this.buf.subarray(this.r, this.w); - throw err; - } - avail = this.w - this.r; - } - - if (avail === 0 && this.eof) { - return null; - } else if (avail < n && this.eof) { - return this.buf.subarray(this.r, this.r + avail); - } else if (avail < n) { - throw new BufferFullError(this.buf.subarray(this.r, this.w)); - } - - return this.buf.subarray(this.r, this.r + n); - } -} - -abstract class AbstractBufBase { - buf!: Uint8Array; - usedBufferBytes = 0; - err: Error | null = null; - - /** Size returns the size of the underlying buffer in bytes. */ - size(): number { - return this.buf.byteLength; - } - - /** Returns how many bytes are unused in the buffer. */ - available(): number { - return this.buf.byteLength - this.usedBufferBytes; - } - - /** buffered returns the number of bytes that have been written into the - * current buffer. - */ - buffered(): number { - return this.usedBufferBytes; - } -} - -/** BufWriter implements buffering for an deno.Writer object. - * If an error occurs writing to a Writer, no more data will be - * accepted and all subsequent writes, and flush(), will return the error. - * After all data has been written, the client should call the - * flush() method to guarantee all data has been forwarded to - * the underlying deno.Writer. - */ -export class BufWriter extends AbstractBufBase implements Writer { - /** return new BufWriter unless writer is BufWriter */ - static create(writer: Writer, size: number = DEFAULT_BUF_SIZE): BufWriter { - return writer instanceof BufWriter ? writer : new BufWriter(writer, size); - } - - constructor(private writer: Writer, size: number = DEFAULT_BUF_SIZE) { - super(); - if (size <= 0) { - size = DEFAULT_BUF_SIZE; - } - this.buf = new Uint8Array(size); - } - - /** Discards any unflushed buffered data, clears any error, and - * resets buffer to write its output to w. - */ - reset(w: Writer): void { - this.err = null; - this.usedBufferBytes = 0; - this.writer = w; - } - - /** Flush writes any buffered data to the underlying io.Writer. */ - async flush(): Promise { - if (this.err !== null) throw this.err; - if (this.usedBufferBytes === 0) return; - - try { - await Deno.writeAll( - this.writer, - this.buf.subarray(0, this.usedBufferBytes), - ); - } catch (e) { - this.err = e; - throw e; - } - - this.buf = new Uint8Array(this.buf.length); - this.usedBufferBytes = 0; - } - - /** Writes the contents of `data` into the buffer. If the contents won't fully - * fit into the buffer, those bytes that can are copied into the buffer, the - * buffer is the flushed to the writer and the remaining bytes are copied into - * the now empty buffer. - * - * @return the number of bytes written to the buffer. - */ - async write(data: Uint8Array): Promise { - if (this.err !== null) throw this.err; - if (data.length === 0) return 0; - - let totalBytesWritten = 0; - let numBytesWritten = 0; - while (data.byteLength > this.available()) { - if (this.buffered() === 0) { - // Large write, empty buffer. - // Write directly from data to avoid copy. - try { - numBytesWritten = await this.writer.write(data); - } catch (e) { - this.err = e; - throw e; - } - } else { - numBytesWritten = copy(data, this.buf, this.usedBufferBytes); - this.usedBufferBytes += numBytesWritten; - await this.flush(); - } - totalBytesWritten += numBytesWritten; - data = data.subarray(numBytesWritten); - } - - numBytesWritten = copy(data, this.buf, this.usedBufferBytes); - this.usedBufferBytes += numBytesWritten; - totalBytesWritten += numBytesWritten; - return totalBytesWritten; - } -} - -/** BufWriterSync implements buffering for a deno.WriterSync object. - * If an error occurs writing to a WriterSync, no more data will be - * accepted and all subsequent writes, and flush(), will return the error. - * After all data has been written, the client should call the - * flush() method to guarantee all data has been forwarded to - * the underlying deno.WriterSync. - */ -export class BufWriterSync extends AbstractBufBase implements WriterSync { - /** return new BufWriterSync unless writer is BufWriterSync */ - static create( - writer: WriterSync, - size: number = DEFAULT_BUF_SIZE, - ): BufWriterSync { - return writer instanceof BufWriterSync - ? writer - : new BufWriterSync(writer, size); - } - - constructor(private writer: WriterSync, size: number = DEFAULT_BUF_SIZE) { - super(); - if (size <= 0) { - size = DEFAULT_BUF_SIZE; - } - this.buf = new Uint8Array(size); - } - - /** Discards any unflushed buffered data, clears any error, and - * resets buffer to write its output to w. - */ - reset(w: WriterSync): void { - this.err = null; - this.usedBufferBytes = 0; - this.writer = w; - } - - /** Flush writes any buffered data to the underlying io.WriterSync. */ - flush(): void { - if (this.err !== null) throw this.err; - if (this.usedBufferBytes === 0) return; - - try { - Deno.writeAllSync( - this.writer, - this.buf.subarray(0, this.usedBufferBytes), - ); - } catch (e) { - this.err = e; - throw e; - } - - this.buf = new Uint8Array(this.buf.length); - this.usedBufferBytes = 0; - } - - /** Writes the contents of `data` into the buffer. If the contents won't fully - * fit into the buffer, those bytes that can are copied into the buffer, the - * buffer is the flushed to the writer and the remaining bytes are copied into - * the now empty buffer. - * - * @return the number of bytes written to the buffer. - */ - writeSync(data: Uint8Array): number { - if (this.err !== null) throw this.err; - if (data.length === 0) return 0; - - let totalBytesWritten = 0; - let numBytesWritten = 0; - while (data.byteLength > this.available()) { - if (this.buffered() === 0) { - // Large write, empty buffer. - // Write directly from data to avoid copy. - try { - numBytesWritten = this.writer.writeSync(data); - } catch (e) { - this.err = e; - throw e; - } - } else { - numBytesWritten = copy(data, this.buf, this.usedBufferBytes); - this.usedBufferBytes += numBytesWritten; - this.flush(); - } - totalBytesWritten += numBytesWritten; - data = data.subarray(numBytesWritten); - } - - numBytesWritten = copy(data, this.buf, this.usedBufferBytes); - this.usedBufferBytes += numBytesWritten; - totalBytesWritten += numBytesWritten; - return totalBytesWritten; - } -} - -/** Generate longest proper prefix which is also suffix array. */ -function createLPS(pat: Uint8Array): Uint8Array { - const lps = new Uint8Array(pat.length); - lps[0] = 0; - let prefixEnd = 0; - let i = 1; - while (i < lps.length) { - if (pat[i] == pat[prefixEnd]) { - prefixEnd++; - lps[i] = prefixEnd; - i++; - } else if (prefixEnd === 0) { - lps[i] = 0; - i++; - } else { - prefixEnd = pat[prefixEnd - 1]; - } - } - return lps; -} - -/** Read delimited bytes from a Reader. */ -export async function* readDelim( - reader: Reader, - delim: Uint8Array, -): AsyncIterableIterator { - // Avoid unicode problems - const delimLen = delim.length; - const delimLPS = createLPS(delim); - - let inputBuffer = new Deno.Buffer(); - const inspectArr = new Uint8Array(Math.max(1024, delimLen + 1)); - - // Modified KMP - let inspectIndex = 0; - let matchIndex = 0; - while (true) { - const result = await reader.read(inspectArr); - if (result === null) { - // Yield last chunk. - yield inputBuffer.bytes(); - return; - } - if ((result as number) < 0) { - // Discard all remaining and silently fail. - return; - } - const sliceRead = inspectArr.subarray(0, result as number); - await Deno.writeAll(inputBuffer, sliceRead); - - let sliceToProcess = inputBuffer.bytes(); - while (inspectIndex < sliceToProcess.length) { - if (sliceToProcess[inspectIndex] === delim[matchIndex]) { - inspectIndex++; - matchIndex++; - if (matchIndex === delimLen) { - // Full match - const matchEnd = inspectIndex - delimLen; - const readyBytes = sliceToProcess.subarray(0, matchEnd); - // Copy - const pendingBytes = sliceToProcess.slice(inspectIndex); - yield readyBytes; - // Reset match, different from KMP. - sliceToProcess = pendingBytes; - inspectIndex = 0; - matchIndex = 0; - } - } else { - if (matchIndex === 0) { - inspectIndex++; - } else { - matchIndex = delimLPS[matchIndex - 1]; - } - } - } - // Keep inspectIndex and matchIndex. - inputBuffer = new Deno.Buffer(sliceToProcess); - } -} - -/** Read delimited strings from a Reader. */ -export async function* readStringDelim( - reader: Reader, - delim: string, -): AsyncIterableIterator { - const encoder = new TextEncoder(); - const decoder = new TextDecoder(); - for await (const chunk of readDelim(reader, encoder.encode(delim))) { - yield decoder.decode(chunk); - } -} - -/** Read strings line-by-line from a Reader. */ -export async function* readLines( - reader: Reader, -): AsyncIterableIterator { - for await (let chunk of readStringDelim(reader, "\n")) { - // Finding a CR at the end of the line is evidence of a - // "\r\n" at the end of the line. The "\r" part should be - // removed too. - if (chunk.endsWith("\r")) { - chunk = chunk.slice(0, -1); - } - yield chunk; - } -} diff --git a/std/io/bufio_test.ts b/std/io/bufio_test.ts deleted file mode 100644 index d6a0856deb..0000000000 --- a/std/io/bufio_test.ts +++ /dev/null @@ -1,566 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// Based on https://github.com/golang/go/blob/891682/src/bufio/bufio_test.go -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -import { assert, assertEquals, fail } from "../testing/asserts.ts"; -import { - BufferFullError, - BufReader, - BufWriter, - BufWriterSync, - PartialReadError, - ReadLineResult, - readLines, - readStringDelim, -} from "./bufio.ts"; -import * as iotest from "./_iotest.ts"; -import { StringReader } from "./readers.ts"; -import { StringWriter } from "./writers.ts"; -import { copy } from "../bytes/mod.ts"; - -const encoder = new TextEncoder(); - -async function readBytes(buf: BufReader): Promise { - const b = new Uint8Array(1000); - let nb = 0; - while (true) { - const c = await buf.readByte(); - if (c === null) { - break; // EOF - } - b[nb] = c; - nb++; - } - const decoder = new TextDecoder(); - return decoder.decode(b.subarray(0, nb)); -} - -Deno.test("bufioReaderSimple", async function (): Promise { - const data = "hello world"; - const b = new BufReader(new StringReader(data)); - const s = await readBytes(b); - assertEquals(s, data); -}); - -interface ReadMaker { - name: string; - fn: (r: Deno.Reader) => Deno.Reader; -} - -const readMakers: ReadMaker[] = [ - { name: "full", fn: (r): Deno.Reader => r }, - { - name: "byte", - fn: (r): iotest.OneByteReader => new iotest.OneByteReader(r), - }, - { name: "half", fn: (r): iotest.HalfReader => new iotest.HalfReader(r) }, - // TODO(bartlomieju): { name: "data+err", r => new iotest.DataErrReader(r) }, - // { name: "timeout", fn: r => new iotest.TimeoutReader(r) }, -]; - -// Call read to accumulate the text of a file -async function reads(buf: BufReader, m: number): Promise { - const b = new Uint8Array(1000); - let nb = 0; - while (true) { - const result = await buf.read(b.subarray(nb, nb + m)); - if (result === null) { - break; - } - nb += result; - } - const decoder = new TextDecoder(); - return decoder.decode(b.subarray(0, nb)); -} - -interface NamedBufReader { - name: string; - fn: (r: BufReader) => Promise; -} - -const bufreaders: NamedBufReader[] = [ - { name: "1", fn: (b: BufReader): Promise => reads(b, 1) }, - { name: "2", fn: (b: BufReader): Promise => reads(b, 2) }, - { name: "3", fn: (b: BufReader): Promise => reads(b, 3) }, - { name: "4", fn: (b: BufReader): Promise => reads(b, 4) }, - { name: "5", fn: (b: BufReader): Promise => reads(b, 5) }, - { name: "7", fn: (b: BufReader): Promise => reads(b, 7) }, - { name: "bytes", fn: readBytes }, - // { name: "lines", fn: readLines }, -]; - -const MIN_READ_BUFFER_SIZE = 16; -const bufsizes: number[] = [ - 0, - MIN_READ_BUFFER_SIZE, - 23, - 32, - 46, - 64, - 93, - 128, - 1024, - 4096, -]; - -Deno.test("bufioBufReader", async function (): Promise { - const texts = new Array(31); - let str = ""; - let all = ""; - for (let i = 0; i < texts.length - 1; i++) { - texts[i] = str + "\n"; - all += texts[i]; - str += String.fromCharCode((i % 26) + 97); - } - texts[texts.length - 1] = all; - - for (const text of texts) { - for (const readmaker of readMakers) { - for (const bufreader of bufreaders) { - for (const bufsize of bufsizes) { - const read = readmaker.fn(new StringReader(text)); - const buf = new BufReader(read, bufsize); - const s = await bufreader.fn(buf); - const debugStr = `reader=${readmaker.name} ` + - `fn=${bufreader.name} bufsize=${bufsize} want=${text} got=${s}`; - assertEquals(s, text, debugStr); - } - } - } - } -}); - -Deno.test("bufioBufferFull", async function (): Promise { - const longString = - "And now, hello, world! It is the time for all good men to come to the" + - " aid of their party"; - const buf = new BufReader(new StringReader(longString), MIN_READ_BUFFER_SIZE); - const decoder = new TextDecoder(); - - try { - await buf.readSlice("!".charCodeAt(0)); - fail("readSlice should throw"); - } catch (err) { - assert(err instanceof BufferFullError); - assert(err.partial instanceof Uint8Array); - assertEquals(decoder.decode(err.partial), "And now, hello, "); - } - - const line = await buf.readSlice("!".charCodeAt(0)); - assert(line !== null); - const actual = decoder.decode(line); - assertEquals(actual, "world!"); -}); - -Deno.test("bufioReadString", async function (): Promise { - const string = "And now, hello world!"; - const buf = new BufReader(new StringReader(string), MIN_READ_BUFFER_SIZE); - - const line = await buf.readString(","); - assert(line !== null); - assertEquals(line, "And now,"); - assertEquals(line.length, 8); - - const line2 = await buf.readString(","); - assert(line2 !== null); - assertEquals(line2, " hello world!"); - - assertEquals(await buf.readString(","), null); - - try { - await buf.readString("deno"); - - fail("should throw"); - } catch (err) { - assert(err.message, "Delimiter should be a single character"); - } -}); - -const testInput = encoder.encode( - "012\n345\n678\n9ab\ncde\nfgh\nijk\nlmn\nopq\nrst\nuvw\nxy", -); -const testInputrn = encoder.encode( - "012\r\n345\r\n678\r\n9ab\r\ncde\r\nfgh\r\nijk\r\nlmn\r\nopq\r\nrst\r\n" + - "uvw\r\nxy\r\n\n\r\n", -); -const testOutput = encoder.encode("0123456789abcdefghijklmnopqrstuvwxy"); - -// TestReader wraps a Uint8Array and returns reads of a specific length. -class TestReader implements Deno.Reader { - constructor(private data: Uint8Array, private stride: number) {} - - read(buf: Uint8Array): Promise { - let nread = this.stride; - if (nread > this.data.byteLength) { - nread = this.data.byteLength; - } - if (nread > buf.byteLength) { - nread = buf.byteLength; - } - if (nread === 0) { - return Promise.resolve(null); - } - copy(this.data, buf as Uint8Array); - this.data = this.data.subarray(nread); - return Promise.resolve(nread); - } -} - -async function testReadLine(input: Uint8Array): Promise { - for (let stride = 1; stride < 2; stride++) { - let done = 0; - const reader = new TestReader(input, stride); - const l = new BufReader(reader, input.byteLength + 1); - while (true) { - const r = await l.readLine(); - if (r === null) { - break; - } - const { line, more } = r; - assertEquals(more, false); - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands - const want = testOutput.subarray(done, done + line.byteLength); - assertEquals( - line, - want, - `Bad line at stride ${stride}: want: ${want} got: ${line}`, - ); - done += line.byteLength; - } - assertEquals( - done, - testOutput.byteLength, - `readLine didn't return everything: got: ${done}, ` + - `want: ${testOutput} (stride: ${stride})`, - ); - } -} - -Deno.test("bufioReadLine", async function (): Promise { - await testReadLine(testInput); - await testReadLine(testInputrn); -}); - -Deno.test("bufioPeek", async function (): Promise { - const decoder = new TextDecoder(); - const p = new Uint8Array(10); - // string is 16 (minReadBufferSize) long. - const buf = new BufReader( - new StringReader("abcdefghijklmnop"), - MIN_READ_BUFFER_SIZE, - ); - - let actual = await buf.peek(1); - assert(actual !== null); - assertEquals(decoder.decode(actual), "a"); - - actual = await buf.peek(4); - assert(actual !== null); - assertEquals(decoder.decode(actual), "abcd"); - - try { - await buf.peek(32); - fail("peek() should throw"); - } catch (err) { - assert(err instanceof BufferFullError); - assert(err.partial instanceof Uint8Array); - assertEquals(decoder.decode(err.partial), "abcdefghijklmnop"); - } - - await buf.read(p.subarray(0, 3)); - assertEquals(decoder.decode(p.subarray(0, 3)), "abc"); - - actual = await buf.peek(1); - assert(actual !== null); - assertEquals(decoder.decode(actual), "d"); - - actual = await buf.peek(1); - assert(actual !== null); - assertEquals(decoder.decode(actual), "d"); - - actual = await buf.peek(1); - assert(actual !== null); - assertEquals(decoder.decode(actual), "d"); - - actual = await buf.peek(2); - assert(actual !== null); - assertEquals(decoder.decode(actual), "de"); - - const res = await buf.read(p.subarray(0, 3)); - assertEquals(decoder.decode(p.subarray(0, 3)), "def"); - assert(res !== null); - - actual = await buf.peek(4); - assert(actual !== null); - assertEquals(decoder.decode(actual), "ghij"); - - await buf.read(p); - assertEquals(decoder.decode(p), "ghijklmnop"); - - actual = await buf.peek(0); - assert(actual !== null); - assertEquals(decoder.decode(actual), ""); - - const r = await buf.peek(1); - assert(r === null); - /* TODO - Test for issue 3022, not exposing a reader's error on a successful Peek. - buf = NewReaderSize(dataAndEOFReader("abcd"), 32) - if s, err := buf.Peek(2); string(s) != "ab" || err != nil { - t.Errorf(`Peek(2) on "abcd", EOF = %q, %v; want "ab", nil`, string(s), err) - } - if s, err := buf.Peek(4); string(s) != "abcd" || err != nil { - t.Errorf( - `Peek(4) on "abcd", EOF = %q, %v; want "abcd", nil`, - string(s), - err - ) - } - if n, err := buf.Read(p[0:5]); string(p[0:n]) != "abcd" || err != nil { - t.Fatalf("Read after peek = %q, %v; want abcd, EOF", p[0:n], err) - } - if n, err := buf.Read(p[0:1]); string(p[0:n]) != "" || err != io.EOF { - t.Fatalf(`second Read after peek = %q, %v; want "", EOF`, p[0:n], err) - } - */ -}); - -Deno.test("bufioWriter", async function (): Promise { - const data = new Uint8Array(8192); - - for (let i = 0; i < data.byteLength; i++) { - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands - data[i] = " ".charCodeAt(0) + (i % ("~".charCodeAt(0) - " ".charCodeAt(0))); - } - - const w = new Deno.Buffer(); - for (const nwrite of bufsizes) { - for (const bs of bufsizes) { - // Write nwrite bytes using buffer size bs. - // Check that the right amount makes it out - // and that the data is correct. - - w.reset(); - const buf = new BufWriter(w, bs); - - const context = `nwrite=${nwrite} bufsize=${bs}`; - const n = await buf.write(data.subarray(0, nwrite)); - assertEquals(n, nwrite, context); - - await buf.flush(); - - const written = w.bytes(); - assertEquals(written.byteLength, nwrite); - - for (let l = 0; l < written.byteLength; l++) { - assertEquals(written[l], data[l]); - } - } - } -}); - -Deno.test("bufioWriterSync", function (): void { - const data = new Uint8Array(8192); - - for (let i = 0; i < data.byteLength; i++) { - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands - data[i] = " ".charCodeAt(0) + (i % ("~".charCodeAt(0) - " ".charCodeAt(0))); - } - - const w = new Deno.Buffer(); - for (const nwrite of bufsizes) { - for (const bs of bufsizes) { - // Write nwrite bytes using buffer size bs. - // Check that the right amount makes it out - // and that the data is correct. - - w.reset(); - const buf = new BufWriterSync(w, bs); - - const context = `nwrite=${nwrite} bufsize=${bs}`; - const n = buf.writeSync(data.subarray(0, nwrite)); - assertEquals(n, nwrite, context); - - buf.flush(); - - const written = w.bytes(); - assertEquals(written.byteLength, nwrite); - - for (let l = 0; l < written.byteLength; l++) { - assertEquals(written[l], data[l]); - } - } - } -}); - -Deno.test("bufReaderReadFull", async function (): Promise { - const enc = new TextEncoder(); - const dec = new TextDecoder(); - const text = "Hello World"; - const data = new Deno.Buffer(enc.encode(text)); - const bufr = new BufReader(data, 3); - { - const buf = new Uint8Array(6); - const r = await bufr.readFull(buf); - assert(r !== null); - assertEquals(r, buf); - assertEquals(dec.decode(buf), "Hello "); - } - { - const buf = new Uint8Array(6); - try { - await bufr.readFull(buf); - fail("readFull() should throw PartialReadError"); - } catch (err) { - assert(err instanceof PartialReadError); - assert(err.partial instanceof Uint8Array); - assertEquals(err.partial.length, 5); - assertEquals(dec.decode(buf.subarray(0, 5)), "World"); - } - } -}); - -Deno.test("readStringDelimAndLines", async function (): Promise { - const enc = new TextEncoder(); - const data = new Deno.Buffer( - enc.encode("Hello World\tHello World 2\tHello World 3"), - ); - const chunks_ = []; - - for await (const c of readStringDelim(data, "\t")) { - chunks_.push(c); - } - - assertEquals(chunks_.length, 3); - assertEquals(chunks_, ["Hello World", "Hello World 2", "Hello World 3"]); - - const linesData = new Deno.Buffer(enc.encode("0\n1\n2\n3\n4\n5\n6\n7\n8\n9")); - // consider data with windows newlines too - const linesDataWindows = new Deno.Buffer( - enc.encode("0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9"), - ); - const lines_ = []; - - for await (const l of readLines(linesData)) { - lines_.push(l); - } - - assertEquals(lines_.length, 10); - assertEquals(lines_, ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]); - - // Now test for "windows" lines - lines_.length = 0; - for await (const l of readLines(linesDataWindows)) { - lines_.push(l); - } - assertEquals(lines_.length, 10); - assertEquals(lines_, ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]); -}); - -Deno.test( - "bufReaderShouldNotShareArrayBufferAcrossReads", - async function (): Promise { - const decoder = new TextDecoder(); - const data = "abcdefghijklmnopqrstuvwxyz"; - const bufSize = 25; - const b = new BufReader(new StringReader(data), bufSize); - - const r1 = (await b.readLine()) as ReadLineResult; - assert(r1 !== null); - assertEquals(decoder.decode(r1.line), "abcdefghijklmnopqrstuvwxy"); - - const r2 = (await b.readLine()) as ReadLineResult; - assert(r2 !== null); - assertEquals(decoder.decode(r2.line), "z"); - assert( - r1.line.buffer !== r2.line.buffer, - "array buffer should not be shared across reads", - ); - }, -); - -Deno.test({ - name: "Reset buffer after flush", - async fn(): Promise { - const stringWriter = new StringWriter(); - const bufWriter = new BufWriter(stringWriter); - const encoder = new TextEncoder(); - await bufWriter.write(encoder.encode("hello\nworld\nhow\nare\nyou?\n\n")); - await bufWriter.flush(); - await bufWriter.write(encoder.encode("foobar\n\n")); - await bufWriter.flush(); - const actual = stringWriter.toString(); - assertEquals(actual, "hello\nworld\nhow\nare\nyou?\n\nfoobar\n\n"); - }, -}); - -Deno.test({ - name: "Reset buffer after flush sync", - fn(): void { - const stringWriter = new StringWriter(); - const bufWriter = new BufWriterSync(stringWriter); - const encoder = new TextEncoder(); - bufWriter.writeSync(encoder.encode("hello\nworld\nhow\nare\nyou?\n\n")); - bufWriter.flush(); - bufWriter.writeSync(encoder.encode("foobar\n\n")); - bufWriter.flush(); - const actual = stringWriter.toString(); - assertEquals(actual, "hello\nworld\nhow\nare\nyou?\n\nfoobar\n\n"); - }, -}); - -Deno.test({ - name: "BufWriter.flush should write all bytes", - async fn(): Promise { - const bufSize = 16 * 1024; - const data = new Uint8Array(bufSize); - data.fill("a".charCodeAt(0)); - - const cache: Uint8Array[] = []; - const writer: Deno.Writer = { - write(p: Uint8Array): Promise { - cache.push(p.subarray(0, 1)); - - // Writer that only writes 1 byte at a time - return Promise.resolve(1); - }, - }; - - const bufWriter = new BufWriter(writer); - await bufWriter.write(data); - - await bufWriter.flush(); - const buf = new Uint8Array(cache.length); - for (let i = 0; i < cache.length; i++) buf.set(cache[i], i); - - assertEquals(data, buf); - }, -}); - -Deno.test({ - name: "BufWriterSync.flush should write all bytes", - fn(): void { - const bufSize = 16 * 1024; - const data = new Uint8Array(bufSize); - data.fill("a".charCodeAt(0)); - - const cache: Uint8Array[] = []; - const writer: Deno.WriterSync = { - writeSync(p: Uint8Array): number { - cache.push(p.subarray(0, 1)); - // Writer that only writes 1 byte at a time - return 1; - }, - }; - - const bufWriter = new BufWriterSync(writer); - bufWriter.writeSync(data); - - bufWriter.flush(); - const buf = new Uint8Array(cache.length); - for (let i = 0; i < cache.length; i++) buf.set(cache[i], i); - - assertEquals(data, buf); - }, -}); diff --git a/std/io/ioutil.ts b/std/io/ioutil.ts deleted file mode 100644 index d213095d9e..0000000000 --- a/std/io/ioutil.ts +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import type { BufReader } from "./bufio.ts"; -type Reader = Deno.Reader; -type Writer = Deno.Writer; -import { assert } from "../_util/assert.ts"; - -const DEFAULT_BUFFER_SIZE = 32 * 1024; - -/** copy N size at the most. - * If read size is lesser than N, then returns nread - * */ -export async function copyN( - r: Reader, - dest: Writer, - size: number, -): Promise { - let bytesRead = 0; - let buf = new Uint8Array(DEFAULT_BUFFER_SIZE); - while (bytesRead < size) { - if (size - bytesRead < DEFAULT_BUFFER_SIZE) { - buf = new Uint8Array(size - bytesRead); - } - const result = await r.read(buf); - const nread = result ?? 0; - bytesRead += nread; - if (nread > 0) { - let n = 0; - while (n < nread) { - n += await dest.write(buf.slice(n, nread)); - } - assert(n === nread, "could not write"); - } - if (result === null) { - break; - } - } - return bytesRead; -} - -/** Read big endian 16bit short from BufReader */ -export async function readShort(buf: BufReader): Promise { - const high = await buf.readByte(); - if (high === null) return null; - const low = await buf.readByte(); - if (low === null) throw new Deno.errors.UnexpectedEof(); - return (high << 8) | low; -} - -/** Read big endian 32bit integer from BufReader */ -export async function readInt(buf: BufReader): Promise { - const high = await readShort(buf); - if (high === null) return null; - const low = await readShort(buf); - if (low === null) throw new Deno.errors.UnexpectedEof(); - return (high << 16) | low; -} - -const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER); - -/** Read big endian 64bit long from BufReader */ -export async function readLong(buf: BufReader): Promise { - const high = await readInt(buf); - if (high === null) return null; - const low = await readInt(buf); - if (low === null) throw new Deno.errors.UnexpectedEof(); - const big = (BigInt(high) << 32n) | BigInt(low); - // We probably should provide a similar API that returns BigInt values. - if (big > MAX_SAFE_INTEGER) { - throw new RangeError( - "Long value too big to be represented as a JavaScript number.", - ); - } - return Number(big); -} - -/** Slice number into 64bit big endian byte array */ -export function sliceLongToBytes(d: number, dest = new Array(8)): number[] { - let big = BigInt(d); - for (let i = 0; i < 8; i++) { - dest[7 - i] = Number(big & 0xffn); - big >>= 8n; - } - return dest; -} diff --git a/std/io/ioutil_test.ts b/std/io/ioutil_test.ts deleted file mode 100644 index ec94ccc5ba..0000000000 --- a/std/io/ioutil_test.ts +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { - copyN, - readInt, - readLong, - readShort, - sliceLongToBytes, -} from "./ioutil.ts"; -import { StringReader } from "./readers.ts"; -import { BufReader } from "./bufio.ts"; - -class BinaryReader implements Deno.Reader { - index = 0; - - constructor(private bytes: Uint8Array = new Uint8Array(0)) {} - - read(p: Uint8Array): Promise { - p.set(this.bytes.subarray(this.index, p.byteLength)); - this.index += p.byteLength; - return Promise.resolve(p.byteLength); - } -} - -Deno.test("testReadShort", async function (): Promise { - const r = new BinaryReader(new Uint8Array([0x12, 0x34])); - const short = await readShort(new BufReader(r)); - assertEquals(short, 0x1234); -}); - -Deno.test("testReadInt", async function (): Promise { - const r = new BinaryReader(new Uint8Array([0x12, 0x34, 0x56, 0x78])); - const int = await readInt(new BufReader(r)); - assertEquals(int, 0x12345678); -}); - -Deno.test("testReadLong", async function (): Promise { - const r = new BinaryReader( - new Uint8Array([0x00, 0x00, 0x00, 0x78, 0x12, 0x34, 0x56, 0x78]), - ); - const long = await readLong(new BufReader(r)); - assertEquals(long, 0x7812345678); -}); - -Deno.test("testReadLong2", async function (): Promise { - const r = new BinaryReader( - new Uint8Array([0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78]), - ); - const long = await readLong(new BufReader(r)); - assertEquals(long, 0x12345678); -}); - -Deno.test("testSliceLongToBytes", function (): void { - const arr = sliceLongToBytes(0x1234567890abcdef); - const actual = readLong(new BufReader(new BinaryReader(new Uint8Array(arr)))); - const expected = readLong( - new BufReader( - new BinaryReader( - new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef]), - ), - ), - ); - assertEquals(actual, expected); -}); - -Deno.test("testSliceLongToBytes2", function (): void { - const arr = sliceLongToBytes(0x12345678); - assertEquals(arr, [0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78]); -}); - -Deno.test("testCopyN1", async function (): Promise { - const w = new Deno.Buffer(); - const r = new StringReader("abcdefghij"); - const n = await copyN(r, w, 3); - assertEquals(n, 3); - assertEquals(new TextDecoder().decode(w.bytes()), "abc"); -}); - -Deno.test("testCopyN2", async function (): Promise { - const w = new Deno.Buffer(); - const r = new StringReader("abcdefghij"); - const n = await copyN(r, w, 11); - assertEquals(n, 10); - assertEquals(new TextDecoder().decode(w.bytes()), "abcdefghij"); -}); - -Deno.test("copyNWriteAllData", async function (): Promise { - const tmpDir = await Deno.makeTempDir(); - const filepath = `${tmpDir}/data`; - const file = await Deno.open(filepath, { create: true, write: true }); - - const size = 16 * 1024 + 1; - const data = "a".repeat(32 * 1024); - const r = new StringReader(data); - const n = await copyN(r, file, size); // Over max file possible buffer - file.close(); - await Deno.remove(filepath); - - assertEquals(n, size); -}); - -Deno.test("testStringReaderEof", async function (): Promise { - const r = new StringReader("abc"); - assertEquals(await r.read(new Uint8Array()), 0); - assertEquals(await r.read(new Uint8Array(4)), 3); - assertEquals(await r.read(new Uint8Array(1)), null); -}); diff --git a/std/io/mod.ts b/std/io/mod.ts deleted file mode 100644 index 257d3be9ed..0000000000 --- a/std/io/mod.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -export * from "./bufio.ts"; -export * from "./ioutil.ts"; -export * from "./readers.ts"; -export * from "./writers.ts"; -export * from "./streams.ts"; diff --git a/std/io/readers.ts b/std/io/readers.ts deleted file mode 100644 index c3ee2ad144..0000000000 --- a/std/io/readers.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// Based on https://github.com/golang/go/blob/0452f9460f50f0f0aba18df43dc2b31906fb66cc/src/io/io.go -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -import { encode } from "../encoding/utf8.ts"; - -/** Reader utility for strings */ -export class StringReader extends Deno.Buffer { - constructor(s: string) { - super(encode(s).buffer); - } -} - -/** Reader utility for combining multiple readers */ -export class MultiReader implements Deno.Reader { - private readonly readers: Deno.Reader[]; - private currentIndex = 0; - - constructor(...readers: Deno.Reader[]) { - this.readers = readers; - } - - async read(p: Uint8Array): Promise { - const r = this.readers[this.currentIndex]; - if (!r) return null; - const result = await r.read(p); - if (result === null) { - this.currentIndex++; - return 0; - } - return result; - } -} - -/** - * A `LimitedReader` reads from `reader` but limits the amount of data returned to just `limit` bytes. - * Each call to `read` updates `limit` to reflect the new amount remaining. - * `read` returns `null` when `limit` <= `0` or - * when the underlying `reader` returns `null`. - */ -export class LimitedReader implements Deno.Reader { - constructor(public reader: Deno.Reader, public limit: number) {} - - async read(p: Uint8Array): Promise { - if (this.limit <= 0) { - return null; - } - - if (p.length > this.limit) { - p = p.subarray(0, this.limit); - } - const n = await this.reader.read(p); - if (n == null) { - return null; - } - - this.limit -= n; - return n; - } -} diff --git a/std/io/readers_test.ts b/std/io/readers_test.ts deleted file mode 100644 index e29a984b80..0000000000 --- a/std/io/readers_test.ts +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { LimitedReader, MultiReader, StringReader } from "./readers.ts"; -import { StringWriter } from "./writers.ts"; -import { copyN } from "./ioutil.ts"; -import { decode } from "../encoding/utf8.ts"; - -Deno.test("ioStringReader", async function (): Promise { - const r = new StringReader("abcdef"); - const res0 = await r.read(new Uint8Array(6)); - assertEquals(res0, 6); - const res1 = await r.read(new Uint8Array(6)); - assertEquals(res1, null); -}); - -Deno.test("ioStringReader", async function (): Promise { - const r = new StringReader("abcdef"); - const buf = new Uint8Array(3); - const res1 = await r.read(buf); - assertEquals(res1, 3); - assertEquals(decode(buf), "abc"); - const res2 = await r.read(buf); - assertEquals(res2, 3); - assertEquals(decode(buf), "def"); - const res3 = await r.read(buf); - assertEquals(res3, null); - assertEquals(decode(buf), "def"); -}); - -Deno.test("ioMultiReader", async function (): Promise { - const r = new MultiReader(new StringReader("abc"), new StringReader("def")); - const w = new StringWriter(); - const n = await copyN(r, w, 4); - assertEquals(n, 4); - assertEquals(w.toString(), "abcd"); - await Deno.copy(r, w); - assertEquals(w.toString(), "abcdef"); -}); - -Deno.test("ioLimitedReader", async function (): Promise { - let sr = new StringReader("abc"); - let r = new LimitedReader(sr, 2); - let buffer = await Deno.readAll(r); - assertEquals(decode(buffer), "ab"); - assertEquals(decode(await Deno.readAll(sr)), "c"); - sr = new StringReader("abc"); - r = new LimitedReader(sr, 3); - buffer = await Deno.readAll(r); - assertEquals(decode(buffer), "abc"); - assertEquals((await Deno.readAll(r)).length, 0); - sr = new StringReader("abc"); - r = new LimitedReader(sr, 4); - buffer = await Deno.readAll(r); - assertEquals(decode(buffer), "abc"); - assertEquals((await Deno.readAll(r)).length, 0); -}); - -Deno.test("ioLimitedReader", async function (): Promise { - const rb = new StringReader("abc"); - const wb = new StringWriter(); - await Deno.copy(new LimitedReader(rb, -1), wb); - assertEquals(wb.toString(), ""); -}); diff --git a/std/io/streams.ts b/std/io/streams.ts deleted file mode 100644 index 8c726db43f..0000000000 --- a/std/io/streams.ts +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -/** Create a `Writer` from a `WritableStreamDefaultReader`. */ -export function writerFromStreamWriter( - streamWriter: WritableStreamDefaultWriter, -): Deno.Writer { - return { - async write(p: Uint8Array): Promise { - await streamWriter.ready; - await streamWriter.write(p); - return p.length; - }, - }; -} - -/** Create a `Reader` from a `ReadableStreamDefaultReader`. */ -export function readerFromStreamReader( - streamReader: ReadableStreamDefaultReader, -): Deno.Reader { - const buffer = new Deno.Buffer(); - - return { - async read(p: Uint8Array): Promise { - if (buffer.empty()) { - const res = await streamReader.read(); - if (res.done) { - return null; // EOF - } - - await Deno.writeAll(buffer, res.value); - } - - return buffer.read(p); - }, - }; -} - -/** Create a `WritableStream` from a `Writer`. */ -export function writableStreamFromWriter( - writer: Deno.Writer, -): WritableStream { - return new WritableStream({ - async write(chunk) { - await Deno.writeAll(writer, chunk); - }, - }); -} - -/** Create a `ReadableStream` from an `AsyncIterator`. */ -export function readableStreamFromAsyncIterator( - iterator: AsyncIterableIterator, -): ReadableStream { - return new ReadableStream({ - async pull(controller) { - const { value, done } = await iterator.next(); - - if (done) { - controller.close(); - } else { - controller.enqueue(value); - } - }, - }); -} diff --git a/std/io/streams_test.ts b/std/io/streams_test.ts deleted file mode 100644 index 594fc2357c..0000000000 --- a/std/io/streams_test.ts +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { assert, assertEquals } from "../testing/asserts.ts"; -import { - readableStreamFromAsyncIterator, - readerFromStreamReader, - writableStreamFromWriter, - writerFromStreamWriter, -} from "./streams.ts"; - -function repeat(c: string, bytes: number): Uint8Array { - assertEquals(c.length, 1); - const ui8 = new Uint8Array(bytes); - ui8.fill(c.charCodeAt(0)); - return ui8; -} - -Deno.test("toWriterCheck", async function (): Promise { - const written: string[] = []; - const chunks: string[] = ["hello", "deno", "land"]; - const writableStream = new WritableStream({ - write(chunk): void { - const decoder = new TextDecoder(); - written.push(decoder.decode(chunk)); - }, - }); - - const encoder = new TextEncoder(); - const writer = writerFromStreamWriter(writableStream.getWriter()); - - for (const chunk of chunks) { - const n = await writer.write(encoder.encode(chunk)); - // stream writers always write all the bytes - assertEquals(n, chunk.length); - } - - assertEquals(written, chunks); -}); - -Deno.test("toReaderCheck", async function (): Promise { - const chunks: string[] = ["hello", "deno", "land"]; - const expected = chunks.slice(); - const readChunks: Uint8Array[] = []; - const readableStream = new ReadableStream({ - pull(controller): void { - const encoder = new TextEncoder(); - const chunk = chunks.shift(); - if (!chunk) return controller.close(); - controller.enqueue(encoder.encode(chunk)); - }, - }); - - const decoder = new TextDecoder(); - const reader = readerFromStreamReader(readableStream.getReader()); - - let i = 0; - - while (true) { - const b = new Uint8Array(1024); - const n = await reader.read(b); - - if (n === null) break; - - readChunks.push(b.subarray(0, n)); - assert(i < expected.length); - - i++; - } - - assertEquals( - expected, - readChunks.map((chunk) => decoder.decode(chunk)), - ); -}); - -Deno.test("toReaderBigChunksCheck", async function (): Promise { - const bufSize = 1024; - const chunkSize = 3 * bufSize; - const writer = new Deno.Buffer(); - - // A readable stream can enqueue chunks bigger than Copy bufSize - // Reader returned by toReader should enqueue exceeding bytes - const chunks: string[] = [ - "a".repeat(chunkSize), - "b".repeat(chunkSize), - "c".repeat(chunkSize), - ]; - const expected = chunks.slice(); - const readableStream = new ReadableStream({ - pull(controller): void { - const encoder = new TextEncoder(); - const chunk = chunks.shift(); - if (!chunk) return controller.close(); - - controller.enqueue(encoder.encode(chunk)); - }, - }); - - const reader = readerFromStreamReader(readableStream.getReader()); - const n = await Deno.copy(reader, writer, { bufSize }); - - const expectedWritten = chunkSize * expected.length; - assertEquals(n, chunkSize * expected.length); - assertEquals(writer.length, expectedWritten); -}); - -Deno.test("toReaderBigIrregularChunksCheck", async function (): Promise { - const bufSize = 1024; - const chunkSize = 3 * bufSize; - const writer = new Deno.Buffer(); - - // A readable stream can enqueue chunks bigger than Copy bufSize - // Reader returned by toReader should enqueue exceeding bytes - const chunks: Uint8Array[] = [ - repeat("a", chunkSize), - repeat("b", chunkSize + 253), - repeat("c", chunkSize + 8), - ]; - const expected = new Uint8Array( - chunks - .slice() - .map((chunk) => [...chunk]) - .flat(), - ); - const readableStream = new ReadableStream({ - pull(controller): void { - const chunk = chunks.shift(); - if (!chunk) return controller.close(); - - controller.enqueue(chunk); - }, - }); - - const reader = readerFromStreamReader(readableStream.getReader()); - - const n = await Deno.copy(reader, writer, { bufSize }); - assertEquals(n, expected.length); - assertEquals(expected, writer.bytes()); -}); - -Deno.test("toWritableCheck", async function (): Promise { - const written: string[] = []; - const chunks: string[] = ["hello", "deno", "land"]; - const decoder = new TextDecoder(); - - // deno-lint-ignore require-await - async function write(p: Uint8Array): Promise { - written.push(decoder.decode(p)); - return p.length; - } - - const writableStream = writableStreamFromWriter({ write }); - - const encoder = new TextEncoder(); - const streamWriter = writableStream.getWriter(); - for (const chunk of chunks) { - await streamWriter.write(encoder.encode(chunk)); - } - - assertEquals(written, chunks); -}); - -Deno.test("toReadableCheck", async function (): Promise { - const chunks: string[] = ["hello", "deno", "land"]; - const expected = chunks.slice(); - const readChunks: string[] = []; - const encoder = new TextEncoder(); - - // deno-lint-ignore require-await - async function read(p: Uint8Array): Promise { - const chunk = chunks.shift(); - if (chunk === undefined) { - return null; - } else { - const encoded = encoder.encode(chunk); - p.set(encoded); - return encoded.length; - } - } - const iter = Deno.iter({ read }); - const writableStream = readableStreamFromAsyncIterator(iter); - - const decoder = new TextDecoder(); - for await (const chunk of writableStream) { - readChunks.push(decoder.decode(chunk)); - } - - assertEquals(expected, readChunks); -}); diff --git a/std/io/test.ts b/std/io/test.ts deleted file mode 100644 index 590417055f..0000000000 --- a/std/io/test.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import "./mod.ts"; diff --git a/std/io/writers.ts b/std/io/writers.ts deleted file mode 100644 index a9ce821806..0000000000 --- a/std/io/writers.ts +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -type Writer = Deno.Writer; -type WriterSync = Deno.WriterSync; -import { decode, encode } from "../encoding/utf8.ts"; - -/** Writer utility for buffering string chunks */ -export class StringWriter implements Writer, WriterSync { - private chunks: Uint8Array[] = []; - private byteLength = 0; - private cache: string | undefined; - - constructor(private base: string = "") { - const c = encode(base); - this.chunks.push(c); - this.byteLength += c.byteLength; - } - - write(p: Uint8Array): Promise { - return Promise.resolve(this.writeSync(p)); - } - - writeSync(p: Uint8Array): number { - this.chunks.push(p); - this.byteLength += p.byteLength; - this.cache = undefined; - return p.byteLength; - } - - toString(): string { - if (this.cache) { - return this.cache; - } - const buf = new Uint8Array(this.byteLength); - let offs = 0; - for (const chunk of this.chunks) { - buf.set(chunk, offs); - offs += chunk.byteLength; - } - this.cache = decode(buf); - return this.cache; - } -} diff --git a/std/io/writers_test.ts b/std/io/writers_test.ts deleted file mode 100644 index 7135bfff34..0000000000 --- a/std/io/writers_test.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { StringWriter } from "./writers.ts"; -import { StringReader } from "./readers.ts"; -import { copyN } from "./ioutil.ts"; - -Deno.test("ioStringWriter", async function (): Promise { - const w = new StringWriter("base"); - const r = new StringReader("0123456789"); - await copyN(r, w, 4); - assertEquals(w.toString(), "base0123"); - await Deno.copy(r, w); - assertEquals(w.toString(), "base0123456789"); -}); - -Deno.test("ioStringWriterSync", function (): void { - const encoder = new TextEncoder(); - const w = new StringWriter(""); - w.writeSync(encoder.encode("deno")); - assertEquals(w.toString(), "deno"); - w.writeSync(encoder.encode("\nland")); - assertEquals(w.toString(), "deno\nland"); -}); diff --git a/std/log/README.md b/std/log/README.md deleted file mode 100644 index 1208f5ba99..0000000000 --- a/std/log/README.md +++ /dev/null @@ -1,316 +0,0 @@ -# Log - -## Usage - -```ts -import * as log from "https://deno.land/std@$STD_VERSION/log/mod.ts"; - -// Simple default logger out of the box. You can customize it -// by overriding logger and handler named "default", or providing -// additional logger configurations. You can log any data type. -log.debug("Hello world"); -log.info(123456); -log.warning(true); -log.error({ foo: "bar", fizz: "bazz" }); -log.critical("500 Internal server error"); - -// custom configuration with 2 loggers (the default and `tasks` loggers). -await log.setup({ - handlers: { - console: new log.handlers.ConsoleHandler("DEBUG"), - - file: new log.handlers.FileHandler("WARNING", { - filename: "./log.txt", - // you can change format of output message using any keys in `LogRecord`. - formatter: "{levelName} {msg}", - }), - }, - - loggers: { - // configure default logger available via short-hand methods above. - default: { - level: "DEBUG", - handlers: ["console", "file"], - }, - - tasks: { - level: "ERROR", - handlers: ["console"], - }, - }, -}); - -let logger; - -// get default logger. -logger = log.getLogger(); -logger.debug("fizz"); // logs to `console`, because `file` handler requires "WARNING" level. -logger.warning(41256); // logs to both `console` and `file` handlers. - -// get custom logger -logger = log.getLogger("tasks"); -logger.debug("fizz"); // won't get output because this logger has "ERROR" level. -logger.error({ productType: "book", value: "126.11" }); // log to `console`. - -// if you try to use a logger that hasn't been configured -// you're good to go, it gets created automatically with level set to 0 -// so no message is logged. -const unknownLogger = log.getLogger("mystery"); -unknownLogger.info("foobar"); // no-op -``` - -## Advanced usage - -### Loggers - -Loggers are objects that you interact with. When you use a logger method it -constructs a `LogRecord` and passes it down to its handlers for output. To -create custom loggers, specify them in `loggers` when calling `log.setup`. - -#### `LogRecord` - -`LogRecord` is an object that encapsulates provided message and arguments as -well some meta data that can be later used when formatting a message. - -```ts -class LogRecord { - readonly msg: string; - readonly args: any[]; - readonly datetime: Date; - readonly level: number; - readonly levelName: string; - readonly loggerName: string; -} -``` - -### Log Levels - -The different log levels are exported in the `LogLevels` enum type. It defaults -to INFO if none is specified. - -### Handlers - -Handlers are responsible for actual output of log messages. When a handler is -called by a logger, it firstly checks that `LogRecord`'s level is not lower than -level of the handler. If level check passes, handlers formats log record into -string and outputs it to target. - -`log` module comes with three built-in handlers: - -#### `ConsoleHandler` - -This is the default logger. It will output color coded log messages to the -console via `console.log()`. This logger takes `HandlerOptions`: - -```typescript -type FormatterFunction = (logRecord: LogRecord) => string; - -interface HandlerOptions { - formatter?: string | FormatterFunction; //see `Custom message format` below -} -``` - -#### `FileHandler` - -This handler will output to a file using an optional mode (default is `a`, e.g. -append). The file will grow indefinitely. It uses a buffer for writing to file. -Logs can be manually flushed with `fileHandler.flush()`. Log messages with a log -level greater than error are immediately flushed. Logs are also flushed on -process completion. This logger takes `FileOptions`: - -```typescript -interface FileHandlerOptions { - formatter?: string | FormatterFunction; //see `Custom message format` below - filename: string; - mode?: LogMode; // 'a', 'w', 'x' -} -``` - -Behavior of the log modes is as follows: - -- `'a'` - Default mode. Appends new log messages to the end of an existing log - file, or create a new log file if none exists. -- `'w'` - Upon creation of the handler, any existing log file will be removed - and a new one created. -- `'x'` - This will create a new log file and throw an error if one already - exists. - -This handler requires `--allow-write` permission on the log file. - -#### `RotatingFileHandler` - -This handler extends the functionality of the `FileHandler` by "rotating" the -log file when it reaches a certain size. `maxBytes` specifies the maximum size -in bytes that the log file can grow to before rolling over to a new one. If the -size of the new log message plus the current log file size exceeds `maxBytes` -then a roll over is triggered. When a roll over occurs, before the log message -is written, the log file is renamed and appended with `.1`. If a `.1` version -already existed, it would have been renamed `.2` first and so on. The maximum -number of log files to keep is specified by `maxBackupCount`. After the renames -are complete the log message is written to the original, now blank, file. - -Example: Given `log.txt`, `log.txt.1`, `log.txt.2` and `log.txt.3`, a -`maxBackupCount` of 3 and a new log message which would cause `log.txt` to -exceed `maxBytes`, then `log.txt.2` would be renamed to `log.txt.3` (thereby -discarding the original contents of `log.txt.3` since 3 is the maximum number of -backups to keep), `log.txt.1` would be renamed to `log.txt.2`, `log.txt` would -be renamed to `log.txt.1` and finally `log.txt` would be created from scratch -where the new log message would be written. - -This handler uses a buffer for writing log messages to file. Logs can be -manually flushed with `fileHandler.flush()`. Log messages with a log level -greater than ERROR are immediately flushed. Logs are also flushed on process -completion. - -Options for this handler are: - -```typescript -interface RotatingFileHandlerOptions { - maxBytes: number; - maxBackupCount: number; - formatter?: string | FormatterFunction; //see `Custom message format` below - filename: string; - mode?: LogMode; // 'a', 'w', 'x' -} -``` - -Additional notes on `mode` as described above: - -- `'a'` Default mode. As above, this will pick up where the logs left off in - rotation, or create a new log file if it doesn't exist. -- `'w'` in addition to starting with a clean `filename`, this mode will also - cause any existing backups (up to `maxBackupCount`) to be deleted on setup - giving a fully clean slate. -- `'x'` requires that neither `filename`, nor any backups (up to - `maxBackupCount`), exist before setup. - -This handler requires both `--allow-read` and `--allow-write` permissions on the -log files. - -### Custom message format - -If you want to override default format of message you can define `formatter` -option for handler. It can be either simple string-based format that uses -`LogRecord` fields or more complicated function-based one that takes `LogRecord` -as argument and outputs string. - -The default log format is `{levelName} {msg}`. - -Eg. - -```ts -await log.setup({ - handlers: { - stringFmt: new log.handlers.ConsoleHandler("DEBUG", { - formatter: "[{levelName}] {msg}" - }), - - functionFmt: new log.handlers.ConsoleHandler("DEBUG", { - formatter: logRecord => { - let msg = `${logRecord.level} ${logRecord.msg}`; - - logRecord.args.forEach((arg, index) => { - msg += `, arg${index}: ${arg}`; - }); - - return msg; - } - }), - - anotherFmt: new log.handlers.ConsoleHandler("DEBUG", { - formatter: "[{loggerName}] - {levelName} {msg}" - }), - }, - - loggers: { - default: { - level: "DEBUG", - handlers: ["stringFmt", "functionFmt"], - }, - dataLogger: { - level: "INFO", - handlers: ["anotherFmt"], - } - } -}) - -// calling: -log.debug("Hello, world!", 1, "two", [3, 4, 5]); -// results in: -[DEBUG] Hello, world! // output from "stringFmt" handler. -10 Hello, world!, arg0: 1, arg1: two, arg3: [3, 4, 5] // output from "functionFmt" formatter. - -// calling: -log.getLogger("dataLogger").error("oh no!"); -// results in: -[dataLogger] - ERROR oh no! // output from anotherFmt handler. -``` - -#### Custom handlers - -Custom handlers can be implemented by subclassing `BaseHandler` or -`WriterHandler`. - -`BaseHandler` is bare-bones handler that has no output logic at all, - -`WriterHandler` is an abstract class that supports any target with `Writer` -interface. - -During setup async hooks `setup` and `destroy` are called, you can use them to -open and close file/HTTP connection or any other action you might need. - -For examples check source code of `FileHandler` and `TestHandler`. - -### Inline Logging - -Log functions return the data passed in the `msg` parameter. Data is returned -regardless if the logger actually logs it. - -```ts -const stringData: string = logger.debug("hello world"); -const booleanData: boolean = logger.debug(true, 1, "abc"); -const fn = (): number => { - return 123; -}; -const resolvedFunctionData: number = logger.debug(fn()); -console.log(stringData); // 'hello world' -console.log(booleanData); // true -console.log(resolvedFunctionData); // 123 -``` - -### Lazy Log Evaluation - -Some log statements are expensive to compute. In these cases, you can use lazy -log evaluation to prevent the computation taking place if the logger won't log -the message. - -```ts -// `expensiveFn(5)` is only evaluated if this logger is configured for debug logging. -logger.debug(() => `this is expensive: ${expensiveFn(5)}`); -``` - -> NOTE: When using lazy log evaluation, `undefined` will be returned if the -> resolver function is not called because the logger won't log it. It is an -> antipattern use lazy evaluation with inline logging because the return value -> depends on the current log level. - -Example: - -```ts -await log.setup({ - handlers: { - console: new log.handlers.ConsoleHandler("DEBUG"), - }, - - loggers: { - tasks: { - level: "ERROR", - handlers: ["console"], - }, - }, -}); - -// not logged, as debug < error. -const data: string | undefined = logger.debug(() => someExpenseFn(5, true)); -console.log(data); // undefined -``` diff --git a/std/log/handlers.ts b/std/log/handlers.ts deleted file mode 100644 index acfb166417..0000000000 --- a/std/log/handlers.ts +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { getLevelByName, LevelName, LogLevels } from "./levels.ts"; -import type { LogRecord } from "./logger.ts"; -import { blue, bold, red, yellow } from "../fmt/colors.ts"; -import { exists, existsSync } from "../fs/exists.ts"; -import { BufWriterSync } from "../io/bufio.ts"; - -const DEFAULT_FORMATTER = "{levelName} {msg}"; -type FormatterFunction = (logRecord: LogRecord) => string; -type LogMode = "a" | "w" | "x"; - -interface HandlerOptions { - formatter?: string | FormatterFunction; -} - -export class BaseHandler { - level: number; - levelName: LevelName; - formatter: string | FormatterFunction; - - constructor(levelName: LevelName, options: HandlerOptions = {}) { - this.level = getLevelByName(levelName); - this.levelName = levelName; - - this.formatter = options.formatter || DEFAULT_FORMATTER; - } - - handle(logRecord: LogRecord): void { - if (this.level > logRecord.level) return; - - const msg = this.format(logRecord); - return this.log(msg); - } - - format(logRecord: LogRecord): string { - if (this.formatter instanceof Function) { - return this.formatter(logRecord); - } - - return this.formatter.replace(/{(\S+)}/g, (match, p1): string => { - const value = logRecord[p1 as keyof LogRecord]; - - // do not interpolate missing values - if (value == null) { - return match; - } - - return String(value); - }); - } - - log(_msg: string): void {} - async setup(): Promise {} - async destroy(): Promise {} -} - -export class ConsoleHandler extends BaseHandler { - format(logRecord: LogRecord): string { - let msg = super.format(logRecord); - - switch (logRecord.level) { - case LogLevels.INFO: - msg = blue(msg); - break; - case LogLevels.WARNING: - msg = yellow(msg); - break; - case LogLevels.ERROR: - msg = red(msg); - break; - case LogLevels.CRITICAL: - msg = bold(red(msg)); - break; - default: - break; - } - - return msg; - } - - log(msg: string): void { - console.log(msg); - } -} - -export abstract class WriterHandler extends BaseHandler { - protected _writer!: Deno.Writer; - #encoder = new TextEncoder(); - - abstract log(msg: string): void; -} - -interface FileHandlerOptions extends HandlerOptions { - filename: string; - mode?: LogMode; -} - -export class FileHandler extends WriterHandler { - protected _file: Deno.File | undefined; - protected _buf!: BufWriterSync; - protected _filename: string; - protected _mode: LogMode; - protected _openOptions: Deno.OpenOptions; - protected _encoder = new TextEncoder(); - #unloadCallback = (): Promise => this.destroy(); - - constructor(levelName: LevelName, options: FileHandlerOptions) { - super(levelName, options); - this._filename = options.filename; - // default to append mode, write only - this._mode = options.mode ? options.mode : "a"; - this._openOptions = { - createNew: this._mode === "x", - create: this._mode !== "x", - append: this._mode === "a", - truncate: this._mode !== "a", - write: true, - }; - } - - async setup(): Promise { - this._file = await Deno.open(this._filename, this._openOptions); - this._writer = this._file; - this._buf = new BufWriterSync(this._file); - - addEventListener("unload", this.#unloadCallback); - } - - handle(logRecord: LogRecord): void { - super.handle(logRecord); - - // Immediately flush if log level is higher than ERROR - if (logRecord.level > LogLevels.ERROR) { - this.flush(); - } - } - - log(msg: string): void { - this._buf.writeSync(this._encoder.encode(msg + "\n")); - } - - flush(): void { - if (this._buf?.buffered() > 0) { - this._buf.flush(); - } - } - - destroy(): Promise { - this.flush(); - this._file?.close(); - this._file = undefined; - removeEventListener("unload", this.#unloadCallback); - return Promise.resolve(); - } -} - -interface RotatingFileHandlerOptions extends FileHandlerOptions { - maxBytes: number; - maxBackupCount: number; -} - -export class RotatingFileHandler extends FileHandler { - #maxBytes: number; - #maxBackupCount: number; - #currentFileSize = 0; - - constructor(levelName: LevelName, options: RotatingFileHandlerOptions) { - super(levelName, options); - this.#maxBytes = options.maxBytes; - this.#maxBackupCount = options.maxBackupCount; - } - - async setup(): Promise { - if (this.#maxBytes < 1) { - this.destroy(); - throw new Error("maxBytes cannot be less than 1"); - } - if (this.#maxBackupCount < 1) { - this.destroy(); - throw new Error("maxBackupCount cannot be less than 1"); - } - await super.setup(); - - if (this._mode === "w") { - // Remove old backups too as it doesn't make sense to start with a clean - // log file, but old backups - for (let i = 1; i <= this.#maxBackupCount; i++) { - if (await exists(this._filename + "." + i)) { - await Deno.remove(this._filename + "." + i); - } - } - } else if (this._mode === "x") { - // Throw if any backups also exist - for (let i = 1; i <= this.#maxBackupCount; i++) { - if (await exists(this._filename + "." + i)) { - this.destroy(); - throw new Deno.errors.AlreadyExists( - "Backup log file " + this._filename + "." + i + " already exists", - ); - } - } - } else { - this.#currentFileSize = (await Deno.stat(this._filename)).size; - } - } - - log(msg: string): void { - const msgByteLength = this._encoder.encode(msg).byteLength + 1; - - if (this.#currentFileSize + msgByteLength > this.#maxBytes) { - this.rotateLogFiles(); - this.#currentFileSize = 0; - } - - this._buf.writeSync(this._encoder.encode(msg + "\n")); - this.#currentFileSize += msgByteLength; - } - - rotateLogFiles(): void { - this._buf.flush(); - Deno.close(this._file!.rid); - - for (let i = this.#maxBackupCount - 1; i >= 0; i--) { - const source = this._filename + (i === 0 ? "" : "." + i); - const dest = this._filename + "." + (i + 1); - - if (existsSync(source)) { - Deno.renameSync(source, dest); - } - } - - this._file = Deno.openSync(this._filename, this._openOptions); - this._writer = this._file; - this._buf = new BufWriterSync(this._file); - } -} diff --git a/std/log/handlers_test.ts b/std/log/handlers_test.ts deleted file mode 100644 index a8e3a3df22..0000000000 --- a/std/log/handlers_test.ts +++ /dev/null @@ -1,541 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertNotEquals, - assertThrowsAsync, -} from "../testing/asserts.ts"; -import { - getLevelByName, - getLevelName, - LevelName, - LogLevelNames, - LogLevels, -} from "./levels.ts"; -import { BaseHandler, FileHandler, RotatingFileHandler } from "./handlers.ts"; -import { LogRecord } from "./logger.ts"; -import { existsSync } from "../fs/exists.ts"; - -const LOG_FILE = "./test_log.file"; - -class TestHandler extends BaseHandler { - public messages: string[] = []; - - public log(str: string): void { - this.messages.push(str); - } -} - -Deno.test("simpleHandler", function (): void { - const cases = new Map([ - [ - LogLevels.DEBUG, - [ - "DEBUG debug-test", - "INFO info-test", - "WARNING warning-test", - "ERROR error-test", - "CRITICAL critical-test", - ], - ], - [ - LogLevels.INFO, - [ - "INFO info-test", - "WARNING warning-test", - "ERROR error-test", - "CRITICAL critical-test", - ], - ], - [ - LogLevels.WARNING, - ["WARNING warning-test", "ERROR error-test", "CRITICAL critical-test"], - ], - [LogLevels.ERROR, ["ERROR error-test", "CRITICAL critical-test"]], - [LogLevels.CRITICAL, ["CRITICAL critical-test"]], - ]); - - for (const [testCase, messages] of cases.entries()) { - const testLevel = getLevelName(testCase); - const handler = new TestHandler(testLevel); - - for (const levelName of LogLevelNames) { - const level = getLevelByName(levelName as LevelName); - handler.handle( - new LogRecord({ - msg: `${levelName.toLowerCase()}-test`, - args: [], - level: level, - loggerName: "default", - }), - ); - } - - assertEquals(handler.level, testCase); - assertEquals(handler.levelName, testLevel); - assertEquals(handler.messages, messages); - } -}); - -Deno.test("testFormatterAsString", function (): void { - const handler = new TestHandler("DEBUG", { - formatter: "test {levelName} {msg}", - }); - - handler.handle( - new LogRecord({ - msg: "Hello, world!", - args: [], - level: LogLevels.DEBUG, - loggerName: "default", - }), - ); - - assertEquals(handler.messages, ["test DEBUG Hello, world!"]); -}); - -Deno.test("testFormatterWithEmptyMsg", function () { - const handler = new TestHandler("DEBUG", { - formatter: "test {levelName} {msg}", - }); - - handler.handle( - new LogRecord({ - msg: "", - args: [], - level: LogLevels.DEBUG, - loggerName: "default", - }), - ); - - assertEquals(handler.messages, ["test DEBUG "]); -}); - -Deno.test("testFormatterAsFunction", function (): void { - const handler = new TestHandler("DEBUG", { - formatter: (logRecord): string => - `fn formatter ${logRecord.levelName} ${logRecord.msg}`, - }); - - handler.handle( - new LogRecord({ - msg: "Hello, world!", - args: [], - level: LogLevels.ERROR, - loggerName: "default", - }), - ); - - assertEquals(handler.messages, ["fn formatter ERROR Hello, world!"]); -}); - -Deno.test({ - name: "FileHandler with mode 'w' will wipe clean existing log file", - async fn() { - const fileHandler = new FileHandler("WARNING", { - filename: LOG_FILE, - mode: "w", - }); - - await fileHandler.setup(); - fileHandler.handle( - new LogRecord({ - msg: "Hello World", - args: [], - level: LogLevels.WARNING, - loggerName: "default", - }), - ); - await fileHandler.destroy(); - const firstFileSize = (await Deno.stat(LOG_FILE)).size; - - await fileHandler.setup(); - fileHandler.handle( - new LogRecord({ - msg: "Hello World", - args: [], - level: LogLevels.WARNING, - loggerName: "default", - }), - ); - await fileHandler.destroy(); - const secondFileSize = (await Deno.stat(LOG_FILE)).size; - - assertEquals(secondFileSize, firstFileSize); - Deno.removeSync(LOG_FILE); - }, -}); - -Deno.test({ - name: "FileHandler with mode 'x' will throw if log file already exists", - async fn() { - const fileHandler = new FileHandler("WARNING", { - filename: LOG_FILE, - mode: "x", - }); - Deno.writeFileSync(LOG_FILE, new TextEncoder().encode("hello world")); - - await assertThrowsAsync(async () => { - await fileHandler.setup(); - }, Deno.errors.AlreadyExists); - - await fileHandler.destroy(); - - Deno.removeSync(LOG_FILE); - }, -}); - -Deno.test({ - name: - "RotatingFileHandler with mode 'w' will wipe clean existing log file and remove others", - async fn() { - Deno.writeFileSync(LOG_FILE, new TextEncoder().encode("hello world")); - Deno.writeFileSync( - LOG_FILE + ".1", - new TextEncoder().encode("hello world"), - ); - Deno.writeFileSync( - LOG_FILE + ".2", - new TextEncoder().encode("hello world"), - ); - Deno.writeFileSync( - LOG_FILE + ".3", - new TextEncoder().encode("hello world"), - ); - - const fileHandler = new RotatingFileHandler("WARNING", { - filename: LOG_FILE, - maxBytes: 50, - maxBackupCount: 3, - mode: "w", - }); - await fileHandler.setup(); - await fileHandler.destroy(); - - assertEquals((await Deno.stat(LOG_FILE)).size, 0); - assert(!existsSync(LOG_FILE + ".1")); - assert(!existsSync(LOG_FILE + ".2")); - assert(!existsSync(LOG_FILE + ".3")); - - Deno.removeSync(LOG_FILE); - }, -}); - -Deno.test({ - name: - "RotatingFileHandler with mode 'x' will throw if any log file already exists", - async fn() { - Deno.writeFileSync( - LOG_FILE + ".3", - new TextEncoder().encode("hello world"), - ); - const fileHandler = new RotatingFileHandler("WARNING", { - filename: LOG_FILE, - maxBytes: 50, - maxBackupCount: 3, - mode: "x", - }); - await assertThrowsAsync( - async () => { - await fileHandler.setup(); - }, - Deno.errors.AlreadyExists, - "Backup log file " + LOG_FILE + ".3 already exists", - ); - - fileHandler.destroy(); - Deno.removeSync(LOG_FILE + ".3"); - Deno.removeSync(LOG_FILE); - }, -}); - -Deno.test({ - name: "RotatingFileHandler with first rollover, monitor step by step", - async fn() { - const fileHandler = new RotatingFileHandler("WARNING", { - filename: LOG_FILE, - maxBytes: 25, - maxBackupCount: 3, - mode: "w", - }); - await fileHandler.setup(); - - fileHandler.handle( - new LogRecord({ - msg: "AAA", - args: [], - level: LogLevels.ERROR, - loggerName: "default", - }), - ); // 'ERROR AAA\n' = 10 bytes - fileHandler.flush(); - assertEquals((await Deno.stat(LOG_FILE)).size, 10); - fileHandler.handle( - new LogRecord({ - msg: "AAA", - args: [], - level: LogLevels.ERROR, - loggerName: "default", - }), - ); - fileHandler.flush(); - assertEquals((await Deno.stat(LOG_FILE)).size, 20); - fileHandler.handle( - new LogRecord({ - msg: "AAA", - args: [], - level: LogLevels.ERROR, - loggerName: "default", - }), - ); - fileHandler.flush(); - // Rollover occurred. Log file now has 1 record, rollover file has the original 2 - assertEquals((await Deno.stat(LOG_FILE)).size, 10); - assertEquals((await Deno.stat(LOG_FILE + ".1")).size, 20); - await fileHandler.destroy(); - - Deno.removeSync(LOG_FILE); - Deno.removeSync(LOG_FILE + ".1"); - }, -}); - -Deno.test({ - name: "RotatingFileHandler with first rollover, check all at once", - async fn() { - const fileHandler = new RotatingFileHandler("WARNING", { - filename: LOG_FILE, - maxBytes: 25, - maxBackupCount: 3, - mode: "w", - }); - await fileHandler.setup(); - - fileHandler.handle( - new LogRecord({ - msg: "AAA", - args: [], - level: LogLevels.ERROR, - loggerName: "default", - }), - ); // 'ERROR AAA\n' = 10 bytes - fileHandler.handle( - new LogRecord({ - msg: "AAA", - args: [], - level: LogLevels.ERROR, - loggerName: "default", - }), - ); - fileHandler.handle( - new LogRecord({ - msg: "AAA", - args: [], - level: LogLevels.ERROR, - loggerName: "default", - }), - ); - - await fileHandler.destroy(); - - assertEquals((await Deno.stat(LOG_FILE)).size, 10); - assertEquals((await Deno.stat(LOG_FILE + ".1")).size, 20); - - Deno.removeSync(LOG_FILE); - Deno.removeSync(LOG_FILE + ".1"); - }, -}); - -Deno.test({ - name: "RotatingFileHandler with all backups rollover", - async fn() { - Deno.writeFileSync(LOG_FILE, new TextEncoder().encode("original log file")); - Deno.writeFileSync( - LOG_FILE + ".1", - new TextEncoder().encode("original log.1 file"), - ); - Deno.writeFileSync( - LOG_FILE + ".2", - new TextEncoder().encode("original log.2 file"), - ); - Deno.writeFileSync( - LOG_FILE + ".3", - new TextEncoder().encode("original log.3 file"), - ); - - const fileHandler = new RotatingFileHandler("WARNING", { - filename: LOG_FILE, - maxBytes: 2, - maxBackupCount: 3, - mode: "a", - }); - await fileHandler.setup(); - fileHandler.handle( - new LogRecord({ - msg: "AAA", - args: [], - level: LogLevels.ERROR, - loggerName: "default", - }), - ); // 'ERROR AAA\n' = 10 bytes - await fileHandler.destroy(); - - const decoder = new TextDecoder(); - assertEquals(decoder.decode(Deno.readFileSync(LOG_FILE)), "ERROR AAA\n"); - assertEquals( - decoder.decode(Deno.readFileSync(LOG_FILE + ".1")), - "original log file", - ); - assertEquals( - decoder.decode(Deno.readFileSync(LOG_FILE + ".2")), - "original log.1 file", - ); - assertEquals( - decoder.decode(Deno.readFileSync(LOG_FILE + ".3")), - "original log.2 file", - ); - assert(!existsSync(LOG_FILE + ".4")); - - Deno.removeSync(LOG_FILE); - Deno.removeSync(LOG_FILE + ".1"); - Deno.removeSync(LOG_FILE + ".2"); - Deno.removeSync(LOG_FILE + ".3"); - }, -}); - -Deno.test({ - name: "RotatingFileHandler maxBytes cannot be less than 1", - async fn() { - await assertThrowsAsync( - async () => { - const fileHandler = new RotatingFileHandler("WARNING", { - filename: LOG_FILE, - maxBytes: 0, - maxBackupCount: 3, - mode: "w", - }); - await fileHandler.setup(); - }, - Error, - "maxBytes cannot be less than 1", - ); - }, -}); - -Deno.test({ - name: "RotatingFileHandler maxBackupCount cannot be less than 1", - async fn() { - await assertThrowsAsync( - async () => { - const fileHandler = new RotatingFileHandler("WARNING", { - filename: LOG_FILE, - maxBytes: 50, - maxBackupCount: 0, - mode: "w", - }); - await fileHandler.setup(); - }, - Error, - "maxBackupCount cannot be less than 1", - ); - }, -}); - -Deno.test({ - name: "Window unload flushes buffer", - async fn() { - const fileHandler = new FileHandler("WARNING", { - filename: LOG_FILE, - mode: "w", - }); - await fileHandler.setup(); - fileHandler.handle( - new LogRecord({ - msg: "AAA", - args: [], - level: LogLevels.ERROR, - loggerName: "default", - }), - ); // 'ERROR AAA\n' = 10 bytes - - assertEquals((await Deno.stat(LOG_FILE)).size, 0); - dispatchEvent(new Event("unload")); - assertEquals((await Deno.stat(LOG_FILE)).size, 10); - - Deno.removeSync(LOG_FILE); - }, -}); - -Deno.test({ - name: "RotatingFileHandler: rotate on byte length, not msg length", - async fn() { - const fileHandler = new RotatingFileHandler("WARNING", { - filename: LOG_FILE, - maxBytes: 7, - maxBackupCount: 1, - mode: "w", - }); - await fileHandler.setup(); - - const msg = "。"; - const msgLength = msg.length; - const msgByteLength = new TextEncoder().encode(msg).byteLength; - assertNotEquals(msgLength, msgByteLength); - assertEquals(msgLength, 1); - assertEquals(msgByteLength, 3); - - fileHandler.log(msg); // logs 4 bytes (including '\n') - fileHandler.log(msg); // max bytes is 7, but this would be 8. Rollover. - - await fileHandler.destroy(); - - const fileSize1 = (await Deno.stat(LOG_FILE)).size; - const fileSize2 = (await Deno.stat(LOG_FILE + ".1")).size; - - assertEquals(fileSize1, msgByteLength + 1); - assertEquals(fileSize2, msgByteLength + 1); - - Deno.removeSync(LOG_FILE); - Deno.removeSync(LOG_FILE + ".1"); - }, -}); - -Deno.test({ - name: "FileHandler: Critical logs trigger immediate flush", - async fn() { - const fileHandler = new FileHandler("WARNING", { - filename: LOG_FILE, - mode: "w", - }); - await fileHandler.setup(); - - fileHandler.handle( - new LogRecord({ - msg: "AAA", - args: [], - level: LogLevels.ERROR, - loggerName: "default", - }), - ); - - // ERROR won't trigger immediate flush - const fileSize = (await Deno.stat(LOG_FILE)).size; - assertEquals(fileSize, 0); - - fileHandler.handle( - new LogRecord({ - msg: "AAA", - args: [], - level: LogLevels.CRITICAL, - loggerName: "default", - }), - ); - - // CRITICAL will trigger immediate flush - const fileSize2 = (await Deno.stat(LOG_FILE)).size; - // ERROR record is 10 bytes, CRITICAL is 13 bytes - assertEquals(fileSize2, 23); - - await fileHandler.destroy(); - Deno.removeSync(LOG_FILE); - }, -}); diff --git a/std/log/levels.ts b/std/log/levels.ts deleted file mode 100644 index 6b748e9928..0000000000 --- a/std/log/levels.ts +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -/** Get log level numeric values through enum constants - */ -export enum LogLevels { - NOTSET = 0, - DEBUG = 10, - INFO = 20, - WARNING = 30, - ERROR = 40, - CRITICAL = 50, -} - -/** Permitted log level names */ -export const LogLevelNames = Object.keys(LogLevels).filter((key) => - isNaN(Number(key)) -); - -/** Union of valid log level strings */ -export type LevelName = keyof typeof LogLevels; - -const byLevel: Record = { - [String(LogLevels.NOTSET)]: "NOTSET", - [String(LogLevels.DEBUG)]: "DEBUG", - [String(LogLevels.INFO)]: "INFO", - [String(LogLevels.WARNING)]: "WARNING", - [String(LogLevels.ERROR)]: "ERROR", - [String(LogLevels.CRITICAL)]: "CRITICAL", -}; - -/** Returns the numeric log level associated with the passed, - * stringy log level name. - */ -export function getLevelByName(name: LevelName): number { - switch (name) { - case "NOTSET": - return LogLevels.NOTSET; - case "DEBUG": - return LogLevels.DEBUG; - case "INFO": - return LogLevels.INFO; - case "WARNING": - return LogLevels.WARNING; - case "ERROR": - return LogLevels.ERROR; - case "CRITICAL": - return LogLevels.CRITICAL; - default: - throw new Error(`no log level found for "${name}"`); - } -} - -/** Returns the stringy log level name provided the numeric log level */ -export function getLevelName(level: number): LevelName { - const levelName = byLevel[level]; - if (levelName) { - return levelName; - } - throw new Error(`no level name found for level: ${level}`); -} diff --git a/std/log/logger.ts b/std/log/logger.ts deleted file mode 100644 index bd8d8cce58..0000000000 --- a/std/log/logger.ts +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { getLevelByName, getLevelName, LogLevels } from "./levels.ts"; -import type { LevelName } from "./levels.ts"; -import type { BaseHandler } from "./handlers.ts"; - -// deno-lint-ignore no-explicit-any -export type GenericFunction = (...args: any[]) => any; - -export interface LogRecordOptions { - msg: string; - args: unknown[]; - level: number; - loggerName: string; -} - -export class LogRecord { - readonly msg: string; - #args: unknown[]; - #datetime: Date; - readonly level: number; - readonly levelName: string; - readonly loggerName: string; - - constructor(options: LogRecordOptions) { - this.msg = options.msg; - this.#args = [...options.args]; - this.level = options.level; - this.loggerName = options.loggerName; - this.#datetime = new Date(); - this.levelName = getLevelName(options.level); - } - get args(): unknown[] { - return [...this.#args]; - } - get datetime(): Date { - return new Date(this.#datetime.getTime()); - } -} - -export interface LoggerOptions { - handlers?: BaseHandler[]; -} - -export class Logger { - #level: LogLevels; - #handlers: BaseHandler[]; - readonly #loggerName: string; - - constructor( - loggerName: string, - levelName: LevelName, - options: LoggerOptions = {}, - ) { - this.#loggerName = loggerName; - this.#level = getLevelByName(levelName); - this.#handlers = options.handlers || []; - } - - get level(): LogLevels { - return this.#level; - } - set level(level: LogLevels) { - this.#level = level; - } - - get levelName(): LevelName { - return getLevelName(this.#level); - } - set levelName(levelName: LevelName) { - this.#level = getLevelByName(levelName); - } - - get loggerName(): string { - return this.#loggerName; - } - - set handlers(hndls: BaseHandler[]) { - this.#handlers = hndls; - } - get handlers(): BaseHandler[] { - return this.#handlers; - } - - /** If the level of the logger is greater than the level to log, then nothing - * is logged, otherwise a log record is passed to each log handler. `msg` data - * passed in is returned. If a function is passed in, it is only evaluated - * if the msg will be logged and the return value will be the result of the - * function, not the function itself, unless the function isn't called, in which - * case undefined is returned. All types are coerced to strings for logging. - */ - private _log( - level: number, - msg: (T extends GenericFunction ? never : T) | (() => T), - ...args: unknown[] - ): T | undefined { - if (this.level > level) { - return msg instanceof Function ? undefined : msg; - } - - let fnResult: T | undefined; - let logMessage: string; - if (msg instanceof Function) { - fnResult = msg(); - logMessage = this.asString(fnResult); - } else { - logMessage = this.asString(msg); - } - const record: LogRecord = new LogRecord({ - msg: logMessage, - args: args, - level: level, - loggerName: this.loggerName, - }); - - this.#handlers.forEach((handler): void => { - handler.handle(record); - }); - - return msg instanceof Function ? fnResult : msg; - } - - asString(data: unknown): string { - if (typeof data === "string") { - return data; - } else if ( - data === null || - typeof data === "number" || - typeof data === "bigint" || - typeof data === "boolean" || - typeof data === "undefined" || - typeof data === "symbol" - ) { - return String(data); - } else if (data instanceof Error) { - return data.stack!; - } else if (typeof data === "object") { - return JSON.stringify(data); - } - return "undefined"; - } - - debug(msg: () => T, ...args: unknown[]): T | undefined; - debug(msg: T extends GenericFunction ? never : T, ...args: unknown[]): T; - debug( - msg: (T extends GenericFunction ? never : T) | (() => T), - ...args: unknown[] - ): T | undefined { - return this._log(LogLevels.DEBUG, msg, ...args); - } - - info(msg: () => T, ...args: unknown[]): T | undefined; - info(msg: T extends GenericFunction ? never : T, ...args: unknown[]): T; - info( - msg: (T extends GenericFunction ? never : T) | (() => T), - ...args: unknown[] - ): T | undefined { - return this._log(LogLevels.INFO, msg, ...args); - } - - warning(msg: () => T, ...args: unknown[]): T | undefined; - warning(msg: T extends GenericFunction ? never : T, ...args: unknown[]): T; - warning( - msg: (T extends GenericFunction ? never : T) | (() => T), - ...args: unknown[] - ): T | undefined { - return this._log(LogLevels.WARNING, msg, ...args); - } - - error(msg: () => T, ...args: unknown[]): T | undefined; - error(msg: T extends GenericFunction ? never : T, ...args: unknown[]): T; - error( - msg: (T extends GenericFunction ? never : T) | (() => T), - ...args: unknown[] - ): T | undefined { - return this._log(LogLevels.ERROR, msg, ...args); - } - - critical(msg: () => T, ...args: unknown[]): T | undefined; - critical( - msg: T extends GenericFunction ? never : T, - ...args: unknown[] - ): T; - critical( - msg: (T extends GenericFunction ? never : T) | (() => T), - ...args: unknown[] - ): T | undefined { - return this._log(LogLevels.CRITICAL, msg, ...args); - } -} diff --git a/std/log/logger_test.ts b/std/log/logger_test.ts deleted file mode 100644 index bfaa653c9a..0000000000 --- a/std/log/logger_test.ts +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals, assertMatch } from "../testing/asserts.ts"; -import { Logger, LogRecord } from "./logger.ts"; -import { LevelName, LogLevels } from "./levels.ts"; -import { BaseHandler } from "./handlers.ts"; - -class TestHandler extends BaseHandler { - public messages: string[] = []; - public records: LogRecord[] = []; - - handle(record: LogRecord): void { - this.records.push(record); - super.handle(record); - } - - public log(str: string): void { - this.messages.push(str); - } -} - -Deno.test({ - name: "Logger names can be output in logs", - fn() { - const handlerNoName = new TestHandler("DEBUG"); - const handlerWithLoggerName = new TestHandler("DEBUG", { - formatter: "[{loggerName}] {levelName} {msg}", - }); - - const logger = new Logger("config", "DEBUG", { - handlers: [handlerNoName, handlerWithLoggerName], - }); - logger.debug("hello"); - assertEquals(handlerNoName.messages[0], "DEBUG hello"); - assertEquals(handlerWithLoggerName.messages[0], "[config] DEBUG hello"); - }, -}); - -Deno.test("simpleLogger", function (): void { - const handler = new TestHandler("DEBUG"); - let logger = new Logger("default", "DEBUG"); - - assertEquals(logger.level, LogLevels.DEBUG); - assertEquals(logger.levelName, "DEBUG"); - assertEquals(logger.handlers, []); - - logger = new Logger("default", "DEBUG", { handlers: [handler] }); - - assertEquals(logger.handlers, [handler]); -}); - -Deno.test("customHandler", function (): void { - const handler = new TestHandler("DEBUG"); - const logger = new Logger("default", "DEBUG", { handlers: [handler] }); - - const inlineData: string = logger.debug("foo", 1, 2); - - const record = handler.records[0]; - assertEquals(record.msg, "foo"); - assertEquals(record.args, [1, 2]); - assertEquals(record.level, LogLevels.DEBUG); - assertEquals(record.levelName, "DEBUG"); - - assertEquals(handler.messages, ["DEBUG foo"]); - assertEquals(inlineData!, "foo"); -}); - -Deno.test("logFunctions", function (): void { - const doLog = (level: LevelName): TestHandler => { - const handler = new TestHandler(level); - const logger = new Logger("default", level, { handlers: [handler] }); - const debugData = logger.debug("foo"); - const infoData = logger.info("bar"); - const warningData = logger.warning("baz"); - const errorData = logger.error("boo"); - const criticalData = logger.critical("doo"); - assertEquals(debugData, "foo"); - assertEquals(infoData, "bar"); - assertEquals(warningData, "baz"); - assertEquals(errorData, "boo"); - assertEquals(criticalData, "doo"); - return handler; - }; - - let handler: TestHandler; - handler = doLog("DEBUG"); - - assertEquals(handler.messages, [ - "DEBUG foo", - "INFO bar", - "WARNING baz", - "ERROR boo", - "CRITICAL doo", - ]); - - handler = doLog("INFO"); - - assertEquals(handler.messages, [ - "INFO bar", - "WARNING baz", - "ERROR boo", - "CRITICAL doo", - ]); - - handler = doLog("WARNING"); - - assertEquals(handler.messages, ["WARNING baz", "ERROR boo", "CRITICAL doo"]); - - handler = doLog("ERROR"); - - assertEquals(handler.messages, ["ERROR boo", "CRITICAL doo"]); - - handler = doLog("CRITICAL"); - - assertEquals(handler.messages, ["CRITICAL doo"]); -}); - -Deno.test( - "String resolver fn will not execute if msg will not be logged", - function (): void { - const handler = new TestHandler("ERROR"); - const logger = new Logger("default", "ERROR", { handlers: [handler] }); - let called = false; - - const expensiveFunction = (): string => { - called = true; - return "expensive function result"; - }; - - const inlineData: string | undefined = logger.debug( - expensiveFunction, - 1, - 2, - ); - assert(!called); - assertEquals(inlineData, undefined); - }, -); - -Deno.test("String resolver fn resolves as expected", function (): void { - const handler = new TestHandler("ERROR"); - const logger = new Logger("default", "ERROR", { handlers: [handler] }); - const expensiveFunction = (x: number): string => { - return "expensive function result " + x; - }; - - const firstInlineData = logger.error(() => expensiveFunction(5)); - const secondInlineData = logger.error(() => expensiveFunction(12), 1, "abc"); - assertEquals(firstInlineData, "expensive function result 5"); - assertEquals(secondInlineData, "expensive function result 12"); -}); - -Deno.test( - "All types map correctly to log strings and are returned as is", - function (): void { - const handler = new TestHandler("DEBUG"); - const logger = new Logger("default", "DEBUG", { handlers: [handler] }); - const sym = Symbol(); - const syma = Symbol("a"); - const fn = (): string => { - return "abc"; - }; - - // string - const data1: string = logger.debug("abc"); - assertEquals(data1, "abc"); - const data2: string = logger.debug("def", 1); - assertEquals(data2, "def"); - assertEquals(handler.messages[0], "DEBUG abc"); - assertEquals(handler.messages[1], "DEBUG def"); - - // null - const data3: null = logger.info(null); - assertEquals(data3, null); - const data4: null = logger.info(null, 1); - assertEquals(data4, null); - assertEquals(handler.messages[2], "INFO null"); - assertEquals(handler.messages[3], "INFO null"); - - // number - const data5: number = logger.warning(3); - assertEquals(data5, 3); - const data6: number = logger.warning(3, 1); - assertEquals(data6, 3); - assertEquals(handler.messages[4], "WARNING 3"); - assertEquals(handler.messages[5], "WARNING 3"); - - // bigint - const data7: bigint = logger.error(5n); - assertEquals(data7, 5n); - const data8: bigint = logger.error(5n, 1); - assertEquals(data8, 5n); - assertEquals(handler.messages[6], "ERROR 5"); - assertEquals(handler.messages[7], "ERROR 5"); - - // boolean - const data9: boolean = logger.critical(true); - assertEquals(data9, true); - const data10: boolean = logger.critical(false, 1); - assertEquals(data10, false); - assertEquals(handler.messages[8], "CRITICAL true"); - assertEquals(handler.messages[9], "CRITICAL false"); - - // undefined - const data11: undefined = logger.debug(undefined); - assertEquals(data11, undefined); - const data12: undefined = logger.debug(undefined, 1); - assertEquals(data12, undefined); - assertEquals(handler.messages[10], "DEBUG undefined"); - assertEquals(handler.messages[11], "DEBUG undefined"); - - // symbol - const data13: symbol = logger.info(sym); - assertEquals(data13, sym); - const data14: symbol = logger.info(syma, 1); - assertEquals(data14, syma); - assertEquals(handler.messages[12], "INFO Symbol()"); - assertEquals(handler.messages[13], "INFO Symbol(a)"); - - // function - const data15: string | undefined = logger.warning(fn); - assertEquals(data15, "abc"); - const data16: string | undefined = logger.warning(fn, 1); - assertEquals(data16, "abc"); - assertEquals(handler.messages[14], "WARNING abc"); - assertEquals(handler.messages[15], "WARNING abc"); - - // object - const data17: { payload: string; other: number } = logger.error({ - payload: "data", - other: 123, - }); - assertEquals(data17, { - payload: "data", - other: 123, - }); - const data18: { payload: string; other: number } = logger.error( - { payload: "data", other: 123 }, - 1, - ); - assertEquals(data18, { - payload: "data", - other: 123, - }); - assertEquals(handler.messages[16], 'ERROR {"payload":"data","other":123}'); - assertEquals(handler.messages[17], 'ERROR {"payload":"data","other":123}'); - - // error - const error = new RangeError("Uh-oh!"); - const data19: RangeError = logger.error(error); - assertEquals(data19, error); - const messages19 = handler.messages[18].split("\n"); - assertEquals(messages19[0], `ERROR ${error.name}: ${error.message}`); - assertMatch(messages19[1], /^\s+at file:.*\d+:\d+$/); - }, -); diff --git a/std/log/mod.ts b/std/log/mod.ts deleted file mode 100644 index 31fc9aaee2..0000000000 --- a/std/log/mod.ts +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { Logger } from "./logger.ts"; -import type { GenericFunction } from "./logger.ts"; -import { - BaseHandler, - ConsoleHandler, - FileHandler, - RotatingFileHandler, - WriterHandler, -} from "./handlers.ts"; -import { assert } from "../_util/assert.ts"; -import type { LevelName } from "./levels.ts"; - -export { LogLevels } from "./levels.ts"; -export type { LevelName } from "./levels.ts"; -export { Logger } from "./logger.ts"; - -export class LoggerConfig { - level?: LevelName; - handlers?: string[]; -} - -export interface LogConfig { - handlers?: { - [name: string]: BaseHandler; - }; - loggers?: { - [name: string]: LoggerConfig; - }; -} - -const DEFAULT_LEVEL = "INFO"; -const DEFAULT_CONFIG: LogConfig = { - handlers: { - default: new ConsoleHandler(DEFAULT_LEVEL), - }, - - loggers: { - default: { - level: DEFAULT_LEVEL, - handlers: ["default"], - }, - }, -}; - -const state = { - handlers: new Map(), - loggers: new Map(), - config: DEFAULT_CONFIG, -}; - -export const handlers = { - BaseHandler, - ConsoleHandler, - WriterHandler, - FileHandler, - RotatingFileHandler, -}; - -/** Get a logger instance. If not specified `name`, get the default logger. */ -export function getLogger(name?: string): Logger { - if (!name) { - const d = state.loggers.get("default"); - assert( - d != null, - `"default" logger must be set for getting logger without name`, - ); - return d; - } - const result = state.loggers.get(name); - if (!result) { - const logger = new Logger(name, "NOTSET", { handlers: [] }); - state.loggers.set(name, logger); - return logger; - } - return result; -} - -/** Log with debug level, using default logger. */ -export function debug(msg: () => T, ...args: unknown[]): T | undefined; -export function debug( - msg: T extends GenericFunction ? never : T, - ...args: unknown[] -): T; -export function debug( - msg: (T extends GenericFunction ? never : T) | (() => T), - ...args: unknown[] -): T | undefined { - // Assist TS compiler with pass-through generic type - if (msg instanceof Function) { - return getLogger("default").debug(msg, ...args); - } - return getLogger("default").debug(msg, ...args); -} - -/** Log with info level, using default logger. */ -export function info(msg: () => T, ...args: unknown[]): T | undefined; -export function info( - msg: T extends GenericFunction ? never : T, - ...args: unknown[] -): T; -export function info( - msg: (T extends GenericFunction ? never : T) | (() => T), - ...args: unknown[] -): T | undefined { - // Assist TS compiler with pass-through generic type - if (msg instanceof Function) { - return getLogger("default").info(msg, ...args); - } - return getLogger("default").info(msg, ...args); -} - -/** Log with warning level, using default logger. */ -export function warning(msg: () => T, ...args: unknown[]): T | undefined; -export function warning( - msg: T extends GenericFunction ? never : T, - ...args: unknown[] -): T; -export function warning( - msg: (T extends GenericFunction ? never : T) | (() => T), - ...args: unknown[] -): T | undefined { - // Assist TS compiler with pass-through generic type - if (msg instanceof Function) { - return getLogger("default").warning(msg, ...args); - } - return getLogger("default").warning(msg, ...args); -} - -/** Log with error level, using default logger. */ -export function error(msg: () => T, ...args: unknown[]): T | undefined; -export function error( - msg: T extends GenericFunction ? never : T, - ...args: unknown[] -): T; -export function error( - msg: (T extends GenericFunction ? never : T) | (() => T), - ...args: unknown[] -): T | undefined { - // Assist TS compiler with pass-through generic type - if (msg instanceof Function) { - return getLogger("default").error(msg, ...args); - } - return getLogger("default").error(msg, ...args); -} - -/** Log with critical level, using default logger. */ -export function critical(msg: () => T, ...args: unknown[]): T | undefined; -export function critical( - msg: T extends GenericFunction ? never : T, - ...args: unknown[] -): T; -export function critical( - msg: (T extends GenericFunction ? never : T) | (() => T), - ...args: unknown[] -): T | undefined { - // Assist TS compiler with pass-through generic type - if (msg instanceof Function) { - return getLogger("default").critical(msg, ...args); - } - return getLogger("default").critical(msg, ...args); -} - -/** Setup logger config. */ -export async function setup(config: LogConfig): Promise { - state.config = { - handlers: { ...DEFAULT_CONFIG.handlers, ...config.handlers }, - loggers: { ...DEFAULT_CONFIG.loggers, ...config.loggers }, - }; - - // tear down existing handlers - state.handlers.forEach((handler): void => { - handler.destroy(); - }); - state.handlers.clear(); - - // setup handlers - const handlers = state.config.handlers || {}; - - for (const handlerName in handlers) { - const handler = handlers[handlerName]; - await handler.setup(); - state.handlers.set(handlerName, handler); - } - - // remove existing loggers - state.loggers.clear(); - - // setup loggers - const loggers = state.config.loggers || {}; - for (const loggerName in loggers) { - const loggerConfig = loggers[loggerName]; - const handlerNames = loggerConfig.handlers || []; - const handlers: BaseHandler[] = []; - - handlerNames.forEach((handlerName): void => { - const handler = state.handlers.get(handlerName); - if (handler) { - handlers.push(handler); - } - }); - - const levelName = loggerConfig.level || DEFAULT_LEVEL; - const logger = new Logger(loggerName, levelName, { handlers: handlers }); - state.loggers.set(loggerName, logger); - } -} - -await setup(DEFAULT_CONFIG); diff --git a/std/log/mod_test.ts b/std/log/mod_test.ts deleted file mode 100644 index 7823042e75..0000000000 --- a/std/log/mod_test.ts +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "../testing/asserts.ts"; -import { - critical, - debug, - error, - getLogger, - info, - LevelName, - Logger, - LogLevels, - setup, - warning, -} from "./mod.ts"; -import { BaseHandler } from "./handlers.ts"; - -class TestHandler extends BaseHandler { - public messages: string[] = []; - - public log(str: string): void { - this.messages.push(str); - } -} - -let logger: Logger | null = null; -try { - // Need to initialize it here - // otherwise it will be already initialized on Deno.test - logger = getLogger(); -} catch { - // Pass -} - -Deno.test("logger is initialized", function (): void { - assert(logger instanceof Logger); -}); - -Deno.test("default loggers work as expected", function (): void { - const sym = Symbol("a"); - const debugData: string = debug("foo"); - const debugResolver: string | undefined = debug(() => "foo"); - const infoData: number = info(456, 1, 2, 3); - const infoResolver: boolean | undefined = info(() => true); - const warningData: symbol = warning(sym); - const warningResolver: null | undefined = warning(() => null); - const errorData: undefined = error(undefined, 1, 2, 3); - const errorResolver: bigint | undefined = error(() => 5n); - const criticalData: string = critical("foo"); - const criticalResolver: string | undefined = critical(() => "bar"); - assertEquals(debugData, "foo"); - assertEquals(debugResolver, undefined); - assertEquals(infoData, 456); - assertEquals(infoResolver, true); - assertEquals(warningData, sym); - assertEquals(warningResolver, null); - assertEquals(errorData, undefined); - assertEquals(errorResolver, 5n); - assertEquals(criticalData, "foo"); - assertEquals(criticalResolver, "bar"); -}); - -Deno.test({ - name: "Logging config works as expected with logger names", - async fn() { - const consoleHandler = new TestHandler("DEBUG"); - const anotherConsoleHandler = new TestHandler("DEBUG", { - formatter: "[{loggerName}] {levelName} {msg}", - }); - await setup({ - handlers: { - console: consoleHandler, - anotherConsole: anotherConsoleHandler, - }, - - loggers: { - // configure default logger available via short-hand methods above - default: { - level: "DEBUG", - handlers: ["console"], - }, - - tasks: { - level: "ERROR", - handlers: ["anotherConsole"], - }, - }, - }); - getLogger().debug("hello"); - getLogger("tasks").error("world"); - assertEquals(consoleHandler.messages[0], "DEBUG hello"); - assertEquals(anotherConsoleHandler.messages[0], "[tasks] ERROR world"); - }, -}); - -Deno.test({ - name: "Loggers have level and levelName to get/set loglevels", - async fn() { - const testHandler = new TestHandler("DEBUG"); - await setup({ - handlers: { - test: testHandler, - }, - - loggers: { - // configure default logger available via short-hand methods above - default: { - level: "DEBUG", - handlers: ["test"], - }, - }, - }); - const logger: Logger = getLogger(); - assertEquals(logger.levelName, "DEBUG"); - assertEquals(logger.level, LogLevels.DEBUG); - - logger.debug("debug"); - logger.error("error"); - logger.critical("critical"); - assertEquals(testHandler.messages.length, 3); - assertEquals(testHandler.messages[0], "DEBUG debug"); - assertEquals(testHandler.messages[1], "ERROR error"); - assertEquals(testHandler.messages[2], "CRITICAL critical"); - - testHandler.messages = []; - logger.level = LogLevels.WARNING; - assertEquals(logger.levelName, "WARNING"); - assertEquals(logger.level, LogLevels.WARNING); - - logger.debug("debug2"); - logger.error("error2"); - logger.critical("critical2"); - assertEquals(testHandler.messages.length, 2); - assertEquals(testHandler.messages[0], "ERROR error2"); - assertEquals(testHandler.messages[1], "CRITICAL critical2"); - - testHandler.messages = []; - const setLevelName: LevelName = "CRITICAL"; - logger.levelName = setLevelName; - assertEquals(logger.levelName, "CRITICAL"); - assertEquals(logger.level, LogLevels.CRITICAL); - - logger.debug("debug3"); - logger.error("error3"); - logger.critical("critical3"); - assertEquals(testHandler.messages.length, 1); - assertEquals(testHandler.messages[0], "CRITICAL critical3"); - }, -}); - -Deno.test({ - name: "Loggers have loggerName to get logger name", - async fn() { - const testHandler = new TestHandler("DEBUG"); - await setup({ - handlers: { - test: testHandler, - }, - - loggers: { - namedA: { - level: "DEBUG", - handlers: ["test"], - }, - namedB: { - level: "DEBUG", - handlers: ["test"], - }, - }, - }); - - assertEquals(getLogger("namedA").loggerName, "namedA"); - assertEquals(getLogger("namedB").loggerName, "namedB"); - assertEquals(getLogger().loggerName, "default"); - assertEquals(getLogger("nonsetupname").loggerName, "nonsetupname"); - }, -}); - -Deno.test({ - name: "Logger has mutable handlers", - async fn() { - const testHandlerA = new TestHandler("DEBUG"); - const testHandlerB = new TestHandler("DEBUG"); - await setup({ - handlers: { - testA: testHandlerA, - testB: testHandlerB, - }, - - loggers: { - default: { - level: "DEBUG", - handlers: ["testA"], - }, - }, - }); - const logger: Logger = getLogger(); - logger.info("msg1"); - assertEquals(testHandlerA.messages.length, 1); - assertEquals(testHandlerA.messages[0], "INFO msg1"); - assertEquals(testHandlerB.messages.length, 0); - - logger.handlers = [testHandlerA, testHandlerB]; - - logger.info("msg2"); - assertEquals(testHandlerA.messages.length, 2); - assertEquals(testHandlerA.messages[1], "INFO msg2"); - assertEquals(testHandlerB.messages.length, 1); - assertEquals(testHandlerB.messages[0], "INFO msg2"); - - logger.handlers = [testHandlerB]; - - logger.info("msg3"); - assertEquals(testHandlerA.messages.length, 2); - assertEquals(testHandlerB.messages.length, 2); - assertEquals(testHandlerB.messages[1], "INFO msg3"); - - logger.handlers = []; - logger.info("msg4"); - assertEquals(testHandlerA.messages.length, 2); - assertEquals(testHandlerB.messages.length, 2); - }, -}); diff --git a/std/log/test.ts b/std/log/test.ts deleted file mode 100644 index 1966824e15..0000000000 --- a/std/log/test.ts +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals, assertThrows } from "../testing/asserts.ts"; -import * as log from "./mod.ts"; -import { - getLevelByName, - getLevelName, - LevelName, - LogLevelNames, -} from "./levels.ts"; - -class TestHandler extends log.handlers.BaseHandler { - public messages: string[] = []; - - log(msg: string): void { - this.messages.push(msg); - } -} - -Deno.test("defaultHandlers", async function (): Promise { - const loggers: { - [key: string]: (msg: string, ...args: unknown[]) => void; - } = { - DEBUG: log.debug, - INFO: log.info, - WARNING: log.warning, - ERROR: log.error, - CRITICAL: log.critical, - }; - - for (const levelName of LogLevelNames) { - if (levelName === "NOTSET") { - continue; - } - - const logger = loggers[levelName]; - const handler = new TestHandler(levelName as LevelName); - - await log.setup({ - handlers: { - default: handler, - }, - loggers: { - default: { - level: levelName as LevelName, - handlers: ["default"], - }, - }, - }); - - logger("foo"); - logger("bar", 1, 2); - - assertEquals(handler.messages, [`${levelName} foo`, `${levelName} bar`]); - } -}); - -Deno.test("getLogger", async function (): Promise { - const handler = new TestHandler("DEBUG"); - - await log.setup({ - handlers: { - default: handler, - }, - loggers: { - default: { - level: "DEBUG", - handlers: ["default"], - }, - }, - }); - - const logger = log.getLogger(); - - assertEquals(logger.levelName, "DEBUG"); - assertEquals(logger.handlers, [handler]); -}); - -Deno.test("getLoggerWithName", async function (): Promise { - const fooHandler = new TestHandler("DEBUG"); - - await log.setup({ - handlers: { - foo: fooHandler, - }, - loggers: { - bar: { - level: "INFO", - handlers: ["foo"], - }, - }, - }); - - const logger = log.getLogger("bar"); - - assertEquals(logger.levelName, "INFO"); - assertEquals(logger.handlers, [fooHandler]); -}); - -Deno.test("getLoggerUnknown", async function (): Promise { - await log.setup({ - handlers: {}, - loggers: {}, - }); - - const logger = log.getLogger("nonexistent"); - - assertEquals(logger.levelName, "NOTSET"); - assertEquals(logger.handlers, []); -}); - -Deno.test("getInvalidLoggerLevels", function (): void { - assertThrows(() => getLevelByName("FAKE_LOG_LEVEL" as LevelName)); - assertThrows(() => getLevelName(5000)); -}); diff --git a/std/mime/mod.ts b/std/mime/mod.ts deleted file mode 100644 index e8bd8d646f..0000000000 --- a/std/mime/mod.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -export * from "./multipart.ts"; diff --git a/std/mime/multipart.ts b/std/mime/multipart.ts deleted file mode 100644 index b0d03bbc6a..0000000000 --- a/std/mime/multipart.ts +++ /dev/null @@ -1,609 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { equals, indexOf, lastIndexOf, startsWith } from "../bytes/mod.ts"; -import { copyN } from "../io/ioutil.ts"; -import { MultiReader } from "../io/readers.ts"; -import { extname } from "../path/mod.ts"; -import { BufReader, BufWriter } from "../io/bufio.ts"; -import { encoder } from "../encoding/utf8.ts"; -import { assert } from "../_util/assert.ts"; -import { TextProtoReader } from "../textproto/mod.ts"; -import { hasOwnProperty } from "../_util/has_own_property.ts"; - -/** FormFile object */ -export interface FormFile { - /** filename */ - filename: string; - /** content-type header value of file */ - type: string; - /** byte size of file */ - size: number; - /** in-memory content of file. Either content or tempfile is set */ - content?: Uint8Array; - /** temporal file path. - * Set if file size is bigger than specified max-memory size at reading form - * */ - tempfile?: string; -} - -/** Type guard for FormFile */ -// deno-lint-ignore no-explicit-any -export function isFormFile(x: any): x is FormFile { - return hasOwnProperty(x, "filename") && hasOwnProperty(x, "type"); -} - -function randomBoundary(): string { - let boundary = "--------------------------"; - for (let i = 0; i < 24; i++) { - boundary += Math.floor(Math.random() * 16).toString(16); - } - return boundary; -} - -/** - * Checks whether `buf` should be considered to match the boundary. - * - * The prefix is "--boundary" or "\r\n--boundary" or "\n--boundary", and the - * caller has verified already that `hasPrefix(buf, prefix)` is true. - * - * `matchAfterPrefix()` returns `1` if the buffer does match the boundary, - * meaning the prefix is followed by a dash, space, tab, cr, nl, or EOF. - * - * It returns `-1` if the buffer definitely does NOT match the boundary, - * meaning the prefix is followed by some other character. - * For example, "--foobar" does not match "--foo". - * - * It returns `0` more input needs to be read to make the decision, - * meaning that `buf.length` and `prefix.length` are the same. - */ -export function matchAfterPrefix( - buf: Uint8Array, - prefix: Uint8Array, - eof: boolean, -): -1 | 0 | 1 { - if (buf.length === prefix.length) { - return eof ? 1 : 0; - } - const c = buf[prefix.length]; - if ( - c === " ".charCodeAt(0) || - c === "\t".charCodeAt(0) || - c === "\r".charCodeAt(0) || - c === "\n".charCodeAt(0) || - c === "-".charCodeAt(0) - ) { - return 1; - } - return -1; -} - -/** - * Scans `buf` to identify how much of it can be safely returned as part of the - * `PartReader` body. - * - * @param buf - The buffer to search for boundaries. - * @param dashBoundary - Is "--boundary". - * @param newLineDashBoundary - Is "\r\n--boundary" or "\n--boundary", depending - * on what mode we are in. The comments below (and the name) assume - * "\n--boundary", but either is accepted. - * @param total - The number of bytes read out so far. If total == 0, then a - * leading "--boundary" is recognized. - * @param eof - Whether `buf` contains the final bytes in the stream before EOF. - * If `eof` is false, more bytes are expected to follow. - * @returns The number of data bytes from buf that can be returned as part of - * the `PartReader` body. - */ -export function scanUntilBoundary( - buf: Uint8Array, - dashBoundary: Uint8Array, - newLineDashBoundary: Uint8Array, - total: number, - eof: boolean, -): number | null { - if (total === 0) { - // At beginning of body, allow dashBoundary. - if (startsWith(buf, dashBoundary)) { - switch (matchAfterPrefix(buf, dashBoundary, eof)) { - case -1: - return dashBoundary.length; - case 0: - return 0; - case 1: - return null; - } - } - if (startsWith(dashBoundary, buf)) { - return 0; - } - } - - // Search for "\n--boundary". - const i = indexOf(buf, newLineDashBoundary); - if (i >= 0) { - switch (matchAfterPrefix(buf.slice(i), newLineDashBoundary, eof)) { - case -1: - return i + newLineDashBoundary.length; - case 0: - return i; - case 1: - return i > 0 ? i : null; - } - } - if (startsWith(newLineDashBoundary, buf)) { - return 0; - } - - // Otherwise, anything up to the final \n is not part of the boundary and so - // must be part of the body. Also, if the section from the final \n onward is - // not a prefix of the boundary, it too must be part of the body. - const j = lastIndexOf(buf, newLineDashBoundary.slice(0, 1)); - if (j >= 0 && startsWith(newLineDashBoundary, buf.slice(j))) { - return j; - } - - return buf.length; -} - -class PartReader implements Deno.Reader, Deno.Closer { - n: number | null = 0; - total = 0; - - constructor(private mr: MultipartReader, public readonly headers: Headers) {} - - async read(p: Uint8Array): Promise { - const br = this.mr.bufReader; - - // Read into buffer until we identify some data to return, - // or we find a reason to stop (boundary or EOF). - let peekLength = 1; - while (this.n === 0) { - peekLength = Math.max(peekLength, br.buffered()); - const peekBuf = await br.peek(peekLength); - if (peekBuf === null) { - throw new Deno.errors.UnexpectedEof(); - } - const eof = peekBuf.length < peekLength; - this.n = scanUntilBoundary( - peekBuf, - this.mr.dashBoundary, - this.mr.newLineDashBoundary, - this.total, - eof, - ); - if (this.n === 0) { - // Force buffered I/O to read more into buffer. - assert(eof === false); - peekLength++; - } - } - - if (this.n === null) { - return null; - } - - const nread = Math.min(p.length, this.n); - const buf = p.subarray(0, nread); - const r = await br.readFull(buf); - assert(r === buf); - this.n -= nread; - this.total += nread; - return nread; - } - - close(): void {} - - private contentDisposition!: string; - private contentDispositionParams!: { [key: string]: string }; - - private getContentDispositionParams(): { [key: string]: string } { - if (this.contentDispositionParams) return this.contentDispositionParams; - const cd = this.headers.get("content-disposition"); - const params: { [key: string]: string } = {}; - assert(cd != null, "content-disposition must be set"); - const comps = decodeURI(cd).split(";"); - this.contentDisposition = comps[0]; - comps - .slice(1) - .map((v: string): string => v.trim()) - .map((kv: string): void => { - const [k, v] = kv.split("="); - if (v) { - const s = v.charAt(0); - const e = v.charAt(v.length - 1); - if ((s === e && s === '"') || s === "'") { - params[k] = v.substr(1, v.length - 2); - } else { - params[k] = v; - } - } - }); - return (this.contentDispositionParams = params); - } - - get fileName(): string { - return this.getContentDispositionParams()["filename"]; - } - - get formName(): string { - const p = this.getContentDispositionParams(); - if (this.contentDisposition === "form-data") { - return p["name"]; - } - return ""; - } -} - -function skipLWSPChar(u: Uint8Array): Uint8Array { - const ret = new Uint8Array(u.length); - const sp = " ".charCodeAt(0); - const ht = "\t".charCodeAt(0); - let j = 0; - for (let i = 0; i < u.length; i++) { - if (u[i] === sp || u[i] === ht) continue; - ret[j++] = u[i]; - } - return ret.slice(0, j); -} - -export interface MultipartFormData { - file(key: string): FormFile | FormFile[] | undefined; - value(key: string): string | undefined; - entries(): IterableIterator< - [string, string | FormFile | FormFile[] | undefined] - >; - [Symbol.iterator](): IterableIterator< - [string, string | FormFile | FormFile[] | undefined] - >; - /** Remove all tempfiles */ - removeAll(): Promise; -} - -/** Reader for parsing multipart/form-data */ -export class MultipartReader { - readonly newLine = encoder.encode("\r\n"); - readonly newLineDashBoundary = encoder.encode(`\r\n--${this.boundary}`); - readonly dashBoundaryDash = encoder.encode(`--${this.boundary}--`); - readonly dashBoundary = encoder.encode(`--${this.boundary}`); - readonly bufReader: BufReader; - - constructor(reader: Deno.Reader, private boundary: string) { - this.bufReader = new BufReader(reader); - } - - /** Read all form data from stream. - * If total size of stored data in memory exceed maxMemory, - * overflowed file data will be written to temporal files. - * String field values are never written to files. - * null value means parsing or writing to file was failed in some reason. - * @param maxMemory maximum memory size to store file in memory. bytes. @default 10485760 (10MB) - * */ - async readForm(maxMemory = 10 << 20): Promise { - const fileMap = new Map(); - const valueMap = new Map(); - let maxValueBytes = maxMemory + (10 << 20); - const buf = new Deno.Buffer(new Uint8Array(maxValueBytes)); - for (;;) { - const p = await this.nextPart(); - if (p === null) { - break; - } - if (p.formName === "") { - continue; - } - buf.reset(); - if (!p.fileName) { - // value - const n = await copyN(p, buf, maxValueBytes); - maxValueBytes -= n; - if (maxValueBytes < 0) { - throw new RangeError("message too large"); - } - const value = new TextDecoder().decode(buf.bytes()); - valueMap.set(p.formName, value); - continue; - } - // file - let formFile: FormFile | FormFile[] | undefined; - const n = await copyN(p, buf, maxValueBytes); - const contentType = p.headers.get("content-type"); - assert(contentType != null, "content-type must be set"); - if (n > maxMemory) { - // too big, write to disk and flush buffer - const ext = extname(p.fileName); - const filepath = await Deno.makeTempFile({ - dir: ".", - prefix: "multipart-", - suffix: ext, - }); - - const file = await Deno.open(filepath, { write: true }); - - try { - const size = await Deno.copy(new MultiReader(buf, p), file); - - file.close(); - formFile = { - filename: p.fileName, - type: contentType, - tempfile: filepath, - size, - }; - } catch (e) { - await Deno.remove(filepath); - throw e; - } - } else { - formFile = { - filename: p.fileName, - type: contentType, - content: buf.bytes(), - size: buf.length, - }; - maxMemory -= n; - maxValueBytes -= n; - } - if (formFile) { - const mapVal = fileMap.get(p.formName); - if (mapVal !== undefined) { - if (Array.isArray(mapVal)) { - mapVal.push(formFile); - } else { - fileMap.set(p.formName, [mapVal, formFile]); - } - } else { - fileMap.set(p.formName, formFile); - } - } - } - return multipartFormData(fileMap, valueMap); - } - - private currentPart: PartReader | undefined; - private partsRead = 0; - - private async nextPart(): Promise { - if (this.currentPart) { - this.currentPart.close(); - } - if (equals(this.dashBoundary, encoder.encode("--"))) { - throw new Error("boundary is empty"); - } - let expectNewPart = false; - for (;;) { - const line = await this.bufReader.readSlice("\n".charCodeAt(0)); - if (line === null) { - throw new Deno.errors.UnexpectedEof(); - } - if (this.isBoundaryDelimiterLine(line)) { - this.partsRead++; - const r = new TextProtoReader(this.bufReader); - const headers = await r.readMIMEHeader(); - if (headers === null) { - throw new Deno.errors.UnexpectedEof(); - } - const np = new PartReader(this, headers); - this.currentPart = np; - return np; - } - if (this.isFinalBoundary(line)) { - return null; - } - if (expectNewPart) { - throw new Error(`expecting a new Part; got line ${line}`); - } - if (this.partsRead === 0) { - continue; - } - if (equals(line, this.newLine)) { - expectNewPart = true; - continue; - } - throw new Error(`unexpected line in nextPart(): ${line}`); - } - } - - private isFinalBoundary(line: Uint8Array): boolean { - if (!startsWith(line, this.dashBoundaryDash)) { - return false; - } - const rest = line.slice(this.dashBoundaryDash.length, line.length); - return rest.length === 0 || equals(skipLWSPChar(rest), this.newLine); - } - - private isBoundaryDelimiterLine(line: Uint8Array): boolean { - if (!startsWith(line, this.dashBoundary)) { - return false; - } - const rest = line.slice(this.dashBoundary.length); - return equals(skipLWSPChar(rest), this.newLine); - } -} - -function multipartFormData( - fileMap: Map, - valueMap: Map, -): MultipartFormData { - function file(key: string): FormFile | FormFile[] | undefined { - return fileMap.get(key); - } - function value(key: string): string | undefined { - return valueMap.get(key); - } - function* entries(): IterableIterator< - [string, string | FormFile | FormFile[] | undefined] - > { - yield* fileMap; - yield* valueMap; - } - async function removeAll(): Promise { - const promises: Array> = []; - for (const val of fileMap.values()) { - if (Array.isArray(val)) { - for (const subVal of val) { - if (!subVal.tempfile) continue; - promises.push(Deno.remove(subVal.tempfile)); - } - } else { - if (!val.tempfile) continue; - promises.push(Deno.remove(val.tempfile)); - } - } - await Promise.all(promises); - } - return { - file, - value, - entries, - removeAll, - [Symbol.iterator](): IterableIterator< - [string, string | FormFile | FormFile[] | undefined] - > { - return entries(); - }, - }; -} - -class PartWriter implements Deno.Writer { - closed = false; - private readonly partHeader: string; - private headersWritten = false; - - constructor( - private writer: Deno.Writer, - readonly boundary: string, - public headers: Headers, - isFirstBoundary: boolean, - ) { - let buf = ""; - if (isFirstBoundary) { - buf += `--${boundary}\r\n`; - } else { - buf += `\r\n--${boundary}\r\n`; - } - for (const [key, value] of headers.entries()) { - buf += `${key}: ${value}\r\n`; - } - buf += `\r\n`; - this.partHeader = buf; - } - - close(): void { - this.closed = true; - } - - async write(p: Uint8Array): Promise { - if (this.closed) { - throw new Error("part is closed"); - } - if (!this.headersWritten) { - await this.writer.write(encoder.encode(this.partHeader)); - this.headersWritten = true; - } - return this.writer.write(p); - } -} - -function checkBoundary(b: string): string { - if (b.length < 1 || b.length > 70) { - throw new Error(`invalid boundary length: ${b.length}`); - } - const end = b.length - 1; - for (let i = 0; i < end; i++) { - const c = b.charAt(i); - if (!c.match(/[a-zA-Z0-9'()+_,\-./:=?]/) || (c === " " && i !== end)) { - throw new Error("invalid boundary character: " + c); - } - } - return b; -} - -/** Writer for creating multipart/form-data */ -export class MultipartWriter { - private readonly _boundary: string; - - get boundary(): string { - return this._boundary; - } - - private lastPart: PartWriter | undefined; - private bufWriter: BufWriter; - private isClosed = false; - - constructor(private readonly writer: Deno.Writer, boundary?: string) { - if (boundary !== void 0) { - this._boundary = checkBoundary(boundary); - } else { - this._boundary = randomBoundary(); - } - this.bufWriter = new BufWriter(writer); - } - - formDataContentType(): string { - return `multipart/form-data; boundary=${this.boundary}`; - } - - private createPart(headers: Headers): Deno.Writer { - if (this.isClosed) { - throw new Error("multipart: writer is closed"); - } - if (this.lastPart) { - this.lastPart.close(); - } - const part = new PartWriter( - this.writer, - this.boundary, - headers, - !this.lastPart, - ); - this.lastPart = part; - return part; - } - - createFormFile(field: string, filename: string): Deno.Writer { - const h = new Headers(); - h.set( - "Content-Disposition", - `form-data; name="${field}"; filename="${filename}"`, - ); - h.set("Content-Type", "application/octet-stream"); - return this.createPart(h); - } - - createFormField(field: string): Deno.Writer { - const h = new Headers(); - h.set("Content-Disposition", `form-data; name="${field}"`); - h.set("Content-Type", "application/octet-stream"); - return this.createPart(h); - } - - async writeField(field: string, value: string): Promise { - const f = await this.createFormField(field); - await f.write(encoder.encode(value)); - } - - async writeFile( - field: string, - filename: string, - file: Deno.Reader, - ): Promise { - const f = await this.createFormFile(field, filename); - await Deno.copy(file, f); - } - - private flush(): Promise { - return this.bufWriter.flush(); - } - - /** Close writer. No additional data can be written to stream */ - async close(): Promise { - if (this.isClosed) { - throw new Error("multipart: writer is closed"); - } - if (this.lastPart) { - this.lastPart.close(); - this.lastPart = void 0; - } - await this.writer.write(encoder.encode(`\r\n--${this.boundary}--\r\n`)); - await this.flush(); - this.isClosed = true; - } -} diff --git a/std/mime/multipart_test.ts b/std/mime/multipart_test.ts deleted file mode 100644 index eee3005f1f..0000000000 --- a/std/mime/multipart_test.ts +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertThrows, - assertThrowsAsync, -} from "../testing/asserts.ts"; -import * as path from "../path/mod.ts"; -import { - isFormFile, - matchAfterPrefix, - MultipartReader, - MultipartWriter, - scanUntilBoundary, -} from "./multipart.ts"; -import { StringWriter } from "../io/writers.ts"; - -const e = new TextEncoder(); -const boundary = "--abcde"; -const dashBoundary = e.encode("--" + boundary); -const nlDashBoundary = e.encode("\r\n--" + boundary); - -const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); -const testdataDir = path.resolve(moduleDir, "testdata"); - -Deno.test("multipartScanUntilBoundary1", function (): void { - const data = `--${boundary}`; - const n = scanUntilBoundary( - e.encode(data), - dashBoundary, - nlDashBoundary, - 0, - true, - ); - assertEquals(n, null); -}); - -Deno.test("multipartScanUntilBoundary2", function (): void { - const data = `foo\r\n--${boundary}`; - const n = scanUntilBoundary( - e.encode(data), - dashBoundary, - nlDashBoundary, - 0, - true, - ); - assertEquals(n, 3); -}); - -Deno.test("multipartScanUntilBoundary3", function (): void { - const data = `foobar`; - const n = scanUntilBoundary( - e.encode(data), - dashBoundary, - nlDashBoundary, - 0, - false, - ); - assertEquals(n, data.length); -}); - -Deno.test("multipartScanUntilBoundary4", function (): void { - const data = `foo\r\n--`; - const n = scanUntilBoundary( - e.encode(data), - dashBoundary, - nlDashBoundary, - 0, - false, - ); - assertEquals(n, 3); -}); - -Deno.test("multipartMatchAfterPrefix1", function (): void { - const data = `${boundary}\r`; - const v = matchAfterPrefix(e.encode(data), e.encode(boundary), false); - assertEquals(v, 1); -}); - -Deno.test("multipartMatchAfterPrefix2", function (): void { - const data = `${boundary}hoge`; - const v = matchAfterPrefix(e.encode(data), e.encode(boundary), false); - assertEquals(v, -1); -}); - -Deno.test("multipartMatchAfterPrefix3", function (): void { - const data = `${boundary}`; - const v = matchAfterPrefix(e.encode(data), e.encode(boundary), false); - assertEquals(v, 0); -}); - -Deno.test("multipartMultipartWriter", async function (): Promise { - const buf = new Deno.Buffer(); - const mw = new MultipartWriter(buf); - await mw.writeField("foo", "foo"); - await mw.writeField("bar", "bar"); - const f = await Deno.open(path.join(testdataDir, "sample.txt"), { - read: true, - }); - await mw.writeFile("file", "sample.txt", f); - await mw.close(); - f.close(); -}); - -Deno.test("multipartMultipartWriter2", function (): void { - const w = new StringWriter(); - assertThrows( - (): MultipartWriter => new MultipartWriter(w, ""), - Error, - "invalid boundary length", - ); - assertThrows( - (): MultipartWriter => - new MultipartWriter( - w, - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaa", - ), - Error, - "invalid boundary length", - ); - assertThrows( - (): MultipartWriter => new MultipartWriter(w, "aaa aaa"), - Error, - "invalid boundary character", - ); - assertThrows( - (): MultipartWriter => new MultipartWriter(w, "boundary¥¥"), - Error, - "invalid boundary character", - ); -}); - -Deno.test("multipartMultipartWriter3", async function (): Promise { - const w = new StringWriter(); - const mw = new MultipartWriter(w); - await mw.writeField("foo", "foo"); - await mw.close(); - await assertThrowsAsync( - async (): Promise => { - await mw.close(); - }, - Error, - "closed", - ); - await assertThrowsAsync( - async (): Promise => { - // deno-lint-ignore no-explicit-any - await mw.writeFile("bar", "file", null as any); - }, - Error, - "closed", - ); - await assertThrowsAsync( - async (): Promise => { - await mw.writeField("bar", "bar"); - }, - Error, - "closed", - ); - assertThrows( - (): void => { - mw.createFormField("bar"); - }, - Error, - "closed", - ); - assertThrows( - (): void => { - mw.createFormFile("bar", "file"); - }, - Error, - "closed", - ); -}); - -Deno.test({ - name: "[mime/multipart] readForm() basic", - async fn() { - const o = await Deno.open(path.join(testdataDir, "sample.txt")); - const mr = new MultipartReader( - o, - "--------------------------434049563556637648550474", - ); - const form = await mr.readForm(); - assertEquals(form.value("foo"), "foo"); - assertEquals(form.value("bar"), "bar"); - const file = form.file("file"); - assert(isFormFile(file)); - assert(file.content !== void 0); - const file2 = form.file("file2"); - assert(isFormFile(file2)); - assert(file2.filename === "中文.json"); - assert(file2.content !== void 0); - o.close(); - }, -}); - -Deno.test({ - name: - "[mime/multipart] readForm() should store big file completely in temp file", - async fn() { - const multipartFile = path.join(testdataDir, "form-data.dat"); - const sampleFile = await Deno.makeTempFile(); - const writer = await Deno.open(multipartFile, { - write: true, - create: true, - }); - - const size = 1 << 24; // 16mb - - await Deno.truncate(sampleFile, size); - const bigFile = await Deno.open(sampleFile, { read: true }); - - const mw = new MultipartWriter(writer); - await mw.writeField("deno", "land"); - await mw.writeField("bar", "bar"); - await mw.writeFile("file", "sample.bin", bigFile); - - await mw.close(); - writer.close(); - bigFile.close(); - - const o = await Deno.open(multipartFile); - const mr = new MultipartReader(o, mw.boundary); - // use low-memory to write "file" into temp file. - const form = await mr.readForm(20); - try { - assertEquals(form.value("deno"), "land"); - assertEquals(form.value("bar"), "bar"); - let file = form.file("file"); - if (Array.isArray(file)) { - file = file[0]; - } - assert(file != null); - assert(file.tempfile != null); - assertEquals(file.size, size); - assertEquals(file.type, "application/octet-stream"); - // TODO(bartlomieju): checksum of tmp & sampleFile - } finally { - await Deno.remove(multipartFile); - await Deno.remove(sampleFile); - await form.removeAll(); - o.close(); - } - }, -}); - -Deno.test({ - name: "[mime/multipart] removeAll() should remove all tempfiles", - async fn() { - const o = await Deno.open(path.join(testdataDir, "sample.txt")); - const mr = new MultipartReader( - o, - "--------------------------434049563556637648550474", - ); - const form = await mr.readForm(20); - let file = form.file("file"); - if (Array.isArray(file)) { - file = file[0]; - } - assert(file != null); - const { tempfile, content } = file; - assert(tempfile != null); - assert(content == null); - const stat = await Deno.stat(tempfile); - assertEquals(stat.size, file.size); - await form.removeAll(); - await assertThrowsAsync(async () => { - await Deno.stat(tempfile); - }, Deno.errors.NotFound); - o.close(); - }, -}); - -Deno.test({ - name: "[mime/multipart] entries()", - async fn() { - const o = await Deno.open(path.join(testdataDir, "sample.txt")); - const mr = new MultipartReader( - o, - "--------------------------434049563556637648550474", - ); - const form = await mr.readForm(); - const map = new Map(form.entries()); - assertEquals(map.get("foo"), "foo"); - assertEquals(map.get("bar"), "bar"); - const file = map.get("file"); - assert(isFormFile(file)); - assertEquals(file.filename, "tsconfig.json"); - o.close(); - }, -}); diff --git a/std/mime/test.ts b/std/mime/test.ts deleted file mode 100644 index 590417055f..0000000000 --- a/std/mime/test.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import "./mod.ts"; diff --git a/std/mime/testdata/sample.txt b/std/mime/testdata/sample.txt deleted file mode 100644 index 8c7a1c2040..0000000000 --- a/std/mime/testdata/sample.txt +++ /dev/null @@ -1,35 +0,0 @@ -----------------------------434049563556637648550474 -content-disposition: form-data; name="foo" -content-type: application/octet-stream - -foo -----------------------------434049563556637648550474 -content-disposition: form-data; name="bar" -content-type: application/octet-stream - -bar -----------------------------434049563556637648550474 -content-disposition: form-data; name="file"; filename="tsconfig.json" -content-type: application/octet-stream - -{ - "compilerOptions": { - "target": "es2018", - "baseUrl": ".", - "paths": { - "deno": ["./deno.d.ts"], - "https://*": ["../../.deno/deps/https/*"], - "http://*": ["../../.deno/deps/http/*"] - } - } -} - -----------------------------434049563556637648550474 -content-disposition: form-data; name="file2"; filename="中文.json" -content-type: application/octet-stream - -{ - "test": "filename" -} - -----------------------------434049563556637648550474-- diff --git a/std/node/README.md b/std/node/README.md deleted file mode 100644 index 5f0d8511ae..0000000000 --- a/std/node/README.md +++ /dev/null @@ -1,137 +0,0 @@ -# Deno Node compatibility - -This module is meant to have a compatibility layer for the -[NodeJS standard library](https://nodejs.org/docs/latest-v12.x/api/). - -**Warning**: Any function of this module should not be referred anywhere in the -deno standard library as it's a compatibility module. - -## Supported Builtins - -- [ ] assert -- [x] buffer -- [ ] child_process -- [ ] cluster -- [ ] console -- [ ] crypto -- [ ] dgram -- [ ] dns -- [x] events -- [x] fs _partly_ -- [ ] http -- [ ] http2 -- [ ] https -- [x] module -- [ ] net -- [x] os _partly_ -- [x] path -- [ ] perf_hooks -- [x] process _partly_ -- [x] querystring -- [ ] readline -- [ ] repl -- [ ] stream -- [ ] string_decoder -- [ ] sys -- [x] timers -- [ ] tls -- [ ] tty -- [ ] url -- [x] util _partly_ -- [ ] ~~v8~~ _can't implement_ -- [ ] vm -- [ ] worker_threads -- [ ] zlib - -* [x] node globals _partly_ - -### Deprecated - -These builtins are deprecated in NodeJS v13 and will probably not be polyfilled: - -- constants -- domain -- freelist -- punycode - -### Experimental - -These builtins are experimental in NodeJS v13 and will not be polyfilled until -they are stable: - -- async_hooks -- inspector -- policies -- report -- trace_events -- wasi - -## CommonJS Module Loading - -`createRequire(...)` is provided to create a `require` function for loading CJS -modules. It also sets supported globals. - -```ts -import { createRequire } from "https://deno.land/std@$STD_VERSION/node/module.ts"; - -const require = createRequire(import.meta.url); -// Loads native module polyfill. -const path = require("path"); -// Loads extensionless module. -const cjsModule = require("./my_mod"); -// Visits node_modules. -const leftPad = require("left-pad"); -``` - -## Contributing - -When converting from promise-based to callback-based APIs, the most obvious way -is like this: - -```ts -promise.then((value) => callback(null, value)).catch(callback); -``` - -This has a subtle bug - if the callback throws an error, the catch statement -will also catch _that_ error, and the callback will be called twice. The correct -way to do it is like this: - -```ts -promise.then((value) => callback(null, value), callback); -``` - -The second parameter of `then` can also be used to catch errors, but only errors -from the existing promise, not the new one created by the callback. - -If the Deno equivalent is actually synchronous, there's a similar problem with -try/catch statements: - -```ts -try { - const value = process(); - callback(null, value); -} catch (err) { - callback(err); -} -``` - -Since the callback is called within the `try` block, any errors from it will be -caught and call the callback again. - -The correct way to do it is like this: - -```ts -let err, value; -try { - value = process(); -} catch (e) { - err = e; -} -if (err) { - callback(err); // Make sure arguments.length === 1 -} else { - callback(null, value); -} -``` - -It's not as clean, but prevents the callback being called twice. diff --git a/std/node/_crypto/constants.ts b/std/node/_crypto/constants.ts deleted file mode 100644 index cfe3f46269..0000000000 --- a/std/node/_crypto/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const MAX_ALLOC = Math.pow(2, 30) - 1; diff --git a/std/node/_crypto/pbkdf2.ts b/std/node/_crypto/pbkdf2.ts deleted file mode 100644 index 2a63802e62..0000000000 --- a/std/node/_crypto/pbkdf2.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { createHash } from "../../hash/mod.ts"; -import { Buffer } from "../buffer.ts"; -import { MAX_ALLOC } from "./constants.ts"; -import { HASH_DATA } from "./types.ts"; - -export type NormalizedAlgorithms = - | "md5" - | "ripemd160" - | "sha1" - | "sha224" - | "sha256" - | "sha384" - | "sha512"; - -type Algorithms = - | "md5" - | "ripemd160" - | "rmd160" - | "sha1" - | "sha224" - | "sha256" - | "sha384" - | "sha512"; - -function createHasher(alg: Algorithms) { - let normalizedAlg: NormalizedAlgorithms; - if (alg === "rmd160") { - normalizedAlg = "ripemd160"; - } else { - normalizedAlg = alg; - } - return (value: Uint8Array) => - Buffer.from(createHash(normalizedAlg).update(value).digest()); -} - -function getZeroes(zeros: number) { - return Buffer.alloc(zeros); -} -const sizes = { - md5: 16, - sha1: 20, - sha224: 28, - sha256: 32, - sha384: 48, - sha512: 64, - rmd160: 20, - ripemd160: 20, -}; - -function toBuffer(bufferable: HASH_DATA) { - if (bufferable instanceof Uint8Array || typeof bufferable === "string") { - return Buffer.from(bufferable as Uint8Array); - } else { - return Buffer.from(bufferable.buffer); - } -} - -class Hmac { - hash: (value: Uint8Array) => Buffer; - ipad1: Buffer; - opad: Buffer; - alg: string; - blocksize: number; - size: number; - ipad2: Buffer; - - constructor(alg: Algorithms, key: Buffer, saltLen: number) { - this.hash = createHasher(alg); - - const blocksize = (alg === "sha512" || alg === "sha384") ? 128 : 64; - - if (key.length > blocksize) { - key = this.hash(key); - } else if (key.length < blocksize) { - key = Buffer.concat([key, getZeroes(blocksize - key.length)], blocksize); - } - - const ipad = Buffer.allocUnsafe(blocksize + sizes[alg]); - const opad = Buffer.allocUnsafe(blocksize + sizes[alg]); - for (let i = 0; i < blocksize; i++) { - ipad[i] = key[i] ^ 0x36; - opad[i] = key[i] ^ 0x5C; - } - - const ipad1 = Buffer.allocUnsafe(blocksize + saltLen + 4); - ipad.copy(ipad1, 0, 0, blocksize); - - this.ipad1 = ipad1; - this.ipad2 = ipad; - this.opad = opad; - this.alg = alg; - this.blocksize = blocksize; - this.size = sizes[alg]; - } - - run(data: Buffer, ipad: Buffer) { - data.copy(ipad, this.blocksize); - const h = this.hash(ipad); - h.copy(this.opad, this.blocksize); - return this.hash(this.opad); - } -} - -/** - * @param iterations Needs to be higher or equal than zero - * @param keylen Needs to be higher or equal than zero but less than max allocation size (2^30) - * @param digest Algorithm to be used for encryption - */ -export function pbkdf2Sync( - password: HASH_DATA, - salt: HASH_DATA, - iterations: number, - keylen: number, - digest: Algorithms = "sha1", -): Buffer { - if (typeof iterations !== "number" || iterations < 0) { - throw new TypeError("Bad iterations"); - } - if (typeof keylen !== "number" || keylen < 0 || keylen > MAX_ALLOC) { - throw new TypeError("Bad key length"); - } - - const bufferedPassword = toBuffer(password); - const bufferedSalt = toBuffer(salt); - - const hmac = new Hmac(digest, bufferedPassword, bufferedSalt.length); - - const DK = Buffer.allocUnsafe(keylen); - const block1 = Buffer.allocUnsafe(bufferedSalt.length + 4); - bufferedSalt.copy(block1, 0, 0, bufferedSalt.length); - - let destPos = 0; - const hLen = sizes[digest]; - const l = Math.ceil(keylen / hLen); - - for (let i = 1; i <= l; i++) { - block1.writeUInt32BE(i, bufferedSalt.length); - - const T = hmac.run(block1, hmac.ipad1); - let U = T; - - for (let j = 1; j < iterations; j++) { - U = hmac.run(U, hmac.ipad2); - for (let k = 0; k < hLen; k++) T[k] ^= U[k]; - } - - T.copy(DK, destPos); - destPos += hLen; - } - - return DK; -} - -/** - * @param iterations Needs to be higher or equal than zero - * @param keylen Needs to be higher or equal than zero but less than max allocation size (2^30) - * @param digest Algorithm to be used for encryption - */ -export function pbkdf2( - password: HASH_DATA, - salt: HASH_DATA, - iterations: number, - keylen: number, - digest: Algorithms = "sha1", - callback: ((err: Error | null, derivedKey?: Buffer) => void), -): void { - setTimeout(() => { - let err = null, res; - try { - res = pbkdf2Sync( - password, - salt, - iterations, - keylen, - digest, - ); - } catch (e) { - err = e; - } - if (err) { - callback(err); - } else { - callback(null, res); - } - }, 0); -} diff --git a/std/node/_crypto/pbkdf2_test.ts b/std/node/_crypto/pbkdf2_test.ts deleted file mode 100644 index 29f149cbe8..0000000000 --- a/std/node/_crypto/pbkdf2_test.ts +++ /dev/null @@ -1,427 +0,0 @@ -import { - NormalizedAlgorithms as Algorithms, - pbkdf2, - pbkdf2Sync, -} from "./pbkdf2.ts"; -import { - assert, - assertEquals, - assertStringIncludes, -} from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; - -type Pbkdf2Fixture = { - key: string | Float64Array | Int32Array | Uint8Array; - salt: string | Float64Array | Int32Array | Uint8Array; - iterations: number; - dkLen: number; - results: { [key in Algorithms]: string }; -}; - -const fixtures: Pbkdf2Fixture[] = [ - { - "key": "password", - "salt": "salt", - "iterations": 1, - "dkLen": 32, - "results": { - "md5": "f31afb6d931392daa5e3130f47f9a9b6e8e72029d8350b9fb27a9e0e00b9d991", - "sha1": - "0c60c80f961f0e71f3a9b524af6012062fe037a6e0f0eb94fe8fc46bdc637164", - "sha256": - "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b", - "sha512": - "867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252", - "sha224": - "3c198cbdb9464b7857966bd05b7bc92bc1cc4e6e63155d4e490557fd85989497", - "sha384": - "c0e14f06e49e32d73f9f52ddf1d0c5c7191609233631dadd76a567db42b78676", - "ripemd160": - "b725258b125e0bacb0e2307e34feb16a4d0d6aed6cb4b0eee458fc1829020428", - }, - }, - { - "key": "password", - "salt": "salt", - "iterations": 2, - "dkLen": 32, - "results": { - "md5": "042407b552be345ad6eee2cf2f7ed01dd9662d8f0c6950eaec7124aa0c82279e", - "sha1": - "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957cae93136266537a8d7bf4b76", - "sha256": - "ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43", - "sha512": - "e1d9c16aa681708a45f5c7c4e215ceb66e011a2e9f0040713f18aefdb866d53c", - "sha224": - "93200ffa96c5776d38fa10abdf8f5bfc0054b9718513df472d2331d2d1e66a3f", - "sha384": - "54f775c6d790f21930459162fc535dbf04a939185127016a04176a0730c6f1f4", - "ripemd160": - "768dcc27b7bfdef794a1ff9d935090fcf598555e66913180b9ce363c615e9ed9", - }, - }, - { - "key": "password", - "salt": "salt", - "iterations": 1, - "dkLen": 64, - "results": { - "md5": - "f31afb6d931392daa5e3130f47f9a9b6e8e72029d8350b9fb27a9e0e00b9d9915a5f18928639ca8bbc3d1c1cb66d4f27b9dfe39156774c6798b42adc57ed253f", - "sha1": - "0c60c80f961f0e71f3a9b524af6012062fe037a6e0f0eb94fe8fc46bdc637164ac2e7a8e3f9d2e83ace57e0d50e5e1071367c179bc86c767fc3f78ddb561363f", - "sha256": - "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b4dbf3a2f3dad3377264bb7b8e8330d4efc7451418617dabef683735361cdc18c", - "sha512": - "867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252c02d470a285a0501bad999bfe943c08f050235d7d68b1da55e63f73b60a57fce", - "sha224": - "3c198cbdb9464b7857966bd05b7bc92bc1cc4e6e63155d4e490557fd859894978ab846d52a1083ac610c36c2c5ea8ce4a024dd691064d5453bd17b15ea1ac194", - "sha384": - "c0e14f06e49e32d73f9f52ddf1d0c5c7191609233631dadd76a567db42b78676b38fc800cc53ddb642f5c74442e62be44d727702213e3bb9223c53b767fbfb5d", - "ripemd160": - "b725258b125e0bacb0e2307e34feb16a4d0d6aed6cb4b0eee458fc18290204289e55d962783bf52237d264cbbab25f18d89d8c798f90f558ea7b45bdf3d08334", - }, - }, - { - "key": "password", - "salt": "salt", - "iterations": 2, - "dkLen": 64, - "results": { - "md5": - "042407b552be345ad6eee2cf2f7ed01dd9662d8f0c6950eaec7124aa0c82279ed0b7e2a854d0f29ec82ddcabe9760368e5821af8745d74846ccbd17afbfe5ff0", - "sha1": - "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957cae93136266537a8d7bf4b76c51094cc1ae010b19923ddc4395cd064acb023ffd1edd5ef4be8ffe61426c28e", - "sha256": - "ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43830651afcb5c862f0b249bd031f7a67520d136470f5ec271ece91c07773253d9", - "sha512": - "e1d9c16aa681708a45f5c7c4e215ceb66e011a2e9f0040713f18aefdb866d53cf76cab2868a39b9f7840edce4fef5a82be67335c77a6068e04112754f27ccf4e", - "sha224": - "93200ffa96c5776d38fa10abdf8f5bfc0054b9718513df472d2331d2d1e66a3f97b510224f700ce72581ffb10a1c99ec99a8cc1b951851a71f30d9265fccf912", - "sha384": - "54f775c6d790f21930459162fc535dbf04a939185127016a04176a0730c6f1f4fb48832ad1261baadd2cedd50814b1c806ad1bbf43ebdc9d047904bf7ceafe1e", - "ripemd160": - "768dcc27b7bfdef794a1ff9d935090fcf598555e66913180b9ce363c615e9ed953b95fd07169be535e38afbea29c030e06d14f40745b1513b7ccdf0e76229e50", - }, - }, - { - "key": "password", - "salt": "salt", - "iterations": 4096, - "dkLen": 32, - "results": { - "md5": "15001f89b9c29ee6998c520d1a0629e893cc3f996a08d27060e4c33305bf0fb2", - "sha1": - "4b007901b765489abead49d926f721d065a429c12e463f6c4cd79401085b03db", - "sha256": - "c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a", - "sha512": - "d197b1b33db0143e018b12f3d1d1479e6cdebdcc97c5c0f87f6902e072f457b5", - "sha224": - "218c453bf90635bd0a21a75d172703ff6108ef603f65bb821aedade1d6961683", - "sha384": - "559726be38db125bc85ed7895f6e3cf574c7a01c080c3447db1e8a76764deb3c", - "ripemd160": - "99a40d3fe4ee95869791d9faa24864562782762171480b620ca8bed3dafbbcac", - }, - }, - { - "key": "passwordPASSWORDpassword", - "salt": "saltSALTsaltSALTsaltSALTsaltSALTsalt", - "iterations": 4096, - "dkLen": 40, - "results": { - "md5": - "8d5d0aad94d14420429fbc7e5b087d7a5527e65dfd0d486a310e8a7b6ff5a21bed000b118b2c26a6", - "sha1": - "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038b6b89a48612c5a25284e6605e12329", - "sha256": - "348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1c635518c7dac47e9", - "sha512": - "8c0511f4c6e597c6ac6315d8f0362e225f3c501495ba23b868c005174dc4ee71115b59f9e60cd953", - "sha224": - "056c4ba438ded91fc14e0594e6f52b87e1f3690c0dc0fbc05784ed9a754ca780e6c017e80c8de278", - "sha384": - "819143ad66df9a552559b9e131c52ae6c5c1b0eed18f4d283b8c5c9eaeb92b392c147cc2d2869d58", - "ripemd160": - "503b9a069633b261b2d3e4f21c5d0cafeb3f5008aec25ed21418d12630b6ce036ec82a0430ef1974", - }, - }, - { - "key": "pass\u00000word", - "salt": "sa\u00000lt", - "iterations": 4096, - "dkLen": 16, - "results": { - "md5": "2d6b566fd00069a30dd1ffdb4d598f54", - "sha1": "345cbad979dfccb90cac5257bea6ea46", - "sha256": "1df6274d3c0bd2fc7f54fb46f149dda4", - "sha512": "336d14366099e8aac2c46c94a8f178d2", - "sha224": "0aca9ca9634db6ef4927931f633c6453", - "sha384": "b6ab6f8f6532fd9c5c30a79e1f93dcc6", - "ripemd160": "914d58209e6483e491571a60e433124a", - }, - }, - { - "key": "63ffeeddccbbaa", - "salt": "salt", - "iterations": 1, - "dkLen": 32, - "results": { - "md5": "23a33e38c9c57ea122df372a5b96347667e843ba21c79f150ce503d947449b75", - "sha1": - "1a12b21aa46bd3bed3a23b8ad072a1465585344b1516252618aabbc41276dada", - "sha256": - "a47c9371d4d4f663c2a0d3becbd475b7eb884722c7265391381d7696151470a6", - "sha512": - "09328469e02fcee4f6ab88a23037de33d54f17f786eee39e1f8826109ee54e16", - "sha224": - "59baceb002865e57061c65dd861c309c049a97207054416c943764efc38b94ed", - "sha384": - "01cc52b81eda47c8bc9861ab7f7de682e92a0d5e522f4d3a06a3b97be1856580", - "ripemd160": - "4f04f4782f2def250005e04ef0497403330b52a085ae856f4640700b19983b7c", - }, - }, - { - "key": - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - "salt": - "6d6e656d6f6e6963e383a1e383bce38388e383abe382abe38299e3838fe38299e382a6e38299e382a1e381afe3829ae381afe38299e3818fe38299e3829de38299e381a1e381a1e38299e58d81e4babae58d81e889b2", - "iterations": 2048, - "dkLen": 64, - "results": { - "md5": - "029d0e4484f56d9cf7ab7ca972c8991aeb2be275cba9683db4143e9b72f67d49551ec4c70ca6d051538fc7a86b8568d08244fdea24ba826b7927babac4f62cf2", - "sha1": - "fb3fa7c05a98ff66da2eadd69fa2ba52401ee630e04322d3c5bb018d1dda03c7e47bdea0c9e4c77c87826632eed59bbe42ce05329a838664683b1a8dae3fffd8", - "sha256": - "3b19907cb907d1ee6e5a0ecb80bd66e2776d1f2c73f4789eafcad94fda832e970471ceb0d200ede70e63ae021044cf4b58b1011e34252ace8d94a48c287906ec", - "sha512": - "0be4563c5175fd02b042251228774f34c1ccb235054a9f0f968c6d828466eae8c32433a7aa09ce922722dc808c6a1629ba8f1b6ba46f0cf7a921e125d1cc9fcd", - "sha224": - "dd529ad11b298cafad9209a0a620af98cf1b782bd0ba1a61efcd74a4fe2662af6c36ffd015c68ed0cd630bdb023ea61e59317eb07b342e0c6ece1bd3034b768c", - "sha384": - "7265c090b602b0a432b4908f70b6a5a2a6657926d09ac72ebb78d8bcc81e0d4563316f1eb5570b2850ef06a14719746a8a8397d3d56aa51b2d50489741b7ff61", - "ripemd160": - "c984beaf664aea5ae7f671063ef2ad1f80098e48382a916809ff9212d1a8cb7ad6cb17354422717c668726dfce294e1442bb354b6a6693db84032172e77af6ae", - }, - }, - { - "key": "password", - "salt": "salt", - "iterations": 1, - "dkLen": 10, - "results": { - "md5": "f31afb6d931392daa5e3", - "sha1": "0c60c80f961f0e71f3a9", - "sha256": "120fb6cffcf8b32c43e7", - "sha512": "867f70cf1ade02cff375", - "sha224": "3c198cbdb9464b785796", - "sha384": "c0e14f06e49e32d73f9f", - "ripemd160": "b725258b125e0bacb0e2", - }, - }, - { - "key": "password", - "salt": "salt", - "iterations": 1, - "dkLen": 100, - "results": { - "md5": - "f31afb6d931392daa5e3130f47f9a9b6e8e72029d8350b9fb27a9e0e00b9d9915a5f18928639ca8bbc3d1c1cb66d4f27b9dfe39156774c6798b42adc57ed253f44fc731edccf067904ce2e317b9ef45767add4dfe53f8c190dac43d90cda5e66e627d4f2", - "sha1": - "0c60c80f961f0e71f3a9b524af6012062fe037a6e0f0eb94fe8fc46bdc637164ac2e7a8e3f9d2e83ace57e0d50e5e1071367c179bc86c767fc3f78ddb561363fc692ba406d1301e42bcccc3c520d06751d78b80c3db926b16ffa3395bd697c647f280b51", - "sha256": - "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b4dbf3a2f3dad3377264bb7b8e8330d4efc7451418617dabef683735361cdc18c22cd7fe60fa40e91c65849e1f60c0d8b62a7b2dbd0d3dfd75fb8498a5c2131ab02b66de5", - "sha512": - "867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252c02d470a285a0501bad999bfe943c08f050235d7d68b1da55e63f73b60a57fce7b532e206c2967d4c7d2ffa460539fc4d4e5eec70125d74c6c7cf86d25284f297907fcea", - "sha224": - "3c198cbdb9464b7857966bd05b7bc92bc1cc4e6e63155d4e490557fd859894978ab846d52a1083ac610c36c2c5ea8ce4a024dd691064d5453bd17b15ea1ac1944bbfd62e61b997e7b22660f588e297186572480015f33bc2bfd2b423827bcdcdb4845914", - "sha384": - "c0e14f06e49e32d73f9f52ddf1d0c5c7191609233631dadd76a567db42b78676b38fc800cc53ddb642f5c74442e62be44d727702213e3bb9223c53b767fbfb5db9d270d54c45d9cb6003d2967280b22671e2dbc6375f6ebf219c36f0d127be35e19d65a8", - "ripemd160": - "b725258b125e0bacb0e2307e34feb16a4d0d6aed6cb4b0eee458fc18290204289e55d962783bf52237d264cbbab25f18d89d8c798f90f558ea7b45bdf3d083340c18b9d23ba842183c5364d18bc0ffde5a8a408dd7ef02dde561a08d21c6d2325a69869b", - }, - }, - { - "key": new Uint8Array([112, 97, 115, 115, 119, 111, 114, 100]), - "salt": "salt", - "iterations": 1, - "dkLen": 32, - "results": { - "md5": "f31afb6d931392daa5e3130f47f9a9b6e8e72029d8350b9fb27a9e0e00b9d991", - "sha1": - "0c60c80f961f0e71f3a9b524af6012062fe037a6e0f0eb94fe8fc46bdc637164", - "sha256": - "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b", - "sha512": - "867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252", - "sha224": - "3c198cbdb9464b7857966bd05b7bc92bc1cc4e6e63155d4e490557fd85989497", - "sha384": - "c0e14f06e49e32d73f9f52ddf1d0c5c7191609233631dadd76a567db42b78676", - "ripemd160": - "b725258b125e0bacb0e2307e34feb16a4d0d6aed6cb4b0eee458fc1829020428", - }, - }, - { - "key": "password", - "salt": new Uint8Array([115, 97, 108, 116]), - "iterations": 1, - "dkLen": 32, - "results": { - "md5": "f31afb6d931392daa5e3130f47f9a9b6e8e72029d8350b9fb27a9e0e00b9d991", - "sha1": - "0c60c80f961f0e71f3a9b524af6012062fe037a6e0f0eb94fe8fc46bdc637164", - "sha256": - "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b", - "sha512": - "867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252", - "sha224": - "3c198cbdb9464b7857966bd05b7bc92bc1cc4e6e63155d4e490557fd85989497", - "sha384": - "c0e14f06e49e32d73f9f52ddf1d0c5c7191609233631dadd76a567db42b78676", - "ripemd160": - "b725258b125e0bacb0e2307e34feb16a4d0d6aed6cb4b0eee458fc1829020428", - }, - }, - { - "key": new Int32Array([112, 97, 115, 115, 119, 111, 114, 100]), - "salt": "salt", - "iterations": 1, - "dkLen": 32, - "results": { - "md5": "81de8e85b07d7969d9fe530641b63cc4ecbbf2345037cdc0ba61ad329fc7029c", - "sha1": - "f260ccd0bbc8fe6773119b834feec48636b716caad4180a4d0af4f9aa67c646e", - "sha256": - "9b4608f5eeab348f0b9d85a918b140706b24f275acf6829382dfee491015f9eb", - "sha512": - "c44b8f26550fe6ca0a55bce54b4a75e9530398f32ec28b59d0fded996e95e3d5", - "sha224": - "03d0c2b530ec6339e6418cb0f906e50591619be40aa8817aa9c7305d1773231c", - "sha384": - "2e69d62ae8c21ebc2de45a885b488f65fb88dfa58aaa9c57dd1fcb9d1edce96a", - "ripemd160": - "fc69276ba3f145492065feb0259b9edf68179f2023c95094e71ac7d01748018a", - }, - }, - { - "key": "password", - "salt": new Int32Array([115, 97, 108, 116]), - "iterations": 1, - "dkLen": 32, - "results": { - "md5": "36587a57770a8eef264391786b4ddfae0723f6a64dc2fc199fe7eb6ad9def701", - "sha1": - "b297f1ea23008f10ba9d645961e4661109e804b10af26bea22c44244492d6252", - "sha256": - "f678f0772894c079f21377d9ee1e76dd77b62dfc1f0575e6aa9eb030af7a356a", - "sha512": - "7f8133f6937ae1d7e4a43c19aabd2de8308d5b833341281716a501334cdb2470", - "sha224": - "ab66d29d3dacc731e44f091a7baa051926219cf493e8b9e3934cedfb215adc8b", - "sha384": - "cf139d648cf63e9b85a3b9b8f23f4445b84d22201bc2544bc273a17d5dcb7b28", - "ripemd160": - "26142e48fae1ad1c53be54823aadda2aa7d42f5524463fb1eff0efafa08edb9d", - }, - }, - { - "key": new Float64Array([112, 97, 115, 115, 119, 111, 114, 100]), - "salt": "salt", - "iterations": 1, - "dkLen": 32, - "results": { - "md5": "48336072da7d11ff203c61705b384b1c60953e7d1677fed2cd3e65738d60e67e", - "sha1": - "c2b17a7e98cc48690a92cd9f753a2c700229045905167571aa281aafe8230bba", - "sha256": - "55d62579a083a6c14b886710f81b54f567d214d343af776e5e90c467ea81b821", - "sha512": - "ded01ce343e2683d962fc74b7b5ceef525228f49393ce9353254f44e3dc7e9aa", - "sha224": - "5f10a348d320c7555b972b8d7d45a363a91e1a82dea063c3ac495cfad74a8d89", - "sha384": - "4b7f97dbadfd652e0579499d0e23607ec476ed4bea9d6f1740d0b110e2d08792", - "ripemd160": - "f92080d972a649d98d91a53922863fc7b8076c54869e9885f9a804868ef752e0", - }, - }, - { - "key": "password", - "salt": new Float64Array([115, 97, 108, 116]), - "iterations": 1, - "dkLen": 32, - "results": { - "md5": "9f1716e6f9d77b0beb56758f9509edea50828d15909073c3c715f66173ac3716", - "sha1": - "f158b9edd28c16ad3b41e0e8197ec132a98c2ddea73b959f55ec9792e0b29d6f", - "sha256": - "a6154d17480547a10212f75883509842f88f2ca5d6c1a2419646e47342051852", - "sha512": - "b10c2ea742de7dd0525988761ee1733564c91380eeaa1b199f4fafcbf7144b0c", - "sha224": - "29b315ac30c7d5e1640ca0f9e27b68a794fb9f950b8dd117129824f103ffb9db", - "sha384": - "624b4ed6ad389b976fb7503e54a35109f249c29ac6eb8b56850152be21b3cb0e", - "ripemd160": - "8999b9280207bc9c76cf25327aa352da26a683fac7a2adff17a39dcc4f4c3b5b", - }, - }, -]; - -Deno.test("pbkdf2 hashes data correctly", () => { - fixtures.forEach(({ - dkLen, - iterations, - key, - results, - salt, - }) => { - for (const algorithm in results) { - pbkdf2( - key, - salt, - iterations, - dkLen, - algorithm as Algorithms, - (err, res) => { - assert(!err); - assertEquals( - res?.toString("hex"), - results[algorithm as Algorithms], - ); - }, - ); - } - }); -}); - -Deno.test("pbkdf2Sync hashes data correctly", () => { - fixtures.forEach(({ - dkLen, - iterations, - key, - results, - salt, - }) => { - for (const algorithm in results) { - assertEquals( - pbkdf2Sync(key, salt, iterations, dkLen, algorithm as Algorithms) - .toString("hex"), - results[algorithm as Algorithms], - ); - } - }); -}); - -Deno.test("[std/node/crypto] pbkdf2 callback isn't called twice if error is thrown", async () => { - const importUrl = new URL("./pbkdf2.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { pbkdf2 } from ${JSON.stringify(importUrl)}`, - invocation: 'pbkdf2("password", "salt", 1, 32, "sha1", ', - }); -}); diff --git a/std/node/_crypto/randomBytes.ts b/std/node/_crypto/randomBytes.ts deleted file mode 100644 index 335c92d682..0000000000 --- a/std/node/_crypto/randomBytes.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Buffer } from "../buffer.ts"; - -export const MAX_RANDOM_VALUES = 65536; -export const MAX_SIZE = 4294967295; - -function generateRandomBytes(size: number) { - if (size > MAX_SIZE) { - throw new RangeError( - `The value of "size" is out of range. It must be >= 0 && <= ${MAX_SIZE}. Received ${size}`, - ); - } - - const bytes = Buffer.allocUnsafe(size); - - //Work around for getRandomValues max generation - if (size > MAX_RANDOM_VALUES) { - for (let generated = 0; generated < size; generated += MAX_RANDOM_VALUES) { - crypto.getRandomValues( - bytes.slice(generated, generated + MAX_RANDOM_VALUES), - ); - } - } else { - crypto.getRandomValues(bytes); - } - - return bytes; -} - -/** - * @param size Buffer length, must be equal or greater than zero - */ -export default function randomBytes(size: number): Buffer; -export default function randomBytes( - size: number, - cb?: (err: Error | null, buf?: Buffer) => void, -): void; -export default function randomBytes( - size: number, - cb?: (err: Error | null, buf?: Buffer) => void, -): Buffer | void { - if (typeof cb === "function") { - let err: Error | null = null, bytes: Buffer; - try { - bytes = generateRandomBytes(size); - } catch (e) { - //NodeJS nonsense - //If the size is out of range it will throw sync, otherwise throw async - if ( - e instanceof RangeError && - e.message.includes('The value of "size" is out of range') - ) { - throw e; - } else { - err = e; - } - } - setTimeout(() => { - if (err) { - cb(err); - } else { - cb(null, bytes); - } - }, 0); - } else { - return generateRandomBytes(size); - } -} diff --git a/std/node/_crypto/randomBytes_test.ts b/std/node/_crypto/randomBytes_test.ts deleted file mode 100644 index 6dd2091e15..0000000000 --- a/std/node/_crypto/randomBytes_test.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { - assert, - assertEquals, - assertStringIncludes, - assertThrows, - assertThrowsAsync, -} from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import randomBytes, { MAX_RANDOM_VALUES, MAX_SIZE } from "./randomBytes.ts"; - -Deno.test("randomBytes sync works correctly", function () { - assertEquals(randomBytes(0).length, 0, "len: " + 0); - assertEquals(randomBytes(3).length, 3, "len: " + 3); - assertEquals(randomBytes(30).length, 30, "len: " + 30); - assertEquals(randomBytes(300).length, 300, "len: " + 300); - assertEquals( - randomBytes(17 + MAX_RANDOM_VALUES).length, - 17 + MAX_RANDOM_VALUES, - "len: " + 17 + MAX_RANDOM_VALUES, - ); - assertEquals( - randomBytes(MAX_RANDOM_VALUES * 100).length, - MAX_RANDOM_VALUES * 100, - "len: " + MAX_RANDOM_VALUES * 100, - ); - assertThrows(() => randomBytes(MAX_SIZE + 1)); - assertThrows(() => randomBytes(-1)); -}); - -Deno.test("randomBytes async works correctly", function () { - randomBytes(0, function (err, resp) { - assert(!err); - assertEquals(resp?.length, 0, "len: " + 0); - }); - randomBytes(3, function (err, resp) { - assert(!err); - assertEquals(resp?.length, 3, "len: " + 3); - }); - randomBytes(30, function (err, resp) { - assert(!err); - assertEquals(resp?.length, 30, "len: " + 30); - }); - randomBytes(300, function (err, resp) { - assert(!err); - assertEquals(resp?.length, 300, "len: " + 300); - }); - randomBytes(17 + MAX_RANDOM_VALUES, function (err, resp) { - assert(!err); - assertEquals( - resp?.length, - 17 + MAX_RANDOM_VALUES, - "len: " + 17 + MAX_RANDOM_VALUES, - ); - }); - randomBytes(MAX_RANDOM_VALUES * 100, function (err, resp) { - assert(!err); - assertEquals( - resp?.length, - MAX_RANDOM_VALUES * 100, - "len: " + MAX_RANDOM_VALUES * 100, - ); - }); - assertThrows(() => - randomBytes(MAX_SIZE + 1, function (err) { - //Shouldn't throw async - assert(!err); - }) - ); - assertThrowsAsync(() => - new Promise((resolve, reject) => { - randomBytes(-1, function (err, res) { - //Shouldn't throw async - if (err) { - reject(err); - } else { - resolve(res); - } - }); - }) - ); -}); - -Deno.test("[std/node/crypto] randomBytes callback isn't called twice if error is thrown", async () => { - const importUrl = new URL("./randomBytes.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import randomBytes from ${JSON.stringify(importUrl)}`, - invocation: "randomBytes(0, ", - }); -}); diff --git a/std/node/_crypto/types.ts b/std/node/_crypto/types.ts deleted file mode 100644 index e56d8416c0..0000000000 --- a/std/node/_crypto/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Buffer } from "../buffer.ts"; - -export type HASH_DATA = string | ArrayBufferView | Buffer; diff --git a/std/node/_errors.ts b/std/node/_errors.ts deleted file mode 100644 index db37e31868..0000000000 --- a/std/node/_errors.ts +++ /dev/null @@ -1,2270 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -/************ NOT IMPLEMENTED -* ERR_INVALID_ARG_VALUE -* ERR_INVALID_MODULE_SPECIFIER -* ERR_INVALID_PACKAGE_TARGET -* ERR_INVALID_URL_SCHEME -* ERR_MANIFEST_ASSERT_INTEGRITY -* ERR_MODULE_NOT_FOUND -* ERR_PACKAGE_PATH_NOT_EXPORTED -* ERR_QUICSESSION_VERSION_NEGOTIATION -* ERR_REQUIRE_ESM -* ERR_SOCKET_BAD_PORT -* ERR_TLS_CERT_ALTNAME_INVALID -* ERR_UNHANDLED_ERROR -* ERR_WORKER_INVALID_EXEC_ARGV -* ERR_WORKER_PATH -* ERR_QUIC_ERROR -* ERR_SOCKET_BUFFER_SIZE //System error, shouldn't ever happen inside Deno -* ERR_SYSTEM_ERROR //System error, shouldn't ever happen inside Deno -* ERR_TTY_INIT_FAILED //System error, shouldn't ever happen inside Deno -* ERR_INVALID_PACKAGE_CONFIG // package.json stuff, probably useless -*************/ - -import { unreachable } from "../testing/asserts.ts"; - -/** - * All error instances in Node have additional methods and properties - * This export class is meant to be extended by these instances abstracting native JS error instances - */ -export class NodeErrorAbstraction extends Error { - code: string; - - constructor(name: string, code: string, message: string) { - super(message); - this.code = code; - this.name = name; - //This number changes dependending on the name of this class - //20 characters as of now - this.stack = this.stack && `${name} [${this.code}]${this.stack.slice(20)}`; - } - - toString() { - return `${this.name} [${this.code}]: ${this.message}`; - } -} - -export class NodeError extends NodeErrorAbstraction { - constructor(code: string, message: string) { - super(Error.prototype.name, code, message); - } -} - -export class NodeSyntaxError extends NodeErrorAbstraction - implements SyntaxError { - constructor(code: string, message: string) { - super(SyntaxError.prototype.name, code, message); - Object.setPrototypeOf(this, SyntaxError.prototype); - } -} - -export class NodeRangeError extends NodeErrorAbstraction { - constructor(code: string, message: string) { - super(RangeError.prototype.name, code, message); - Object.setPrototypeOf(this, RangeError.prototype); - } -} - -export class NodeTypeError extends NodeErrorAbstraction implements TypeError { - constructor(code: string, message: string) { - super(TypeError.prototype.name, code, message); - Object.setPrototypeOf(this, TypeError.prototype); - } -} - -export class NodeURIError extends NodeErrorAbstraction implements URIError { - constructor(code: string, message: string) { - super(URIError.prototype.name, code, message); - Object.setPrototypeOf(this, URIError.prototype); - } -} - -export class ERR_INVALID_ARG_TYPE extends NodeTypeError { - constructor(a1: string, a2: string | string[], a3: unknown) { - super( - "ERR_INVALID_ARG_TYPE", - `The "${a1}" argument must be of type ${ - typeof a2 === "string" - ? a2.toLocaleLowerCase() - : a2.map((x) => x.toLocaleLowerCase()).join(", ") - }. Received ${typeof a3} (${a3})`, - ); - } -} - -export class ERR_OUT_OF_RANGE extends RangeError { - code = "ERR_OUT_OF_RANGE"; - - constructor(str: string, range: string, received: unknown) { - super( - `The value of "${str}" is out of range. It must be ${range}. Received ${received}`, - ); - - const { name } = this; - // Add the error code to the name to include it in the stack trace. - this.name = `${name} [${this.code}]`; - // Access the stack to generate the error message including the error code from the name. - this.stack; - // Reset the name to the actual name. - this.name = name; - } -} - -export class ERR_AMBIGUOUS_ARGUMENT extends NodeTypeError { - constructor(x: string, y: string) { - super("ERR_AMBIGUOUS_ARGUMENT", `The "${x}" argument is ambiguous. ${y}`); - } -} - -export class ERR_ARG_NOT_ITERABLE extends NodeTypeError { - constructor(x: string) { - super("ERR_ARG_NOT_ITERABLE", `${x} must be iterable`); - } -} - -export class ERR_ASSERTION extends NodeError { - constructor(x: string) { - super("ERR_ASSERTION", `${x}`); - } -} - -export class ERR_ASYNC_CALLBACK extends NodeTypeError { - constructor(x: string) { - super("ERR_ASYNC_CALLBACK", `${x} must be a function`); - } -} - -export class ERR_ASYNC_TYPE extends NodeTypeError { - constructor(x: string) { - super("ERR_ASYNC_TYPE", `Invalid name for async "type": ${x}`); - } -} - -export class ERR_BROTLI_INVALID_PARAM extends NodeRangeError { - constructor(x: string) { - super("ERR_BROTLI_INVALID_PARAM", `${x} is not a valid Brotli parameter`); - } -} - -export class ERR_BUFFER_OUT_OF_BOUNDS extends NodeRangeError { - constructor(name?: string) { - super( - "ERR_BUFFER_OUT_OF_BOUNDS", - name - ? `"${name}" is outside of buffer bounds` - : "Attempt to access memory outside buffer bounds", - ); - } -} - -export class ERR_BUFFER_TOO_LARGE extends NodeRangeError { - constructor(x: string) { - super( - "ERR_BUFFER_TOO_LARGE", - `Cannot create a Buffer larger than ${x} bytes`, - ); - } -} - -export class ERR_CANNOT_WATCH_SIGINT extends NodeError { - constructor() { - super( - "ERR_CANNOT_WATCH_SIGINT", - "Cannot watch for SIGINT signals", - ); - } -} - -export class ERR_CHILD_CLOSED_BEFORE_REPLY extends NodeError { - constructor() { - super( - "ERR_CHILD_CLOSED_BEFORE_REPLY", - "Child closed before reply received", - ); - } -} - -export class ERR_CHILD_PROCESS_IPC_REQUIRED extends NodeError { - constructor(x: string) { - super( - "ERR_CHILD_PROCESS_IPC_REQUIRED", - `Forked processes must have an IPC channel, missing value 'ipc' in ${x}`, - ); - } -} - -export class ERR_CHILD_PROCESS_STDIO_MAXBUFFER extends NodeRangeError { - constructor(x: string) { - super( - "ERR_CHILD_PROCESS_STDIO_MAXBUFFER", - `${x} maxBuffer length exceeded`, - ); - } -} - -export class ERR_CONSOLE_WRITABLE_STREAM extends NodeTypeError { - constructor(x: string) { - super( - "ERR_CONSOLE_WRITABLE_STREAM", - `Console expects a writable stream instance for ${x}`, - ); - } -} - -export class ERR_CONTEXT_NOT_INITIALIZED extends NodeError { - constructor() { - super( - "ERR_CONTEXT_NOT_INITIALIZED", - "context used is not initialized", - ); - } -} - -export class ERR_CPU_USAGE extends NodeError { - constructor(x: string) { - super( - "ERR_CPU_USAGE", - `Unable to obtain cpu usage ${x}`, - ); - } -} - -export class ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED extends NodeError { - constructor() { - super( - "ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED", - "Custom engines not supported by this OpenSSL", - ); - } -} - -export class ERR_CRYPTO_ECDH_INVALID_FORMAT extends NodeTypeError { - constructor(x: string) { - super( - "ERR_CRYPTO_ECDH_INVALID_FORMAT", - `Invalid ECDH format: ${x}`, - ); - } -} - -export class ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY extends NodeError { - constructor() { - super( - "ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY", - "Public key is not valid for specified curve", - ); - } -} - -export class ERR_CRYPTO_ENGINE_UNKNOWN extends NodeError { - constructor(x: string) { - super( - "ERR_CRYPTO_ENGINE_UNKNOWN", - `Engine "${x}" was not found`, - ); - } -} - -export class ERR_CRYPTO_FIPS_FORCED extends NodeError { - constructor() { - super( - "ERR_CRYPTO_FIPS_FORCED", - "Cannot set FIPS mode, it was forced with --force-fips at startup.", - ); - } -} - -export class ERR_CRYPTO_FIPS_UNAVAILABLE extends NodeError { - constructor() { - super( - "ERR_CRYPTO_FIPS_UNAVAILABLE", - "Cannot set FIPS mode in a non-FIPS build.", - ); - } -} - -export class ERR_CRYPTO_HASH_FINALIZED extends NodeError { - constructor() { - super( - "ERR_CRYPTO_HASH_FINALIZED", - "Digest already called", - ); - } -} - -export class ERR_CRYPTO_HASH_UPDATE_FAILED extends NodeError { - constructor() { - super( - "ERR_CRYPTO_HASH_UPDATE_FAILED", - "Hash update failed", - ); - } -} - -export class ERR_CRYPTO_INCOMPATIBLE_KEY extends NodeError { - constructor(x: string, y: string) { - super( - "ERR_CRYPTO_INCOMPATIBLE_KEY", - `Incompatible ${x}: ${y}`, - ); - } -} - -export class ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS extends NodeError { - constructor(x: string, y: string) { - super( - "ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS", - `The selected key encoding ${x} ${y}.`, - ); - } -} - -export class ERR_CRYPTO_INVALID_DIGEST extends NodeTypeError { - constructor(x: string) { - super( - "ERR_CRYPTO_INVALID_DIGEST", - `Invalid digest: ${x}`, - ); - } -} - -export class ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE extends NodeTypeError { - constructor(x: string, y: string) { - super( - "ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE", - `Invalid key object type ${x}, expected ${y}.`, - ); - } -} - -export class ERR_CRYPTO_INVALID_STATE extends NodeError { - constructor(x: string) { - super( - "ERR_CRYPTO_INVALID_STATE", - `Invalid state for operation ${x}`, - ); - } -} - -export class ERR_CRYPTO_PBKDF2_ERROR extends NodeError { - constructor() { - super( - "ERR_CRYPTO_PBKDF2_ERROR", - "PBKDF2 error", - ); - } -} - -export class ERR_CRYPTO_SCRYPT_INVALID_PARAMETER extends NodeError { - constructor() { - super( - "ERR_CRYPTO_SCRYPT_INVALID_PARAMETER", - "Invalid scrypt parameter", - ); - } -} - -export class ERR_CRYPTO_SCRYPT_NOT_SUPPORTED extends NodeError { - constructor() { - super( - "ERR_CRYPTO_SCRYPT_NOT_SUPPORTED", - "Scrypt algorithm not supported", - ); - } -} - -export class ERR_CRYPTO_SIGN_KEY_REQUIRED extends NodeError { - constructor() { - super( - "ERR_CRYPTO_SIGN_KEY_REQUIRED", - "No key provided to sign", - ); - } -} - -export class ERR_DIR_CLOSED extends NodeError { - constructor() { - super( - "ERR_DIR_CLOSED", - "Directory handle was closed", - ); - } -} - -export class ERR_DIR_CONCURRENT_OPERATION extends NodeError { - constructor() { - super( - "ERR_DIR_CONCURRENT_OPERATION", - "Cannot do synchronous work on directory handle with concurrent asynchronous operations", - ); - } -} - -export class ERR_DNS_SET_SERVERS_FAILED extends NodeError { - constructor(x: string, y: string) { - super( - "ERR_DNS_SET_SERVERS_FAILED", - `c-ares failed to set servers: "${x}" [${y}]`, - ); - } -} - -export class ERR_DOMAIN_CALLBACK_NOT_AVAILABLE extends NodeError { - constructor() { - super( - "ERR_DOMAIN_CALLBACK_NOT_AVAILABLE", - "A callback was registered through " + - "process.setUncaughtExceptionCaptureCallback(), which is mutually " + - "exclusive with using the `domain` module", - ); - } -} - -export class ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE - extends NodeError { - constructor() { - super( - "ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE", - "The `domain` module is in use, which is mutually exclusive with calling " + - "process.setUncaughtExceptionCaptureCallback()", - ); - } -} - -export class ERR_ENCODING_INVALID_ENCODED_DATA extends NodeErrorAbstraction - implements TypeError { - errno: number; - constructor(encoding: string, ret: number) { - super( - TypeError.prototype.name, - "ERR_ENCODING_INVALID_ENCODED_DATA", - `The encoded data was not valid for encoding ${encoding}`, - ); - Object.setPrototypeOf(this, TypeError.prototype); - - this.errno = ret; - } -} - -// In Node these values are coming from libuv: -// Ref: https://github.com/libuv/libuv/blob/v1.x/include/uv/errno.h -// Ref: https://github.com/nodejs/node/blob/524123fbf064ff64bb6fcd83485cfc27db932f68/lib/internal/errors.js#L383 -// Since there is no easy way to port code from libuv and these maps are -// changing very rarely, we simply extract them from Node and store here. - -// Note -// Run the following to get the map: -// $ node -e "console.log(process.binding('uv').getErrorMap())" -// This setup automatically exports maps from both "win", "linux" & darwin: -// https://github.com/schwarzkopfb/node_errno_map - -type ErrMapData = Array<[number, [string, string]]>; - -const windows: ErrMapData = [ - [-4093, ["E2BIG", "argument list too long"]], - [-4092, ["EACCES", "permission denied"]], - [-4091, ["EADDRINUSE", "address already in use"]], - [-4090, ["EADDRNOTAVAIL", "address not available"]], - [-4089, ["EAFNOSUPPORT", "address family not supported"]], - [-4088, ["EAGAIN", "resource temporarily unavailable"]], - [-3000, ["EAI_ADDRFAMILY", "address family not supported"]], - [-3001, ["EAI_AGAIN", "temporary failure"]], - [-3002, ["EAI_BADFLAGS", "bad ai_flags value"]], - [-3013, ["EAI_BADHINTS", "invalid value for hints"]], - [-3003, ["EAI_CANCELED", "request canceled"]], - [-3004, ["EAI_FAIL", "permanent failure"]], - [-3005, ["EAI_FAMILY", "ai_family not supported"]], - [-3006, ["EAI_MEMORY", "out of memory"]], - [-3007, ["EAI_NODATA", "no address"]], - [-3008, ["EAI_NONAME", "unknown node or service"]], - [-3009, ["EAI_OVERFLOW", "argument buffer overflow"]], - [-3014, ["EAI_PROTOCOL", "resolved protocol is unknown"]], - [-3010, ["EAI_SERVICE", "service not available for socket type"]], - [-3011, ["EAI_SOCKTYPE", "socket type not supported"]], - [-4084, ["EALREADY", "connection already in progress"]], - [-4083, ["EBADF", "bad file descriptor"]], - [-4082, ["EBUSY", "resource busy or locked"]], - [-4081, ["ECANCELED", "operation canceled"]], - [-4080, ["ECHARSET", "invalid Unicode character"]], - [-4079, ["ECONNABORTED", "software caused connection abort"]], - [-4078, ["ECONNREFUSED", "connection refused"]], - [-4077, ["ECONNRESET", "connection reset by peer"]], - [-4076, ["EDESTADDRREQ", "destination address required"]], - [-4075, ["EEXIST", "file already exists"]], - [-4074, ["EFAULT", "bad address in system call argument"]], - [-4036, ["EFBIG", "file too large"]], - [-4073, ["EHOSTUNREACH", "host is unreachable"]], - [-4072, ["EINTR", "interrupted system call"]], - [-4071, ["EINVAL", "invalid argument"]], - [-4070, ["EIO", "i/o error"]], - [-4069, ["EISCONN", "socket is already connected"]], - [-4068, ["EISDIR", "illegal operation on a directory"]], - [-4067, ["ELOOP", "too many symbolic links encountered"]], - [-4066, ["EMFILE", "too many open files"]], - [-4065, ["EMSGSIZE", "message too long"]], - [-4064, ["ENAMETOOLONG", "name too long"]], - [-4063, ["ENETDOWN", "network is down"]], - [-4062, ["ENETUNREACH", "network is unreachable"]], - [-4061, ["ENFILE", "file table overflow"]], - [-4060, ["ENOBUFS", "no buffer space available"]], - [-4059, ["ENODEV", "no such device"]], - [-4058, ["ENOENT", "no such file or directory"]], - [-4057, ["ENOMEM", "not enough memory"]], - [-4056, ["ENONET", "machine is not on the network"]], - [-4035, ["ENOPROTOOPT", "protocol not available"]], - [-4055, ["ENOSPC", "no space left on device"]], - [-4054, ["ENOSYS", "function not implemented"]], - [-4053, ["ENOTCONN", "socket is not connected"]], - [-4052, ["ENOTDIR", "not a directory"]], - [-4051, ["ENOTEMPTY", "directory not empty"]], - [-4050, ["ENOTSOCK", "socket operation on non-socket"]], - [-4049, ["ENOTSUP", "operation not supported on socket"]], - [-4048, ["EPERM", "operation not permitted"]], - [-4047, ["EPIPE", "broken pipe"]], - [-4046, ["EPROTO", "protocol error"]], - [-4045, ["EPROTONOSUPPORT", "protocol not supported"]], - [-4044, ["EPROTOTYPE", "protocol wrong type for socket"]], - [-4034, ["ERANGE", "result too large"]], - [-4043, ["EROFS", "read-only file system"]], - [-4042, ["ESHUTDOWN", "cannot send after transport endpoint shutdown"]], - [-4041, ["ESPIPE", "invalid seek"]], - [-4040, ["ESRCH", "no such process"]], - [-4039, ["ETIMEDOUT", "connection timed out"]], - [-4038, ["ETXTBSY", "text file is busy"]], - [-4037, ["EXDEV", "cross-device link not permitted"]], - [-4094, ["UNKNOWN", "unknown error"]], - [-4095, ["EOF", "end of file"]], - [-4033, ["ENXIO", "no such device or address"]], - [-4032, ["EMLINK", "too many links"]], - [-4031, ["EHOSTDOWN", "host is down"]], - [-4030, ["EREMOTEIO", "remote I/O error"]], - [-4029, ["ENOTTY", "inappropriate ioctl for device"]], - [-4028, ["EFTYPE", "inappropriate file type or format"]], - [-4027, ["EILSEQ", "illegal byte sequence"]], -]; - -const darwin: ErrMapData = [ - [-7, ["E2BIG", "argument list too long"]], - [-13, ["EACCES", "permission denied"]], - [-48, ["EADDRINUSE", "address already in use"]], - [-49, ["EADDRNOTAVAIL", "address not available"]], - [-47, ["EAFNOSUPPORT", "address family not supported"]], - [-35, ["EAGAIN", "resource temporarily unavailable"]], - [-3000, ["EAI_ADDRFAMILY", "address family not supported"]], - [-3001, ["EAI_AGAIN", "temporary failure"]], - [-3002, ["EAI_BADFLAGS", "bad ai_flags value"]], - [-3013, ["EAI_BADHINTS", "invalid value for hints"]], - [-3003, ["EAI_CANCELED", "request canceled"]], - [-3004, ["EAI_FAIL", "permanent failure"]], - [-3005, ["EAI_FAMILY", "ai_family not supported"]], - [-3006, ["EAI_MEMORY", "out of memory"]], - [-3007, ["EAI_NODATA", "no address"]], - [-3008, ["EAI_NONAME", "unknown node or service"]], - [-3009, ["EAI_OVERFLOW", "argument buffer overflow"]], - [-3014, ["EAI_PROTOCOL", "resolved protocol is unknown"]], - [-3010, ["EAI_SERVICE", "service not available for socket type"]], - [-3011, ["EAI_SOCKTYPE", "socket type not supported"]], - [-37, ["EALREADY", "connection already in progress"]], - [-9, ["EBADF", "bad file descriptor"]], - [-16, ["EBUSY", "resource busy or locked"]], - [-89, ["ECANCELED", "operation canceled"]], - [-4080, ["ECHARSET", "invalid Unicode character"]], - [-53, ["ECONNABORTED", "software caused connection abort"]], - [-61, ["ECONNREFUSED", "connection refused"]], - [-54, ["ECONNRESET", "connection reset by peer"]], - [-39, ["EDESTADDRREQ", "destination address required"]], - [-17, ["EEXIST", "file already exists"]], - [-14, ["EFAULT", "bad address in system call argument"]], - [-27, ["EFBIG", "file too large"]], - [-65, ["EHOSTUNREACH", "host is unreachable"]], - [-4, ["EINTR", "interrupted system call"]], - [-22, ["EINVAL", "invalid argument"]], - [-5, ["EIO", "i/o error"]], - [-56, ["EISCONN", "socket is already connected"]], - [-21, ["EISDIR", "illegal operation on a directory"]], - [-62, ["ELOOP", "too many symbolic links encountered"]], - [-24, ["EMFILE", "too many open files"]], - [-40, ["EMSGSIZE", "message too long"]], - [-63, ["ENAMETOOLONG", "name too long"]], - [-50, ["ENETDOWN", "network is down"]], - [-51, ["ENETUNREACH", "network is unreachable"]], - [-23, ["ENFILE", "file table overflow"]], - [-55, ["ENOBUFS", "no buffer space available"]], - [-19, ["ENODEV", "no such device"]], - [-2, ["ENOENT", "no such file or directory"]], - [-12, ["ENOMEM", "not enough memory"]], - [-4056, ["ENONET", "machine is not on the network"]], - [-42, ["ENOPROTOOPT", "protocol not available"]], - [-28, ["ENOSPC", "no space left on device"]], - [-78, ["ENOSYS", "function not implemented"]], - [-57, ["ENOTCONN", "socket is not connected"]], - [-20, ["ENOTDIR", "not a directory"]], - [-66, ["ENOTEMPTY", "directory not empty"]], - [-38, ["ENOTSOCK", "socket operation on non-socket"]], - [-45, ["ENOTSUP", "operation not supported on socket"]], - [-1, ["EPERM", "operation not permitted"]], - [-32, ["EPIPE", "broken pipe"]], - [-100, ["EPROTO", "protocol error"]], - [-43, ["EPROTONOSUPPORT", "protocol not supported"]], - [-41, ["EPROTOTYPE", "protocol wrong type for socket"]], - [-34, ["ERANGE", "result too large"]], - [-30, ["EROFS", "read-only file system"]], - [-58, ["ESHUTDOWN", "cannot send after transport endpoint shutdown"]], - [-29, ["ESPIPE", "invalid seek"]], - [-3, ["ESRCH", "no such process"]], - [-60, ["ETIMEDOUT", "connection timed out"]], - [-26, ["ETXTBSY", "text file is busy"]], - [-18, ["EXDEV", "cross-device link not permitted"]], - [-4094, ["UNKNOWN", "unknown error"]], - [-4095, ["EOF", "end of file"]], - [-6, ["ENXIO", "no such device or address"]], - [-31, ["EMLINK", "too many links"]], - [-64, ["EHOSTDOWN", "host is down"]], - [-4030, ["EREMOTEIO", "remote I/O error"]], - [-25, ["ENOTTY", "inappropriate ioctl for device"]], - [-79, ["EFTYPE", "inappropriate file type or format"]], - [-92, ["EILSEQ", "illegal byte sequence"]], -]; - -const linux: ErrMapData = [ - [-7, ["E2BIG", "argument list too long"]], - [-13, ["EACCES", "permission denied"]], - [-98, ["EADDRINUSE", "address already in use"]], - [-99, ["EADDRNOTAVAIL", "address not available"]], - [-97, ["EAFNOSUPPORT", "address family not supported"]], - [-11, ["EAGAIN", "resource temporarily unavailable"]], - [-3000, ["EAI_ADDRFAMILY", "address family not supported"]], - [-3001, ["EAI_AGAIN", "temporary failure"]], - [-3002, ["EAI_BADFLAGS", "bad ai_flags value"]], - [-3013, ["EAI_BADHINTS", "invalid value for hints"]], - [-3003, ["EAI_CANCELED", "request canceled"]], - [-3004, ["EAI_FAIL", "permanent failure"]], - [-3005, ["EAI_FAMILY", "ai_family not supported"]], - [-3006, ["EAI_MEMORY", "out of memory"]], - [-3007, ["EAI_NODATA", "no address"]], - [-3008, ["EAI_NONAME", "unknown node or service"]], - [-3009, ["EAI_OVERFLOW", "argument buffer overflow"]], - [-3014, ["EAI_PROTOCOL", "resolved protocol is unknown"]], - [-3010, ["EAI_SERVICE", "service not available for socket type"]], - [-3011, ["EAI_SOCKTYPE", "socket type not supported"]], - [-114, ["EALREADY", "connection already in progress"]], - [-9, ["EBADF", "bad file descriptor"]], - [-16, ["EBUSY", "resource busy or locked"]], - [-125, ["ECANCELED", "operation canceled"]], - [-4080, ["ECHARSET", "invalid Unicode character"]], - [-103, ["ECONNABORTED", "software caused connection abort"]], - [-111, ["ECONNREFUSED", "connection refused"]], - [-104, ["ECONNRESET", "connection reset by peer"]], - [-89, ["EDESTADDRREQ", "destination address required"]], - [-17, ["EEXIST", "file already exists"]], - [-14, ["EFAULT", "bad address in system call argument"]], - [-27, ["EFBIG", "file too large"]], - [-113, ["EHOSTUNREACH", "host is unreachable"]], - [-4, ["EINTR", "interrupted system call"]], - [-22, ["EINVAL", "invalid argument"]], - [-5, ["EIO", "i/o error"]], - [-106, ["EISCONN", "socket is already connected"]], - [-21, ["EISDIR", "illegal operation on a directory"]], - [-40, ["ELOOP", "too many symbolic links encountered"]], - [-24, ["EMFILE", "too many open files"]], - [-90, ["EMSGSIZE", "message too long"]], - [-36, ["ENAMETOOLONG", "name too long"]], - [-100, ["ENETDOWN", "network is down"]], - [-101, ["ENETUNREACH", "network is unreachable"]], - [-23, ["ENFILE", "file table overflow"]], - [-105, ["ENOBUFS", "no buffer space available"]], - [-19, ["ENODEV", "no such device"]], - [-2, ["ENOENT", "no such file or directory"]], - [-12, ["ENOMEM", "not enough memory"]], - [-64, ["ENONET", "machine is not on the network"]], - [-92, ["ENOPROTOOPT", "protocol not available"]], - [-28, ["ENOSPC", "no space left on device"]], - [-38, ["ENOSYS", "function not implemented"]], - [-107, ["ENOTCONN", "socket is not connected"]], - [-20, ["ENOTDIR", "not a directory"]], - [-39, ["ENOTEMPTY", "directory not empty"]], - [-88, ["ENOTSOCK", "socket operation on non-socket"]], - [-95, ["ENOTSUP", "operation not supported on socket"]], - [-1, ["EPERM", "operation not permitted"]], - [-32, ["EPIPE", "broken pipe"]], - [-71, ["EPROTO", "protocol error"]], - [-93, ["EPROTONOSUPPORT", "protocol not supported"]], - [-91, ["EPROTOTYPE", "protocol wrong type for socket"]], - [-34, ["ERANGE", "result too large"]], - [-30, ["EROFS", "read-only file system"]], - [-108, ["ESHUTDOWN", "cannot send after transport endpoint shutdown"]], - [-29, ["ESPIPE", "invalid seek"]], - [-3, ["ESRCH", "no such process"]], - [-110, ["ETIMEDOUT", "connection timed out"]], - [-26, ["ETXTBSY", "text file is busy"]], - [-18, ["EXDEV", "cross-device link not permitted"]], - [-4094, ["UNKNOWN", "unknown error"]], - [-4095, ["EOF", "end of file"]], - [-6, ["ENXIO", "no such device or address"]], - [-31, ["EMLINK", "too many links"]], - [-112, ["EHOSTDOWN", "host is down"]], - [-121, ["EREMOTEIO", "remote I/O error"]], - [-25, ["ENOTTY", "inappropriate ioctl for device"]], - [-4028, ["EFTYPE", "inappropriate file type or format"]], - [-84, ["EILSEQ", "illegal byte sequence"]], -]; - -const { os } = Deno.build; -export const errorMap = new Map( - os === "windows" - ? windows - : os === "darwin" - ? darwin - : os === "linux" - ? linux - : unreachable(), -); -export class ERR_ENCODING_NOT_SUPPORTED extends NodeRangeError { - constructor(x: string) { - super( - "ERR_ENCODING_NOT_SUPPORTED", - `The "${x}" encoding is not supported`, - ); - } -} -export class ERR_EVAL_ESM_CANNOT_PRINT extends NodeError { - constructor() { - super( - "ERR_EVAL_ESM_CANNOT_PRINT", - `--print cannot be used with ESM input`, - ); - } -} -export class ERR_EVENT_RECURSION extends NodeError { - constructor(x: string) { - super( - "ERR_EVENT_RECURSION", - `The event "${x}" is already being dispatched`, - ); - } -} -export class ERR_FEATURE_UNAVAILABLE_ON_PLATFORM extends NodeTypeError { - constructor(x: string) { - super( - "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM", - `The feature ${x} is unavailable on the current platform, which is being used to run Node.js`, - ); - } -} -export class ERR_FS_FILE_TOO_LARGE extends NodeRangeError { - constructor(x: string) { - super( - "ERR_FS_FILE_TOO_LARGE", - `File size (${x}) is greater than 2 GB`, - ); - } -} -export class ERR_FS_INVALID_SYMLINK_TYPE extends NodeError { - constructor(x: string) { - super( - "ERR_FS_INVALID_SYMLINK_TYPE", - `Symlink type must be one of "dir", "file", or "junction". Received "${x}"`, - ); - } -} -export class ERR_HTTP2_ALTSVC_INVALID_ORIGIN extends NodeTypeError { - constructor() { - super( - "ERR_HTTP2_ALTSVC_INVALID_ORIGIN", - `HTTP/2 ALTSVC frames require a valid origin`, - ); - } -} -export class ERR_HTTP2_ALTSVC_LENGTH extends NodeTypeError { - constructor() { - super( - "ERR_HTTP2_ALTSVC_LENGTH", - `HTTP/2 ALTSVC frames are limited to 16382 bytes`, - ); - } -} -export class ERR_HTTP2_CONNECT_AUTHORITY extends NodeError { - constructor() { - super( - "ERR_HTTP2_CONNECT_AUTHORITY", - `:authority header is required for CONNECT requests`, - ); - } -} -export class ERR_HTTP2_CONNECT_PATH extends NodeError { - constructor() { - super( - "ERR_HTTP2_CONNECT_PATH", - `The :path header is forbidden for CONNECT requests`, - ); - } -} -export class ERR_HTTP2_CONNECT_SCHEME extends NodeError { - constructor() { - super( - "ERR_HTTP2_CONNECT_SCHEME", - `The :scheme header is forbidden for CONNECT requests`, - ); - } -} -export class ERR_HTTP2_GOAWAY_SESSION extends NodeError { - constructor() { - super( - "ERR_HTTP2_GOAWAY_SESSION", - `New streams cannot be created after receiving a GOAWAY`, - ); - } -} -export class ERR_HTTP2_HEADERS_AFTER_RESPOND extends NodeError { - constructor() { - super( - "ERR_HTTP2_HEADERS_AFTER_RESPOND", - `Cannot specify additional headers after response initiated`, - ); - } -} -export class ERR_HTTP2_HEADERS_SENT extends NodeError { - constructor() { - super( - "ERR_HTTP2_HEADERS_SENT", - `Response has already been initiated.`, - ); - } -} -export class ERR_HTTP2_HEADER_SINGLE_VALUE extends NodeTypeError { - constructor(x: string) { - super( - "ERR_HTTP2_HEADER_SINGLE_VALUE", - `Header field "${x}" must only have a single value`, - ); - } -} -export class ERR_HTTP2_INFO_STATUS_NOT_ALLOWED extends NodeRangeError { - constructor() { - super( - "ERR_HTTP2_INFO_STATUS_NOT_ALLOWED", - `Informational status codes cannot be used`, - ); - } -} -export class ERR_HTTP2_INVALID_CONNECTION_HEADERS extends NodeTypeError { - constructor(x: string) { - super( - "ERR_HTTP2_INVALID_CONNECTION_HEADERS", - `HTTP/1 Connection specific headers are forbidden: "${x}"`, - ); - } -} -export class ERR_HTTP2_INVALID_HEADER_VALUE extends NodeTypeError { - constructor(x: string, y: string) { - super( - "ERR_HTTP2_INVALID_HEADER_VALUE", - `Invalid value "${x}" for header "${y}"`, - ); - } -} -export class ERR_HTTP2_INVALID_INFO_STATUS extends NodeRangeError { - constructor(x: string) { - super( - "ERR_HTTP2_INVALID_INFO_STATUS", - `Invalid informational status code: ${x}`, - ); - } -} -export class ERR_HTTP2_INVALID_ORIGIN extends NodeTypeError { - constructor() { - super( - "ERR_HTTP2_INVALID_ORIGIN", - `HTTP/2 ORIGIN frames require a valid origin`, - ); - } -} -export class ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH extends NodeRangeError { - constructor() { - super( - "ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH", - `Packed settings length must be a multiple of six`, - ); - } -} -export class ERR_HTTP2_INVALID_PSEUDOHEADER extends NodeTypeError { - constructor(x: string) { - super( - "ERR_HTTP2_INVALID_PSEUDOHEADER", - `"${x}" is an invalid pseudoheader or is used incorrectly`, - ); - } -} -export class ERR_HTTP2_INVALID_SESSION extends NodeError { - constructor() { - super( - "ERR_HTTP2_INVALID_SESSION", - `The session has been destroyed`, - ); - } -} -export class ERR_HTTP2_INVALID_STREAM extends NodeError { - constructor() { - super( - "ERR_HTTP2_INVALID_STREAM", - `The stream has been destroyed`, - ); - } -} -export class ERR_HTTP2_MAX_PENDING_SETTINGS_ACK extends NodeError { - constructor() { - super( - "ERR_HTTP2_MAX_PENDING_SETTINGS_ACK", - `Maximum number of pending settings acknowledgements`, - ); - } -} -export class ERR_HTTP2_NESTED_PUSH extends NodeError { - constructor() { - super( - "ERR_HTTP2_NESTED_PUSH", - `A push stream cannot initiate another push stream.`, - ); - } -} -export class ERR_HTTP2_NO_SOCKET_MANIPULATION extends NodeError { - constructor() { - super( - "ERR_HTTP2_NO_SOCKET_MANIPULATION", - `HTTP/2 sockets should not be directly manipulated (e.g. read and written)`, - ); - } -} -export class ERR_HTTP2_ORIGIN_LENGTH extends NodeTypeError { - constructor() { - super( - "ERR_HTTP2_ORIGIN_LENGTH", - `HTTP/2 ORIGIN frames are limited to 16382 bytes`, - ); - } -} -export class ERR_HTTP2_OUT_OF_STREAMS extends NodeError { - constructor() { - super( - "ERR_HTTP2_OUT_OF_STREAMS", - `No stream ID is available because maximum stream ID has been reached`, - ); - } -} -export class ERR_HTTP2_PAYLOAD_FORBIDDEN extends NodeError { - constructor(x: string) { - super( - "ERR_HTTP2_PAYLOAD_FORBIDDEN", - `Responses with ${x} status must not have a payload`, - ); - } -} -export class ERR_HTTP2_PING_CANCEL extends NodeError { - constructor() { - super( - "ERR_HTTP2_PING_CANCEL", - `HTTP2 ping cancelled`, - ); - } -} -export class ERR_HTTP2_PING_LENGTH extends NodeRangeError { - constructor() { - super( - "ERR_HTTP2_PING_LENGTH", - `HTTP2 ping payload must be 8 bytes`, - ); - } -} -export class ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED extends NodeTypeError { - constructor() { - super( - "ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED", - `Cannot set HTTP/2 pseudo-headers`, - ); - } -} -export class ERR_HTTP2_PUSH_DISABLED extends NodeError { - constructor() { - super( - "ERR_HTTP2_PUSH_DISABLED", - `HTTP/2 client has disabled push streams`, - ); - } -} -export class ERR_HTTP2_SEND_FILE extends NodeError { - constructor() { - super( - "ERR_HTTP2_SEND_FILE", - `Directories cannot be sent`, - ); - } -} -export class ERR_HTTP2_SEND_FILE_NOSEEK extends NodeError { - constructor() { - super( - "ERR_HTTP2_SEND_FILE_NOSEEK", - `Offset or length can only be specified for regular files`, - ); - } -} -export class ERR_HTTP2_SESSION_ERROR extends NodeError { - constructor(x: string) { - super( - "ERR_HTTP2_SESSION_ERROR", - `Session closed with error code ${x}`, - ); - } -} -export class ERR_HTTP2_SETTINGS_CANCEL extends NodeError { - constructor() { - super( - "ERR_HTTP2_SETTINGS_CANCEL", - `HTTP2 session settings canceled`, - ); - } -} -export class ERR_HTTP2_SOCKET_BOUND extends NodeError { - constructor() { - super( - "ERR_HTTP2_SOCKET_BOUND", - `The socket is already bound to an Http2Session`, - ); - } -} -export class ERR_HTTP2_SOCKET_UNBOUND extends NodeError { - constructor() { - super( - "ERR_HTTP2_SOCKET_UNBOUND", - `The socket has been disconnected from the Http2Session`, - ); - } -} -export class ERR_HTTP2_STATUS_101 extends NodeError { - constructor() { - super( - "ERR_HTTP2_STATUS_101", - `HTTP status code 101 (Switching Protocols) is forbidden in HTTP/2`, - ); - } -} -export class ERR_HTTP2_STATUS_INVALID extends NodeRangeError { - constructor(x: string) { - super( - "ERR_HTTP2_STATUS_INVALID", - `Invalid status code: ${x}`, - ); - } -} -export class ERR_HTTP2_STREAM_ERROR extends NodeError { - constructor(x: string) { - super( - "ERR_HTTP2_STREAM_ERROR", - `Stream closed with error code ${x}`, - ); - } -} -export class ERR_HTTP2_STREAM_SELF_DEPENDENCY extends NodeError { - constructor() { - super( - "ERR_HTTP2_STREAM_SELF_DEPENDENCY", - `A stream cannot depend on itself`, - ); - } -} -export class ERR_HTTP2_TRAILERS_ALREADY_SENT extends NodeError { - constructor() { - super( - "ERR_HTTP2_TRAILERS_ALREADY_SENT", - `Trailing headers have already been sent`, - ); - } -} -export class ERR_HTTP2_TRAILERS_NOT_READY extends NodeError { - constructor() { - super( - "ERR_HTTP2_TRAILERS_NOT_READY", - `Trailing headers cannot be sent until after the wantTrailers event is emitted`, - ); - } -} -export class ERR_HTTP2_UNSUPPORTED_PROTOCOL extends NodeError { - constructor(x: string) { - super( - "ERR_HTTP2_UNSUPPORTED_PROTOCOL", - `protocol "${x}" is unsupported.`, - ); - } -} -export class ERR_HTTP_HEADERS_SENT extends NodeError { - constructor(x: string) { - super( - "ERR_HTTP_HEADERS_SENT", - `Cannot ${x} headers after they are sent to the client`, - ); - } -} -export class ERR_HTTP_INVALID_HEADER_VALUE extends NodeTypeError { - constructor(x: string, y: string) { - super( - "ERR_HTTP_INVALID_HEADER_VALUE", - `Invalid value "${x}" for header "${y}"`, - ); - } -} -export class ERR_HTTP_INVALID_STATUS_CODE extends NodeRangeError { - constructor(x: string) { - super( - "ERR_HTTP_INVALID_STATUS_CODE", - `Invalid status code: ${x}`, - ); - } -} -export class ERR_HTTP_SOCKET_ENCODING extends NodeError { - constructor() { - super( - "ERR_HTTP_SOCKET_ENCODING", - `Changing the socket encoding is not allowed per RFC7230 Section 3.`, - ); - } -} -export class ERR_HTTP_TRAILER_INVALID extends NodeError { - constructor() { - super( - "ERR_HTTP_TRAILER_INVALID", - `Trailers are invalid with this transfer encoding`, - ); - } -} -export class ERR_INCOMPATIBLE_OPTION_PAIR extends NodeTypeError { - constructor(x: string, y: string) { - super( - "ERR_INCOMPATIBLE_OPTION_PAIR", - `Option "${x}" cannot be used in combination with option "${y}"`, - ); - } -} -export class ERR_INPUT_TYPE_NOT_ALLOWED extends NodeError { - constructor() { - super( - "ERR_INPUT_TYPE_NOT_ALLOWED", - `--input-type can only be used with string input via --eval, --print, or STDIN`, - ); - } -} -export class ERR_INSPECTOR_ALREADY_ACTIVATED extends NodeError { - constructor() { - super( - "ERR_INSPECTOR_ALREADY_ACTIVATED", - `Inspector is already activated. Close it with inspector.close() before activating it again.`, - ); - } -} -export class ERR_INSPECTOR_ALREADY_CONNECTED extends NodeError { - constructor(x: string) { - super( - "ERR_INSPECTOR_ALREADY_CONNECTED", - `${x} is already connected`, - ); - } -} -export class ERR_INSPECTOR_CLOSED extends NodeError { - constructor() { - super( - "ERR_INSPECTOR_CLOSED", - `Session was closed`, - ); - } -} -export class ERR_INSPECTOR_COMMAND extends NodeError { - constructor(x: number, y: string) { - super( - "ERR_INSPECTOR_COMMAND", - `Inspector error ${x}: ${y}`, - ); - } -} -export class ERR_INSPECTOR_NOT_ACTIVE extends NodeError { - constructor() { - super( - "ERR_INSPECTOR_NOT_ACTIVE", - `Inspector is not active`, - ); - } -} -export class ERR_INSPECTOR_NOT_AVAILABLE extends NodeError { - constructor() { - super( - "ERR_INSPECTOR_NOT_AVAILABLE", - `Inspector is not available`, - ); - } -} -export class ERR_INSPECTOR_NOT_CONNECTED extends NodeError { - constructor() { - super( - "ERR_INSPECTOR_NOT_CONNECTED", - `Session is not connected`, - ); - } -} -export class ERR_INSPECTOR_NOT_WORKER extends NodeError { - constructor() { - super( - "ERR_INSPECTOR_NOT_WORKER", - `Current thread is not a worker`, - ); - } -} -export class ERR_INVALID_ASYNC_ID extends NodeRangeError { - constructor(x: string, y: string) { - super( - "ERR_INVALID_ASYNC_ID", - `Invalid ${x} value: ${y}`, - ); - } -} -export class ERR_INVALID_BUFFER_SIZE extends NodeRangeError { - constructor(x: string) { - super( - "ERR_INVALID_BUFFER_SIZE", - `Buffer size must be a multiple of ${x}`, - ); - } -} -export class ERR_INVALID_CALLBACK extends NodeTypeError { - constructor(object: unknown) { - super( - "ERR_INVALID_CALLBACK", - `Callback must be a function. Received ${JSON.stringify(object)}`, - ); - } -} -export class ERR_INVALID_CURSOR_POS extends NodeTypeError { - constructor() { - super( - "ERR_INVALID_CURSOR_POS", - `Cannot set cursor row without setting its column`, - ); - } -} -export class ERR_INVALID_FD extends NodeRangeError { - constructor(x: string) { - super( - "ERR_INVALID_FD", - `"fd" must be a positive integer: ${x}`, - ); - } -} -export class ERR_INVALID_FD_TYPE extends NodeTypeError { - constructor(x: string) { - super( - "ERR_INVALID_FD_TYPE", - `Unsupported fd type: ${x}`, - ); - } -} -export class ERR_INVALID_FILE_URL_HOST extends NodeTypeError { - constructor(x: string) { - super( - "ERR_INVALID_FILE_URL_HOST", - `File URL host must be "localhost" or empty on ${x}`, - ); - } -} -export class ERR_INVALID_FILE_URL_PATH extends NodeTypeError { - constructor(x: string) { - super( - "ERR_INVALID_FILE_URL_PATH", - `File URL path ${x}`, - ); - } -} -export class ERR_INVALID_HANDLE_TYPE extends NodeTypeError { - constructor() { - super( - "ERR_INVALID_HANDLE_TYPE", - `This handle type cannot be sent`, - ); - } -} -export class ERR_INVALID_HTTP_TOKEN extends NodeTypeError { - constructor(x: string, y: string) { - super( - "ERR_INVALID_HTTP_TOKEN", - `${x} must be a valid HTTP token ["${y}"]`, - ); - } -} -export class ERR_INVALID_IP_ADDRESS extends NodeTypeError { - constructor(x: string) { - super( - "ERR_INVALID_IP_ADDRESS", - `Invalid IP address: ${x}`, - ); - } -} -export class ERR_INVALID_OPT_VALUE_ENCODING extends NodeTypeError { - constructor(x: string) { - super( - "ERR_INVALID_OPT_VALUE_ENCODING", - `The value "${x}" is invalid for option "encoding"`, - ); - } -} -export class ERR_INVALID_PERFORMANCE_MARK extends NodeError { - constructor(x: string) { - super( - "ERR_INVALID_PERFORMANCE_MARK", - `The "${x}" performance mark has not been set`, - ); - } -} -export class ERR_INVALID_PROTOCOL extends NodeTypeError { - constructor(x: string, y: string) { - super( - "ERR_INVALID_PROTOCOL", - `Protocol "${x}" not supported. Expected "${y}"`, - ); - } -} -export class ERR_INVALID_REPL_EVAL_CONFIG extends NodeTypeError { - constructor() { - super( - "ERR_INVALID_REPL_EVAL_CONFIG", - `Cannot specify both "breakEvalOnSigint" and "eval" for REPL`, - ); - } -} -export class ERR_INVALID_REPL_INPUT extends NodeTypeError { - constructor(x: string) { - super( - "ERR_INVALID_REPL_INPUT", - `${x}`, - ); - } -} -export class ERR_INVALID_SYNC_FORK_INPUT extends NodeTypeError { - constructor(x: string) { - super( - "ERR_INVALID_SYNC_FORK_INPUT", - `Asynchronous forks do not support Buffer, TypedArray, DataView or string input: ${x}`, - ); - } -} -export class ERR_INVALID_THIS extends NodeTypeError { - constructor(x: string) { - super( - "ERR_INVALID_THIS", - `Value of "this" must be of type ${x}`, - ); - } -} -export class ERR_INVALID_TUPLE extends NodeTypeError { - constructor(x: string, y: string) { - super( - "ERR_INVALID_TUPLE", - `${x} must be an iterable ${y} tuple`, - ); - } -} -export class ERR_INVALID_URI extends NodeURIError { - constructor() { - super( - "ERR_INVALID_URI", - `URI malformed`, - ); - } -} -export class ERR_IPC_CHANNEL_CLOSED extends NodeError { - constructor() { - super( - "ERR_IPC_CHANNEL_CLOSED", - `Channel closed`, - ); - } -} -export class ERR_IPC_DISCONNECTED extends NodeError { - constructor() { - super( - "ERR_IPC_DISCONNECTED", - `IPC channel is already disconnected`, - ); - } -} -export class ERR_IPC_ONE_PIPE extends NodeError { - constructor() { - super( - "ERR_IPC_ONE_PIPE", - `Child process can have only one IPC pipe`, - ); - } -} -export class ERR_IPC_SYNC_FORK extends NodeError { - constructor() { - super( - "ERR_IPC_SYNC_FORK", - `IPC cannot be used with synchronous forks`, - ); - } -} -export class ERR_MANIFEST_DEPENDENCY_MISSING extends NodeError { - constructor(x: string, y: string) { - super( - "ERR_MANIFEST_DEPENDENCY_MISSING", - `Manifest resource ${x} does not list ${y} as a dependency specifier`, - ); - } -} -export class ERR_MANIFEST_INTEGRITY_MISMATCH extends NodeSyntaxError { - constructor(x: string) { - super( - "ERR_MANIFEST_INTEGRITY_MISMATCH", - `Manifest resource ${x} has multiple entries but integrity lists do not match`, - ); - } -} -export class ERR_MANIFEST_INVALID_RESOURCE_FIELD extends NodeTypeError { - constructor(x: string, y: string) { - super( - "ERR_MANIFEST_INVALID_RESOURCE_FIELD", - `Manifest resource ${x} has invalid property value for ${y}`, - ); - } -} -export class ERR_MANIFEST_TDZ extends NodeError { - constructor() { - super( - "ERR_MANIFEST_TDZ", - `Manifest initialization has not yet run`, - ); - } -} -export class ERR_MANIFEST_UNKNOWN_ONERROR extends NodeSyntaxError { - constructor(x: string) { - super( - "ERR_MANIFEST_UNKNOWN_ONERROR", - `Manifest specified unknown error behavior "${x}".`, - ); - } -} -export class ERR_METHOD_NOT_IMPLEMENTED extends NodeError { - constructor(x: string) { - super( - "ERR_METHOD_NOT_IMPLEMENTED", - `The ${x} method is not implemented`, - ); - } -} -export class ERR_MISSING_ARGS extends NodeTypeError { - constructor(...args: string[]) { - args = args.map((a) => `"${a}"`); - - let msg = "The "; - switch (args.length) { - case 1: - msg += `${args[0]} argument`; - break; - case 2: - msg += `${args[0]} and ${args[1]} arguments`; - break; - default: - msg += args.slice(0, args.length - 1).join(", "); - msg += `, and ${args[args.length - 1]} arguments`; - break; - } - super( - "ERR_MISSING_ARGS", - `${msg} must be specified`, - ); - } -} -export class ERR_MISSING_OPTION extends NodeTypeError { - constructor(x: string) { - super( - "ERR_MISSING_OPTION", - `${x} is required`, - ); - } -} -export class ERR_MULTIPLE_CALLBACK extends NodeError { - constructor() { - super( - "ERR_MULTIPLE_CALLBACK", - `Callback called multiple times`, - ); - } -} -export class ERR_NAPI_CONS_FUNCTION extends NodeTypeError { - constructor() { - super( - "ERR_NAPI_CONS_FUNCTION", - `Constructor must be a function`, - ); - } -} -export class ERR_NAPI_INVALID_DATAVIEW_ARGS extends NodeRangeError { - constructor() { - super( - "ERR_NAPI_INVALID_DATAVIEW_ARGS", - `byte_offset + byte_length should be less than or equal to the size in bytes of the array passed in`, - ); - } -} -export class ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT extends NodeRangeError { - constructor(x: string, y: string) { - super( - "ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT", - `start offset of ${x} should be a multiple of ${y}`, - ); - } -} -export class ERR_NAPI_INVALID_TYPEDARRAY_LENGTH extends NodeRangeError { - constructor() { - super( - "ERR_NAPI_INVALID_TYPEDARRAY_LENGTH", - `Invalid typed array length`, - ); - } -} -export class ERR_NO_CRYPTO extends NodeError { - constructor() { - super( - "ERR_NO_CRYPTO", - `Node.js is not compiled with OpenSSL crypto support`, - ); - } -} -export class ERR_NO_ICU extends NodeTypeError { - constructor(x: string) { - super( - "ERR_NO_ICU", - `${x} is not supported on Node.js compiled without ICU`, - ); - } -} -export class ERR_QUICCLIENTSESSION_FAILED extends NodeError { - constructor(x: string) { - super( - "ERR_QUICCLIENTSESSION_FAILED", - `Failed to create a new QuicClientSession: ${x}`, - ); - } -} -export class ERR_QUICCLIENTSESSION_FAILED_SETSOCKET extends NodeError { - constructor() { - super( - "ERR_QUICCLIENTSESSION_FAILED_SETSOCKET", - `Failed to set the QuicSocket`, - ); - } -} -export class ERR_QUICSESSION_DESTROYED extends NodeError { - constructor(x: string) { - super( - "ERR_QUICSESSION_DESTROYED", - `Cannot call ${x} after a QuicSession has been destroyed`, - ); - } -} -export class ERR_QUICSESSION_INVALID_DCID extends NodeError { - constructor(x: string) { - super( - "ERR_QUICSESSION_INVALID_DCID", - `Invalid DCID value: ${x}`, - ); - } -} -export class ERR_QUICSESSION_UPDATEKEY extends NodeError { - constructor() { - super( - "ERR_QUICSESSION_UPDATEKEY", - `Unable to update QuicSession keys`, - ); - } -} -export class ERR_QUICSOCKET_DESTROYED extends NodeError { - constructor(x: string) { - super( - "ERR_QUICSOCKET_DESTROYED", - `Cannot call ${x} after a QuicSocket has been destroyed`, - ); - } -} -export class ERR_QUICSOCKET_INVALID_STATELESS_RESET_SECRET_LENGTH - extends NodeError { - constructor() { - super( - "ERR_QUICSOCKET_INVALID_STATELESS_RESET_SECRET_LENGTH", - `The stateResetToken must be exactly 16-bytes in length`, - ); - } -} -export class ERR_QUICSOCKET_LISTENING extends NodeError { - constructor() { - super( - "ERR_QUICSOCKET_LISTENING", - `This QuicSocket is already listening`, - ); - } -} -export class ERR_QUICSOCKET_UNBOUND extends NodeError { - constructor(x: string) { - super( - "ERR_QUICSOCKET_UNBOUND", - `Cannot call ${x} before a QuicSocket has been bound`, - ); - } -} -export class ERR_QUICSTREAM_DESTROYED extends NodeError { - constructor(x: string) { - super( - "ERR_QUICSTREAM_DESTROYED", - `Cannot call ${x} after a QuicStream has been destroyed`, - ); - } -} -export class ERR_QUICSTREAM_INVALID_PUSH extends NodeError { - constructor() { - super( - "ERR_QUICSTREAM_INVALID_PUSH", - `Push streams are only supported on client-initiated, bidirectional streams`, - ); - } -} -export class ERR_QUICSTREAM_OPEN_FAILED extends NodeError { - constructor() { - super( - "ERR_QUICSTREAM_OPEN_FAILED", - `Opening a new QuicStream failed`, - ); - } -} -export class ERR_QUICSTREAM_UNSUPPORTED_PUSH extends NodeError { - constructor() { - super( - "ERR_QUICSTREAM_UNSUPPORTED_PUSH", - `Push streams are not supported on this QuicSession`, - ); - } -} -export class ERR_QUIC_TLS13_REQUIRED extends NodeError { - constructor() { - super( - "ERR_QUIC_TLS13_REQUIRED", - `QUIC requires TLS version 1.3`, - ); - } -} -export class ERR_SCRIPT_EXECUTION_INTERRUPTED extends NodeError { - constructor() { - super( - "ERR_SCRIPT_EXECUTION_INTERRUPTED", - "Script execution was interrupted by `SIGINT`", - ); - } -} -export class ERR_SERVER_ALREADY_LISTEN extends NodeError { - constructor() { - super( - "ERR_SERVER_ALREADY_LISTEN", - `Listen method has been called more than once without closing.`, - ); - } -} -export class ERR_SERVER_NOT_RUNNING extends NodeError { - constructor() { - super( - "ERR_SERVER_NOT_RUNNING", - `Server is not running.`, - ); - } -} -export class ERR_SOCKET_ALREADY_BOUND extends NodeError { - constructor() { - super( - "ERR_SOCKET_ALREADY_BOUND", - `Socket is already bound`, - ); - } -} -export class ERR_SOCKET_BAD_BUFFER_SIZE extends NodeTypeError { - constructor() { - super( - "ERR_SOCKET_BAD_BUFFER_SIZE", - `Buffer size must be a positive integer`, - ); - } -} -export class ERR_SOCKET_BAD_TYPE extends NodeTypeError { - constructor() { - super( - "ERR_SOCKET_BAD_TYPE", - `Bad socket type specified. Valid types are: udp4, udp6`, - ); - } -} -export class ERR_SOCKET_CLOSED extends NodeError { - constructor() { - super( - "ERR_SOCKET_CLOSED", - `Socket is closed`, - ); - } -} -export class ERR_SOCKET_DGRAM_IS_CONNECTED extends NodeError { - constructor() { - super( - "ERR_SOCKET_DGRAM_IS_CONNECTED", - `Already connected`, - ); - } -} -export class ERR_SOCKET_DGRAM_NOT_CONNECTED extends NodeError { - constructor() { - super( - "ERR_SOCKET_DGRAM_NOT_CONNECTED", - `Not connected`, - ); - } -} -export class ERR_SOCKET_DGRAM_NOT_RUNNING extends NodeError { - constructor() { - super( - "ERR_SOCKET_DGRAM_NOT_RUNNING", - `Not running`, - ); - } -} -export class ERR_SRI_PARSE extends NodeSyntaxError { - constructor(name: string, char: string, position: number) { - super( - "ERR_SRI_PARSE", - `Subresource Integrity string ${name} had an unexpected ${char} at position ${position}`, - ); - } -} -export class ERR_STREAM_ALREADY_FINISHED extends NodeError { - constructor(x: string) { - super( - "ERR_STREAM_ALREADY_FINISHED", - `Cannot call ${x} after a stream was finished`, - ); - } -} -export class ERR_STREAM_CANNOT_PIPE extends NodeError { - constructor() { - super( - "ERR_STREAM_CANNOT_PIPE", - `Cannot pipe, not readable`, - ); - } -} -export class ERR_STREAM_DESTROYED extends NodeError { - constructor(x: string) { - super( - "ERR_STREAM_DESTROYED", - `Cannot call ${x} after a stream was destroyed`, - ); - } -} -export class ERR_STREAM_NULL_VALUES extends NodeTypeError { - constructor() { - super( - "ERR_STREAM_NULL_VALUES", - `May not write null values to stream`, - ); - } -} -export class ERR_STREAM_PREMATURE_CLOSE extends NodeError { - constructor() { - super( - "ERR_STREAM_PREMATURE_CLOSE", - `Premature close`, - ); - } -} -export class ERR_STREAM_PUSH_AFTER_EOF extends NodeError { - constructor() { - super( - "ERR_STREAM_PUSH_AFTER_EOF", - `stream.push() after EOF`, - ); - } -} -export class ERR_STREAM_UNSHIFT_AFTER_END_EVENT extends NodeError { - constructor() { - super( - "ERR_STREAM_UNSHIFT_AFTER_END_EVENT", - `stream.unshift() after end event`, - ); - } -} -export class ERR_STREAM_WRAP extends NodeError { - constructor() { - super( - "ERR_STREAM_WRAP", - `Stream has StringDecoder set or is in objectMode`, - ); - } -} -export class ERR_STREAM_WRITE_AFTER_END extends NodeError { - constructor() { - super( - "ERR_STREAM_WRITE_AFTER_END", - `write after end`, - ); - } -} -export class ERR_SYNTHETIC extends NodeError { - constructor() { - super( - "ERR_SYNTHETIC", - `JavaScript Callstack`, - ); - } -} -export class ERR_TLS_DH_PARAM_SIZE extends NodeError { - constructor(x: string) { - super( - "ERR_TLS_DH_PARAM_SIZE", - `DH parameter size ${x} is less than 2048`, - ); - } -} -export class ERR_TLS_HANDSHAKE_TIMEOUT extends NodeError { - constructor() { - super( - "ERR_TLS_HANDSHAKE_TIMEOUT", - `TLS handshake timeout`, - ); - } -} -export class ERR_TLS_INVALID_CONTEXT extends NodeTypeError { - constructor(x: string) { - super( - "ERR_TLS_INVALID_CONTEXT", - `${x} must be a SecureContext`, - ); - } -} -export class ERR_TLS_INVALID_STATE extends NodeError { - constructor() { - super( - "ERR_TLS_INVALID_STATE", - `TLS socket connection must be securely established`, - ); - } -} -export class ERR_TLS_INVALID_PROTOCOL_VERSION extends NodeTypeError { - constructor(protocol: string, x: string) { - super( - "ERR_TLS_INVALID_PROTOCOL_VERSION", - `${protocol} is not a valid ${x} TLS protocol version`, - ); - } -} -export class ERR_TLS_PROTOCOL_VERSION_CONFLICT extends NodeTypeError { - constructor(prevProtocol: string, protocol: string) { - super( - "ERR_TLS_PROTOCOL_VERSION_CONFLICT", - `TLS protocol version ${prevProtocol} conflicts with secureProtocol ${protocol}`, - ); - } -} -export class ERR_TLS_RENEGOTIATION_DISABLED extends NodeError { - constructor() { - super( - "ERR_TLS_RENEGOTIATION_DISABLED", - `TLS session renegotiation disabled for this socket`, - ); - } -} -export class ERR_TLS_REQUIRED_SERVER_NAME extends NodeError { - constructor() { - super( - "ERR_TLS_REQUIRED_SERVER_NAME", - `"servername" is required parameter for Server.addContext`, - ); - } -} -export class ERR_TLS_SESSION_ATTACK extends NodeError { - constructor() { - super( - "ERR_TLS_SESSION_ATTACK", - `TLS session renegotiation attack detected`, - ); - } -} -export class ERR_TLS_SNI_FROM_SERVER extends NodeError { - constructor() { - super( - "ERR_TLS_SNI_FROM_SERVER", - `Cannot issue SNI from a TLS server-side socket`, - ); - } -} -export class ERR_TRACE_EVENTS_CATEGORY_REQUIRED extends NodeTypeError { - constructor() { - super( - "ERR_TRACE_EVENTS_CATEGORY_REQUIRED", - `At least one category is required`, - ); - } -} -export class ERR_TRACE_EVENTS_UNAVAILABLE extends NodeError { - constructor() { - super( - "ERR_TRACE_EVENTS_UNAVAILABLE", - `Trace events are unavailable`, - ); - } -} -export class ERR_UNAVAILABLE_DURING_EXIT extends NodeError { - constructor() { - super( - "ERR_UNAVAILABLE_DURING_EXIT", - `Cannot call function in process exit handler`, - ); - } -} -export class ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET extends NodeError { - constructor() { - super( - "ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET", - "`process.setupUncaughtExceptionCapture()` was called while a capture callback was already active", - ); - } -} -export class ERR_UNESCAPED_CHARACTERS extends NodeTypeError { - constructor(x: string) { - super( - "ERR_UNESCAPED_CHARACTERS", - `${x} contains unescaped characters`, - ); - } -} -export class ERR_UNKNOWN_BUILTIN_MODULE extends NodeError { - constructor(x: string) { - super( - "ERR_UNKNOWN_BUILTIN_MODULE", - `No such built-in module: ${x}`, - ); - } -} -export class ERR_UNKNOWN_CREDENTIAL extends NodeError { - constructor(x: string, y: string) { - super( - "ERR_UNKNOWN_CREDENTIAL", - `${x} identifier does not exist: ${y}`, - ); - } -} -export class ERR_UNKNOWN_ENCODING extends NodeTypeError { - constructor(x: string) { - super( - "ERR_UNKNOWN_ENCODING", - `Unknown encoding: ${x}`, - ); - } -} -export class ERR_UNKNOWN_FILE_EXTENSION extends NodeTypeError { - constructor(x: string, y: string) { - super( - "ERR_UNKNOWN_FILE_EXTENSION", - `Unknown file extension "${x}" for ${y}`, - ); - } -} -export class ERR_UNKNOWN_MODULE_FORMAT extends NodeRangeError { - constructor(x: string) { - super( - "ERR_UNKNOWN_MODULE_FORMAT", - `Unknown module format: ${x}`, - ); - } -} -export class ERR_UNKNOWN_SIGNAL extends NodeTypeError { - constructor(x: string) { - super( - "ERR_UNKNOWN_SIGNAL", - `Unknown signal: ${x}`, - ); - } -} -export class ERR_UNSUPPORTED_DIR_IMPORT extends NodeError { - constructor(x: string, y: string) { - super( - "ERR_UNSUPPORTED_DIR_IMPORT", - `Directory import '${x}' is not supported resolving ES modules, imported from ${y}`, - ); - } -} -export class ERR_UNSUPPORTED_ESM_URL_SCHEME extends NodeError { - constructor() { - super( - "ERR_UNSUPPORTED_ESM_URL_SCHEME", - `Only file and data URLs are supported by the default ESM loader`, - ); - } -} -export class ERR_V8BREAKITERATOR extends NodeError { - constructor() { - super( - "ERR_V8BREAKITERATOR", - `Full ICU data not installed. See https://github.com/nodejs/node/wiki/Intl`, - ); - } -} -export class ERR_VALID_PERFORMANCE_ENTRY_TYPE extends NodeError { - constructor() { - super( - "ERR_VALID_PERFORMANCE_ENTRY_TYPE", - `At least one valid performance entry type is required`, - ); - } -} -export class ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING extends NodeTypeError { - constructor() { - super( - "ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING", - `A dynamic import callback was not specified.`, - ); - } -} -export class ERR_VM_MODULE_ALREADY_LINKED extends NodeError { - constructor() { - super( - "ERR_VM_MODULE_ALREADY_LINKED", - `Module has already been linked`, - ); - } -} -export class ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA extends NodeError { - constructor() { - super( - "ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA", - `Cached data cannot be created for a module which has been evaluated`, - ); - } -} -export class ERR_VM_MODULE_DIFFERENT_CONTEXT extends NodeError { - constructor() { - super( - "ERR_VM_MODULE_DIFFERENT_CONTEXT", - `Linked modules must use the same context`, - ); - } -} -export class ERR_VM_MODULE_LINKING_ERRORED extends NodeError { - constructor() { - super( - "ERR_VM_MODULE_LINKING_ERRORED", - `Linking has already failed for the provided module`, - ); - } -} -export class ERR_VM_MODULE_NOT_MODULE extends NodeError { - constructor() { - super( - "ERR_VM_MODULE_NOT_MODULE", - `Provided module is not an instance of Module`, - ); - } -} -export class ERR_VM_MODULE_STATUS extends NodeError { - constructor(x: string) { - super( - "ERR_VM_MODULE_STATUS", - `Module status ${x}`, - ); - } -} -export class ERR_WASI_ALREADY_STARTED extends NodeError { - constructor() { - super( - "ERR_WASI_ALREADY_STARTED", - `WASI instance has already started`, - ); - } -} -export class ERR_WORKER_INIT_FAILED extends NodeError { - constructor(x: string) { - super( - "ERR_WORKER_INIT_FAILED", - `Worker initialization failure: ${x}`, - ); - } -} -export class ERR_WORKER_NOT_RUNNING extends NodeError { - constructor() { - super( - "ERR_WORKER_NOT_RUNNING", - `Worker instance not running`, - ); - } -} -export class ERR_WORKER_OUT_OF_MEMORY extends NodeError { - constructor(x: string) { - super( - "ERR_WORKER_OUT_OF_MEMORY", - `Worker terminated due to reaching memory limit: ${x}`, - ); - } -} -export class ERR_WORKER_UNSERIALIZABLE_ERROR extends NodeError { - constructor() { - super( - "ERR_WORKER_UNSERIALIZABLE_ERROR", - `Serializing an uncaught exception failed`, - ); - } -} -export class ERR_WORKER_UNSUPPORTED_EXTENSION extends NodeTypeError { - constructor(x: string) { - super( - "ERR_WORKER_UNSUPPORTED_EXTENSION", - `The worker script extension must be ".js", ".mjs", or ".cjs". Received "${x}"`, - ); - } -} -export class ERR_WORKER_UNSUPPORTED_OPERATION extends NodeTypeError { - constructor(x: string) { - super( - "ERR_WORKER_UNSUPPORTED_OPERATION", - `${x} is not supported in workers`, - ); - } -} -export class ERR_ZLIB_INITIALIZATION_FAILED extends NodeError { - constructor() { - super( - "ERR_ZLIB_INITIALIZATION_FAILED", - `Initialization failed`, - ); - } -} -export class ERR_FALSY_VALUE_REJECTION extends NodeError { - reason: string; - constructor(reason: string) { - super( - "ERR_FALSY_VALUE_REJECTION", - "Promise was rejected with falsy value", - ); - this.reason = reason; - } -} -export class ERR_HTTP2_INVALID_SETTING_VALUE extends NodeRangeError { - actual: unknown; - min?: number; - max?: number; - - constructor(name: string, actual: unknown, min?: number, max?: number) { - super( - "ERR_HTTP2_INVALID_SETTING_VALUE", - `Invalid value for setting "${name}": ${actual}`, - ); - this.actual = actual; - if (min !== undefined) { - this.min = min; - this.max = max; - } - } -} -export class ERR_HTTP2_STREAM_CANCEL extends NodeError { - cause?: Error; - constructor(error: Error) { - super( - "ERR_HTTP2_STREAM_CANCEL", - typeof error.message === "string" - ? `The pending stream has been canceled (caused by: ${error.message})` - : "The pending stream has been canceled", - ); - if (error) { - this.cause = error; - } - } -} - -export class ERR_INVALID_ADDRESS_FAMILY extends NodeRangeError { - host: string; - port: number; - constructor(addressType: string, host: string, port: number) { - super( - "ERR_INVALID_ADDRESS_FAMILY", - `Invalid address family: ${addressType} ${host}:${port}`, - ); - this.host = host; - this.port = port; - } -} - -export class ERR_INVALID_CHAR extends NodeTypeError { - constructor(name: string, field?: string) { - super( - "ERR_INVALID_CHAR", - field - ? `Invalid character in ${name}` - : `Invalid character in ${name} ["${field}"]`, - ); - } -} - -export class ERR_INVALID_OPT_VALUE extends NodeTypeError { - constructor(name: string, value: unknown) { - super( - "ERR_INVALID_OPT_VALUE", - `The value "${value}" is invalid for option "${name}"`, - ); - } -} - -export class ERR_INVALID_RETURN_PROPERTY extends NodeTypeError { - constructor(input: string, name: string, prop: string, value: string) { - super( - "ERR_INVALID_RETURN_PROPERTY", - `Expected a valid ${input} to be returned for the "${prop}" from the "${name}" function but got ${value}.`, - ); - } -} - -// deno-lint-ignore no-explicit-any -function buildReturnPropertyType(value: any) { - if (value && value.constructor && value.constructor.name) { - return `instance of ${value.constructor.name}`; - } else { - return `type ${typeof value}`; - } -} - -export class ERR_INVALID_RETURN_PROPERTY_VALUE extends NodeTypeError { - constructor(input: string, name: string, prop: string, value: unknown) { - super( - "ERR_INVALID_RETURN_PROPERTY_VALUE", - `Expected ${input} to be returned for the "${prop}" from the "${name}" function but got ${ - buildReturnPropertyType(value) - }.`, - ); - } -} - -export class ERR_INVALID_RETURN_VALUE extends NodeTypeError { - constructor(input: string, name: string, value: unknown) { - super( - "ERR_INVALID_RETURN_VALUE", - `Expected ${input} to be returned from the "${name}" function but got ${ - buildReturnPropertyType(value) - }.`, - ); - } -} - -export class ERR_INVALID_URL extends NodeTypeError { - input: string; - constructor(input: string) { - super( - "ERR_INVALID_URL", - `Invalid URL: ${input}`, - ); - this.input = input; - } -} diff --git a/std/node/_fs/_fs_access.ts b/std/node/_fs/_fs_access.ts deleted file mode 100644 index 3211e16400..0000000000 --- a/std/node/_fs/_fs_access.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import type { CallbackWithError } from "./_fs_common.ts"; -import { notImplemented } from "../_utils.ts"; - -/** Revist once https://github.com/denoland/deno/issues/4017 lands */ - -// TODO(bartlomieju) 'path' can also be a Buffer. Neither of these polyfills -//is available yet. See https://github.com/denoland/deno/issues/3403 -export function access( - _path: string | URL, - _modeOrCallback: number | ((...args: unknown[]) => void), - _callback?: CallbackWithError, -): void { - notImplemented("Not yet available"); -} - -// TODO(bartlomieju) 'path' can also be a Buffer. Neither of these polyfills -// is available yet. See https://github.com/denoland/deno/issues/3403 -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export function accessSync(path: string | URL, mode?: number): void { - notImplemented("Not yet available"); -} diff --git a/std/node/_fs/_fs_appendFile.ts b/std/node/_fs/_fs_appendFile.ts deleted file mode 100644 index ee01b9cc8c..0000000000 --- a/std/node/_fs/_fs_appendFile.ts +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - CallbackWithError, - getOpenOptions, - isFileOptions, - WriteFileOptions, -} from "./_fs_common.ts"; -import { Encodings, notImplemented } from "../_utils.ts"; -import { fromFileUrl } from "../path.ts"; - -/** - * TODO: Also accept 'data' parameter as a Node polyfill Buffer type once these - * are implemented. See https://github.com/denoland/deno/issues/3403 - */ -export function appendFile( - pathOrRid: string | number | URL, - data: string | Uint8Array, - optionsOrCallback: Encodings | WriteFileOptions | CallbackWithError, - callback?: CallbackWithError, -): void { - pathOrRid = pathOrRid instanceof URL ? fromFileUrl(pathOrRid) : pathOrRid; - const callbackFn: CallbackWithError | undefined = - optionsOrCallback instanceof Function ? optionsOrCallback : callback; - const options: Encodings | WriteFileOptions | undefined = - optionsOrCallback instanceof Function ? undefined : optionsOrCallback; - if (!callbackFn) { - throw new Error("No callback function supplied"); - } - - validateEncoding(options); - let rid = -1; - const buffer: Uint8Array = data instanceof Uint8Array - ? data - : new TextEncoder().encode(data); - new Promise((resolve, reject) => { - if (typeof pathOrRid === "number") { - rid = pathOrRid; - Deno.write(rid, buffer).then(resolve, reject); - } else { - const mode: number | undefined = isFileOptions(options) - ? options.mode - : undefined; - const flag: string | undefined = isFileOptions(options) - ? options.flag - : undefined; - - if (mode) { - // TODO(bartlomieju) rework once https://github.com/denoland/deno/issues/4017 completes - notImplemented("Deno does not yet support setting mode on create"); - } - Deno.open(pathOrRid as string, getOpenOptions(flag)) - .then(({ rid: openedFileRid }) => { - rid = openedFileRid; - return Deno.write(openedFileRid, buffer); - }) - .then(resolve, reject); - } - }) - .then(() => { - closeRidIfNecessary(typeof pathOrRid === "string", rid); - callbackFn(null); - }, (err) => { - closeRidIfNecessary(typeof pathOrRid === "string", rid); - callbackFn(err); - }); -} - -function closeRidIfNecessary(isPathString: boolean, rid: number): void { - if (isPathString && rid != -1) { - //Only close if a path was supplied and a rid allocated - Deno.close(rid); - } -} - -/** - * TODO: Also accept 'data' parameter as a Node polyfill Buffer type once these - * are implemented. See https://github.com/denoland/deno/issues/3403 - */ -export function appendFileSync( - pathOrRid: string | number | URL, - data: string | Uint8Array, - options?: Encodings | WriteFileOptions, -): void { - let rid = -1; - - validateEncoding(options); - pathOrRid = pathOrRid instanceof URL ? fromFileUrl(pathOrRid) : pathOrRid; - - try { - if (typeof pathOrRid === "number") { - rid = pathOrRid; - } else { - const mode: number | undefined = isFileOptions(options) - ? options.mode - : undefined; - const flag: string | undefined = isFileOptions(options) - ? options.flag - : undefined; - - if (mode) { - // TODO(bartlomieju) rework once https://github.com/denoland/deno/issues/4017 completes - notImplemented("Deno does not yet support setting mode on create"); - } - - const file = Deno.openSync(pathOrRid, getOpenOptions(flag)); - rid = file.rid; - } - - const buffer: Uint8Array = data instanceof Uint8Array - ? data - : new TextEncoder().encode(data); - - Deno.writeSync(rid, buffer); - } finally { - closeRidIfNecessary(typeof pathOrRid === "string", rid); - } -} - -function validateEncoding( - encodingOption: Encodings | WriteFileOptions | undefined, -): void { - if (!encodingOption) return; - - if (typeof encodingOption === "string") { - if (encodingOption !== "utf8") { - throw new Error("Only 'utf8' encoding is currently supported"); - } - } else if (encodingOption.encoding && encodingOption.encoding !== "utf8") { - throw new Error("Only 'utf8' encoding is currently supported"); - } -} diff --git a/std/node/_fs/_fs_appendFile_test.ts b/std/node/_fs/_fs_appendFile_test.ts deleted file mode 100644 index 6a77974fd8..0000000000 --- a/std/node/_fs/_fs_appendFile_test.ts +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals, assertThrows, fail } from "../../testing/asserts.ts"; -import { appendFile, appendFileSync } from "./_fs_appendFile.ts"; -import { fromFileUrl } from "../path.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; - -const decoder = new TextDecoder("utf-8"); - -Deno.test({ - name: "No callback Fn results in Error", - fn() { - assertThrows( - () => { - appendFile("some/path", "some data", "utf8"); - }, - Error, - "No callback function supplied", - ); - }, -}); - -Deno.test({ - name: "Unsupported encoding results in error()", - fn() { - assertThrows( - () => { - // @ts-expect-error Type '"made-up-encoding"' is not assignable to type - appendFile("some/path", "some data", "made-up-encoding", () => {}); - }, - Error, - "Only 'utf8' encoding is currently supported", - ); - assertThrows( - () => { - appendFile( - "some/path", - "some data", - // @ts-expect-error Type '"made-up-encoding"' is not assignable to type - { encoding: "made-up-encoding" }, - () => {}, - ); - }, - Error, - "Only 'utf8' encoding is currently supported", - ); - assertThrows( - // @ts-expect-error Type '"made-up-encoding"' is not assignable to type - () => appendFileSync("some/path", "some data", "made-up-encoding"), - Error, - "Only 'utf8' encoding is currently supported", - ); - assertThrows( - () => - appendFileSync("some/path", "some data", { - // @ts-expect-error Type '"made-up-encoding"' is not assignable to type - encoding: "made-up-encoding", - }), - Error, - "Only 'utf8' encoding is currently supported", - ); - }, -}); - -Deno.test({ - name: "Async: Data is written to passed in rid", - async fn() { - const tempFile: string = await Deno.makeTempFile(); - const file: Deno.File = await Deno.open(tempFile, { - create: true, - write: true, - read: true, - }); - await new Promise((resolve, reject) => { - appendFile(file.rid, "hello world", (err) => { - if (err) reject(); - else resolve(); - }); - }) - .then(async () => { - const data = await Deno.readFile(tempFile); - assertEquals(decoder.decode(data), "hello world"); - }, () => { - fail("No error expected"); - }) - .finally(async () => { - Deno.close(file.rid); - await Deno.remove(tempFile); - }); - }, -}); - -Deno.test({ - name: "Async: Data is written to passed in file path", - async fn() { - const openResourcesBeforeAppend: Deno.ResourceMap = Deno.resources(); - await new Promise((resolve, reject) => { - appendFile("_fs_appendFile_test_file.txt", "hello world", (err) => { - if (err) reject(err); - else resolve(); - }); - }) - .then(async () => { - assertEquals(Deno.resources(), openResourcesBeforeAppend); - const data = await Deno.readFile("_fs_appendFile_test_file.txt"); - assertEquals(decoder.decode(data), "hello world"); - }, (err) => { - fail("No error was expected: " + err); - }) - .finally(async () => { - await Deno.remove("_fs_appendFile_test_file.txt"); - }); - }, -}); - -Deno.test({ - name: "Async: Data is written to passed in URL", - async fn() { - const openResourcesBeforeAppend: Deno.ResourceMap = Deno.resources(); - const fileURL = new URL("_fs_appendFile_test_file.txt", import.meta.url); - await new Promise((resolve, reject) => { - appendFile(fileURL, "hello world", (err) => { - if (err) reject(err); - else resolve(); - }); - }) - .then(async () => { - assertEquals(Deno.resources(), openResourcesBeforeAppend); - const data = await Deno.readFile(fromFileUrl(fileURL)); - assertEquals(decoder.decode(data), "hello world"); - }, (err) => { - fail("No error was expected: " + err); - }) - .finally(async () => { - await Deno.remove(fromFileUrl(fileURL)); - }); - }, -}); - -Deno.test({ - name: - "Async: Callback is made with error if attempting to append data to an existing file with 'ax' flag", - async fn() { - const openResourcesBeforeAppend: Deno.ResourceMap = Deno.resources(); - const tempFile: string = await Deno.makeTempFile(); - await new Promise((resolve, reject) => { - appendFile(tempFile, "hello world", { flag: "ax" }, (err) => { - if (err) reject(err); - else resolve(); - }); - }) - .then(() => { - fail("Expected error to be thrown"); - }, () => { - assertEquals(Deno.resources(), openResourcesBeforeAppend); - }) - .finally(async () => { - await Deno.remove(tempFile); - }); - }, -}); - -Deno.test({ - name: "Sync: Data is written to passed in rid", - fn() { - const tempFile: string = Deno.makeTempFileSync(); - const file: Deno.File = Deno.openSync(tempFile, { - create: true, - write: true, - read: true, - }); - appendFileSync(file.rid, "hello world"); - Deno.close(file.rid); - const data = Deno.readFileSync(tempFile); - assertEquals(decoder.decode(data), "hello world"); - Deno.removeSync(tempFile); - }, -}); - -Deno.test({ - name: "Sync: Data is written to passed in file path", - fn() { - const openResourcesBeforeAppend: Deno.ResourceMap = Deno.resources(); - appendFileSync("_fs_appendFile_test_file_sync.txt", "hello world"); - assertEquals(Deno.resources(), openResourcesBeforeAppend); - const data = Deno.readFileSync("_fs_appendFile_test_file_sync.txt"); - assertEquals(decoder.decode(data), "hello world"); - Deno.removeSync("_fs_appendFile_test_file_sync.txt"); - }, -}); - -Deno.test({ - name: - "Sync: error thrown if attempting to append data to an existing file with 'ax' flag", - fn() { - const openResourcesBeforeAppend: Deno.ResourceMap = Deno.resources(); - const tempFile: string = Deno.makeTempFileSync(); - assertThrows( - () => appendFileSync(tempFile, "hello world", { flag: "ax" }), - Deno.errors.AlreadyExists, - "", - ); - assertEquals(Deno.resources(), openResourcesBeforeAppend); - Deno.removeSync(tempFile); - }, -}); - -Deno.test({ - name: "Sync: Data is written in Uint8Array to passed in file path", - fn() { - const openResourcesBeforeAppend: Deno.ResourceMap = Deno.resources(); - const testData = new TextEncoder().encode("hello world"); - appendFileSync("_fs_appendFile_test_file_sync.txt", testData); - assertEquals(Deno.resources(), openResourcesBeforeAppend); - const data = Deno.readFileSync("_fs_appendFile_test_file_sync.txt"); - assertEquals(data, testData); - Deno.removeSync("_fs_appendFile_test_file_sync.txt"); - }, -}); - -Deno.test({ - name: "Async: Data is written in Uint8Array to passed in file path", - async fn() { - const openResourcesBeforeAppend: Deno.ResourceMap = Deno.resources(); - const testData = new TextEncoder().encode("hello world"); - await new Promise((resolve, reject) => { - appendFile("_fs_appendFile_test_file.txt", testData, (err) => { - if (err) reject(err); - else resolve(); - }); - }) - .then(async () => { - assertEquals(Deno.resources(), openResourcesBeforeAppend); - const data = await Deno.readFile("_fs_appendFile_test_file.txt"); - assertEquals(data, testData); - }, (err) => { - fail("No error was expected: " + err); - }) - .finally(async () => { - await Deno.remove("_fs_appendFile_test_file.txt"); - }); - }, -}); - -Deno.test("[std/node/fs] appendFile callback isn't called twice if error is thrown", async () => { - const tempFile = await Deno.makeTempFile(); - const importUrl = new URL("./_fs_appendFile.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { appendFile } from ${JSON.stringify(importUrl)}`, - invocation: `appendFile(${JSON.stringify(tempFile)}, "hello world", `, - async cleanup() { - await Deno.remove(tempFile); - }, - }); -}); diff --git a/std/node/_fs/_fs_chmod.ts b/std/node/_fs/_fs_chmod.ts deleted file mode 100644 index bf4031d2cc..0000000000 --- a/std/node/_fs/_fs_chmod.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import type { CallbackWithError } from "./_fs_common.ts"; -import { fromFileUrl } from "../path.ts"; - -const allowedModes = /^[0-7]{3}/; - -/** - * TODO: Also accept 'path' parameter as a Node polyfill Buffer type once these - * are implemented. See https://github.com/denoland/deno/issues/3403 - */ -export function chmod( - path: string | URL, - mode: string | number, - callback: CallbackWithError, -): void { - path = path instanceof URL ? fromFileUrl(path) : path; - - Deno.chmod(path, getResolvedMode(mode)).then(() => callback(null), callback); -} - -/** - * TODO: Also accept 'path' parameter as a Node polyfill Buffer type once these - * are implemented. See https://github.com/denoland/deno/issues/3403 - */ -export function chmodSync(path: string | URL, mode: string | number): void { - path = path instanceof URL ? fromFileUrl(path) : path; - Deno.chmodSync(path, getResolvedMode(mode)); -} - -function getResolvedMode(mode: string | number): number { - if (typeof mode === "number") { - return mode; - } - - if (typeof mode === "string" && !allowedModes.test(mode)) { - throw new Error("Unrecognized mode: " + mode); - } - - return parseInt(mode, 8); -} diff --git a/std/node/_fs/_fs_chmod_test.ts b/std/node/_fs/_fs_chmod_test.ts deleted file mode 100644 index 4d8647782e..0000000000 --- a/std/node/_fs/_fs_chmod_test.ts +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, fail } from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { chmod, chmodSync } from "./_fs_chmod.ts"; - -Deno.test({ - name: "ASYNC: Permissions are changed (non-Windows)", - ignore: Deno.build.os === "windows", - async fn() { - const tempFile: string = await Deno.makeTempFile(); - const originalFileMode: number | null = (await Deno.lstat(tempFile)).mode; - await new Promise((resolve, reject) => { - chmod(tempFile, 0o777, (err) => { - if (err) reject(err); - else resolve(); - }); - }) - .then(() => { - const newFileMode: number | null = Deno.lstatSync(tempFile).mode; - assert(newFileMode && originalFileMode); - assert(newFileMode === 33279 && newFileMode > originalFileMode); - }, () => { - fail(); - }) - .finally(() => { - Deno.removeSync(tempFile); - }); - }, -}); - -Deno.test({ - name: "SYNC: Permissions are changed (non-Windows)", - ignore: Deno.build.os === "windows", - fn() { - const tempFile: string = Deno.makeTempFileSync(); - const originalFileMode: number | null = Deno.lstatSync(tempFile).mode; - chmodSync(tempFile, "777"); - - const newFileMode: number | null = Deno.lstatSync(tempFile).mode; - assert(newFileMode && originalFileMode); - assert(newFileMode === 33279 && newFileMode > originalFileMode); - Deno.removeSync(tempFile); - }, -}); - -Deno.test({ - name: "[std/node/fs] chmod callback isn't called twice if error is thrown", - ignore: Deno.build.os === "windows", - async fn() { - const tempFile = await Deno.makeTempFile(); - const importUrl = new URL("./_fs_chmod.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { chmod } from ${JSON.stringify(importUrl)}`, - invocation: `chmod(${JSON.stringify(tempFile)}, 0o777, `, - async cleanup() { - await Deno.remove(tempFile); - }, - }); - }, -}); diff --git a/std/node/_fs/_fs_chown.ts b/std/node/_fs/_fs_chown.ts deleted file mode 100644 index f9e7326cec..0000000000 --- a/std/node/_fs/_fs_chown.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import type { CallbackWithError } from "./_fs_common.ts"; -import { fromFileUrl } from "../path.ts"; - -/** - * TODO: Also accept 'path' parameter as a Node polyfill Buffer type once these - * are implemented. See https://github.com/denoland/deno/issues/3403 - */ -export function chown( - path: string | URL, - uid: number, - gid: number, - callback: CallbackWithError, -): void { - path = path instanceof URL ? fromFileUrl(path) : path; - - Deno.chown(path, uid, gid).then(() => callback(null), callback); -} - -/** - * TODO: Also accept 'path' parameter as a Node polyfill Buffer type once these - * are implemented. See https://github.com/denoland/deno/issues/3403 - */ -export function chownSync(path: string | URL, uid: number, gid: number): void { - path = path instanceof URL ? fromFileUrl(path) : path; - - Deno.chownSync(path, uid, gid); -} diff --git a/std/node/_fs/_fs_chown_test.ts b/std/node/_fs/_fs_chown_test.ts deleted file mode 100644 index 8d74f657a0..0000000000 --- a/std/node/_fs/_fs_chown_test.ts +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals, fail } from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { chown, chownSync } from "./_fs_chown.ts"; - -// chown is difficult to test. Best we can do is set the existing user id/group -// id again -const ignore = Deno.build.os == "windows"; - -Deno.test({ - ignore, - name: "ASYNC: setting existing uid/gid works as expected (non-Windows)", - async fn() { - const tempFile: string = await Deno.makeTempFile(); - const originalUserId: number | null = (await Deno.lstat(tempFile)).uid; - const originalGroupId: number | null = (await Deno.lstat(tempFile)).gid; - await new Promise((resolve, reject) => { - chown(tempFile, originalUserId!, originalGroupId!, (err) => { - if (err) reject(err); - else resolve(); - }); - }) - .then(() => { - const newUserId: number | null = Deno.lstatSync(tempFile).uid; - const newGroupId: number | null = Deno.lstatSync(tempFile).gid; - assertEquals(newUserId, originalUserId); - assertEquals(newGroupId, originalGroupId); - }, () => { - fail(); - }) - .finally(() => { - Deno.removeSync(tempFile); - }); - }, -}); - -Deno.test({ - ignore, - name: "SYNC: setting existing uid/gid works as expected (non-Windows)", - fn() { - const tempFile: string = Deno.makeTempFileSync(); - const originalUserId: number | null = Deno.lstatSync(tempFile).uid; - const originalGroupId: number | null = Deno.lstatSync(tempFile).gid; - chownSync(tempFile, originalUserId!, originalGroupId!); - - const newUserId: number | null = Deno.lstatSync(tempFile).uid; - const newGroupId: number | null = Deno.lstatSync(tempFile).gid; - assertEquals(newUserId, originalUserId); - assertEquals(newGroupId, originalGroupId); - Deno.removeSync(tempFile); - }, -}); - -Deno.test({ - name: "[std/node/fs] chown callback isn't called twice if error is thrown", - ignore: Deno.build.os === "windows", - async fn() { - const tempFile = await Deno.makeTempFile(); - const { uid, gid } = await Deno.lstat(tempFile); - const importUrl = new URL("./_fs_chown.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { chown } from ${JSON.stringify(importUrl)}`, - invocation: `chown(${JSON.stringify(tempFile)}, ${uid}, ${gid}, `, - async cleanup() { - await Deno.remove(tempFile); - }, - }); - }, -}); diff --git a/std/node/_fs/_fs_close.ts b/std/node/_fs/_fs_close.ts deleted file mode 100644 index 7218274219..0000000000 --- a/std/node/_fs/_fs_close.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import type { CallbackWithError } from "./_fs_common.ts"; - -export function close(fd: number, callback: CallbackWithError): void { - setTimeout(() => { - let error = null; - try { - Deno.close(fd); - } catch (err) { - error = err; - } - callback(error); - }, 0); -} - -export function closeSync(fd: number): void { - Deno.close(fd); -} diff --git a/std/node/_fs/_fs_close_test.ts b/std/node/_fs/_fs_close_test.ts deleted file mode 100644 index df5a932409..0000000000 --- a/std/node/_fs/_fs_close_test.ts +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertThrows, fail } from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { close, closeSync } from "./_fs_close.ts"; - -Deno.test({ - name: "ASYNC: File is closed", - async fn() { - const tempFile: string = await Deno.makeTempFile(); - const file: Deno.File = await Deno.open(tempFile); - - assert(Deno.resources()[file.rid]); - await new Promise((resolve, reject) => { - close(file.rid, (err) => { - if (err !== null) reject(); - else resolve(); - }); - }) - .then(() => { - assert(!Deno.resources()[file.rid]); - }, () => { - fail("No error expected"); - }) - .finally(async () => { - await Deno.remove(tempFile); - }); - }, -}); - -Deno.test({ - name: "ASYNC: Invalid fd", - async fn() { - await new Promise((resolve, reject) => { - close(-1, (err) => { - if (err !== null) return resolve(); - reject(); - }); - }); - }, -}); - -Deno.test({ - name: "close callback should be asynchronous", - async fn() { - const tempFile: string = Deno.makeTempFileSync(); - const file: Deno.File = Deno.openSync(tempFile); - - let foo: string; - const promise = new Promise((resolve) => { - close(file.rid, () => { - assert(foo === "bar"); - resolve(); - }); - foo = "bar"; - }); - - await promise; - Deno.removeSync(tempFile); - }, -}); - -Deno.test({ - name: "SYNC: File is closed", - fn() { - const tempFile: string = Deno.makeTempFileSync(); - const file: Deno.File = Deno.openSync(tempFile); - - assert(Deno.resources()[file.rid]); - closeSync(file.rid); - assert(!Deno.resources()[file.rid]); - Deno.removeSync(tempFile); - }, -}); - -Deno.test({ - name: "SYNC: Invalid fd", - fn() { - assertThrows(() => closeSync(-1)); - }, -}); - -Deno.test("[std/node/fs] close callback isn't called twice if error is thrown", async () => { - const tempFile = await Deno.makeTempFile(); - const importUrl = new URL("./_fs_close.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: ` - import { close } from ${JSON.stringify(importUrl)}; - - const file = await Deno.open(${JSON.stringify(tempFile)}); - `, - invocation: "close(file.rid, ", - async cleanup() { - await Deno.remove(tempFile); - }, - }); -}); diff --git a/std/node/_fs/_fs_common.ts b/std/node/_fs/_fs_common.ts deleted file mode 100644 index c233b2d76b..0000000000 --- a/std/node/_fs/_fs_common.ts +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - BinaryEncodings, - Encodings, - notImplemented, - TextEncodings, -} from "../_utils.ts"; - -export type CallbackWithError = (err: Error | null) => void; - -export interface FileOptions { - encoding?: Encodings; - flag?: string; -} - -export type TextOptionsArgument = - | TextEncodings - | ({ encoding: TextEncodings } & FileOptions); -export type BinaryOptionsArgument = - | BinaryEncodings - | ({ encoding: BinaryEncodings } & FileOptions); -export type FileOptionsArgument = Encodings | FileOptions; - -export interface WriteFileOptions extends FileOptions { - mode?: number; -} - -export function isFileOptions( - fileOptions: string | WriteFileOptions | undefined, -): fileOptions is FileOptions { - if (!fileOptions) return false; - - return ( - (fileOptions as FileOptions).encoding != undefined || - (fileOptions as FileOptions).flag != undefined || - (fileOptions as WriteFileOptions).mode != undefined - ); -} - -export function getEncoding( - optOrCallback?: - | FileOptions - | WriteFileOptions - // deno-lint-ignore no-explicit-any - | ((...args: any[]) => any) - | Encodings - | null, -): Encodings | null { - if (!optOrCallback || typeof optOrCallback === "function") { - return null; - } - - const encoding = typeof optOrCallback === "string" - ? optOrCallback - : optOrCallback.encoding; - if (!encoding) return null; - return encoding; -} - -export function checkEncoding(encoding: Encodings | null): Encodings | null { - if (!encoding) return null; - - encoding = encoding.toLowerCase() as Encodings; - if (["utf8", "hex", "base64"].includes(encoding)) return encoding; - - if (encoding === "utf-8") { - return "utf8"; - } - if (encoding === "binary") { - return "binary"; - // before this was buffer, however buffer is not used in Node - // node -e "require('fs').readFile('../world.txt', 'buffer', console.log)" - } - - const notImplementedEncodings = ["utf16le", "latin1", "ascii", "ucs2"]; - - if (notImplementedEncodings.includes(encoding as string)) { - notImplemented(`"${encoding}" encoding`); - } - - throw new Error(`The value "${encoding}" is invalid for option "encoding"`); -} - -export function getOpenOptions(flag: string | undefined): Deno.OpenOptions { - if (!flag) { - return { create: true, append: true }; - } - - let openOptions: Deno.OpenOptions; - switch (flag) { - case "a": { - // 'a': Open file for appending. The file is created if it does not exist. - openOptions = { create: true, append: true }; - break; - } - case "ax": { - // 'ax': Like 'a' but fails if the path exists. - openOptions = { createNew: true, write: true, append: true }; - break; - } - case "a+": { - // 'a+': Open file for reading and appending. The file is created if it does not exist. - openOptions = { read: true, create: true, append: true }; - break; - } - case "ax+": { - // 'ax+': Like 'a+' but fails if the path exists. - openOptions = { read: true, createNew: true, append: true }; - break; - } - case "r": { - // 'r': Open file for reading. An exception occurs if the file does not exist. - openOptions = { read: true }; - break; - } - case "r+": { - // 'r+': Open file for reading and writing. An exception occurs if the file does not exist. - openOptions = { read: true, write: true }; - break; - } - case "w": { - // 'w': Open file for writing. The file is created (if it does not exist) or truncated (if it exists). - openOptions = { create: true, write: true, truncate: true }; - break; - } - case "wx": { - // 'wx': Like 'w' but fails if the path exists. - openOptions = { createNew: true, write: true }; - break; - } - case "w+": { - // 'w+': Open file for reading and writing. The file is created (if it does not exist) or truncated (if it exists). - openOptions = { create: true, write: true, truncate: true, read: true }; - break; - } - case "wx+": { - // 'wx+': Like 'w+' but fails if the path exists. - openOptions = { createNew: true, write: true, read: true }; - break; - } - case "as": { - // 'as': Open file for appending in synchronous mode. The file is created if it does not exist. - openOptions = { create: true, append: true }; - break; - } - case "as+": { - // 'as+': Open file for reading and appending in synchronous mode. The file is created if it does not exist. - openOptions = { create: true, read: true, append: true }; - break; - } - case "rs+": { - // 'rs+': Open file for reading and writing in synchronous mode. Instructs the operating system to bypass the local file system cache. - openOptions = { create: true, read: true, write: true }; - break; - } - default: { - throw new Error(`Unrecognized file system flag: ${flag}`); - } - } - - return openOptions; -} diff --git a/std/node/_fs/_fs_constants.ts b/std/node/_fs/_fs_constants.ts deleted file mode 100644 index 7f740276bf..0000000000 --- a/std/node/_fs/_fs_constants.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -//File access constants -export const F_OK = 0; -export const R_OK = 4; -export const W_OK = 2; -export const X_OK = 1; - -//File mode constants -export const S_IRUSR = 0o400; //read by owner -export const S_IWUSR = 0o200; //write by owner -export const S_IXUSR = 0o100; //execute/search by owner -export const S_IRGRP = 0o40; //read by group -export const S_IWGRP = 0o20; //write by group -export const S_IXGRP = 0o10; //execute/search by group -export const S_IROTH = 0o4; //read by others -export const S_IWOTH = 0o2; //write by others -export const S_IXOTH = 0o1; //execute/search by others diff --git a/std/node/_fs/_fs_copy.ts b/std/node/_fs/_fs_copy.ts deleted file mode 100644 index 3242b97b30..0000000000 --- a/std/node/_fs/_fs_copy.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import type { CallbackWithError } from "./_fs_common.ts"; -import { fromFileUrl } from "../path.ts"; - -export function copyFile( - source: string | URL, - destination: string, - callback: CallbackWithError, -): void { - source = source instanceof URL ? fromFileUrl(source) : source; - - Deno.copyFile(source, destination).then(() => callback(null), callback); -} - -export function copyFileSync(source: string | URL, destination: string): void { - source = source instanceof URL ? fromFileUrl(source) : source; - Deno.copyFileSync(source, destination); -} diff --git a/std/node/_fs/_fs_copy_test.ts b/std/node/_fs/_fs_copy_test.ts deleted file mode 100644 index 4fdf78cb0d..0000000000 --- a/std/node/_fs/_fs_copy_test.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import * as path from "../../path/mod.ts"; -import { assert } from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { copyFile, copyFileSync } from "./_fs_copy.ts"; -import { existsSync } from "./_fs_exists.ts"; - -const destFile = "./destination.txt"; - -Deno.test({ - name: "[std/node/fs] copy file", - fn: async () => { - const sourceFile = Deno.makeTempFileSync(); - const err = await new Promise((resolve) => { - copyFile(sourceFile, destFile, (err?: Error | null) => resolve(err)); - }); - assert(!err); - assert(existsSync(destFile)); - Deno.removeSync(sourceFile); - Deno.removeSync(destFile); - }, -}); - -Deno.test({ - name: "[std/node/fs] copy file sync", - fn: () => { - const sourceFile = Deno.makeTempFileSync(); - copyFileSync(sourceFile, destFile); - assert(existsSync(destFile)); - Deno.removeSync(sourceFile); - Deno.removeSync(destFile); - }, -}); - -Deno.test("[std/node/fs] copyFile callback isn't called twice if error is thrown", async () => { - // The correct behaviour is not to catch any errors thrown, - // but that means there'll be an uncaught error and the test will fail. - // So the only way to test this is to spawn a subprocess, and succeed if it has a non-zero exit code. - // (assertThrowsAsync won't work because there's no way to catch the error.) - const tempDir = await Deno.makeTempDir(); - const tempFile1 = path.join(tempDir, "file1.txt"); - const tempFile2 = path.join(tempDir, "file2.txt"); - await Deno.writeTextFile(tempFile1, "hello world"); - const importUrl = new URL("./_fs_copy.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { copyFile } from ${JSON.stringify(importUrl)}`, - invocation: `copyFile(${JSON.stringify(tempFile1)}, - ${JSON.stringify(tempFile2)}, `, - async cleanup() { - await Deno.remove(tempDir, { recursive: true }); - }, - }); -}); diff --git a/std/node/_fs/_fs_dir.ts b/std/node/_fs/_fs_dir.ts deleted file mode 100644 index 4f7beec31f..0000000000 --- a/std/node/_fs/_fs_dir.ts +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import Dirent from "./_fs_dirent.ts"; -import { assert } from "../../_util/assert.ts"; - -export default class Dir { - private dirPath: string | Uint8Array; - private syncIterator!: Iterator | null; - private asyncIterator!: AsyncIterator | null; - - constructor(path: string | Uint8Array) { - this.dirPath = path; - } - - get path(): string { - if (this.dirPath instanceof Uint8Array) { - return new TextDecoder().decode(this.dirPath); - } - return this.dirPath; - } - - // deno-lint-ignore no-explicit-any - read(callback?: (...args: any[]) => void): Promise { - return new Promise((resolve, reject) => { - if (!this.asyncIterator) { - this.asyncIterator = Deno.readDir(this.path)[Symbol.asyncIterator](); - } - assert(this.asyncIterator); - this.asyncIterator - .next() - .then(({ value }) => { - resolve(value ? value : null); - if (callback) { - callback(null, value ? value : null); - } - }, (err) => { - if (callback) { - callback(err); - } - reject(err); - }); - }); - } - - readSync(): Dirent | null { - if (!this.syncIterator) { - this.syncIterator = Deno.readDirSync(this.path)![Symbol.iterator](); - } - - const file: Deno.DirEntry = this.syncIterator.next().value; - - return file ? new Dirent(file) : null; - } - - /** - * Unlike Node, Deno does not require managing resource ids for reading - * directories, and therefore does not need to close directories when - * finished reading. - */ - // deno-lint-ignore no-explicit-any - close(callback?: (...args: any[]) => void): Promise { - return new Promise((resolve) => { - if (callback) { - callback(null); - } - resolve(); - }); - } - - /** - * Unlike Node, Deno does not require managing resource ids for reading - * directories, and therefore does not need to close directories when - * finished reading - */ - closeSync(): void { - //No op - } - - async *[Symbol.asyncIterator](): AsyncIterableIterator { - try { - while (true) { - const dirent: Dirent | null = await this.read(); - if (dirent === null) { - break; - } - yield dirent; - } - } finally { - await this.close(); - } - } -} diff --git a/std/node/_fs/_fs_dir_test.ts b/std/node/_fs/_fs_dir_test.ts deleted file mode 100644 index f12563fd51..0000000000 --- a/std/node/_fs/_fs_dir_test.ts +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals, fail } from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import Dir from "./_fs_dir.ts"; -import type Dirent from "./_fs_dirent.ts"; - -Deno.test({ - name: "Closing current directory with callback is successful", - fn() { - let calledBack = false; - // deno-lint-ignore no-explicit-any - new Dir(".").close((valOrErr: any) => { - assert(!valOrErr); - calledBack = true; - }); - assert(calledBack); - }, -}); - -Deno.test({ - name: "Closing current directory without callback returns void Promise", - async fn() { - await new Dir(".").close(); - }, -}); - -Deno.test({ - name: "Closing current directory synchronously works", - fn() { - new Dir(".").closeSync(); - }, -}); - -Deno.test({ - name: "Path is correctly returned", - fn() { - assertEquals(new Dir("std/node").path, "std/node"); - - const enc: Uint8Array = new TextEncoder().encode("std/node"); - assertEquals(new Dir(enc).path, "std/node"); - }, -}); - -Deno.test({ - name: "read returns null for empty directory", - async fn() { - const testDir: string = Deno.makeTempDirSync(); - try { - const file: Dirent | null = await new Dir(testDir).read(); - assert(file === null); - - let calledBack = false; - const fileFromCallback: Dirent | null = await new Dir( - testDir, - // deno-lint-ignore no-explicit-any - ).read((err: any, res: Dirent) => { - assert(res === null); - assert(err === null); - calledBack = true; - }); - assert(fileFromCallback === null); - assert(calledBack); - - assertEquals(new Dir(testDir).readSync(), null); - } finally { - Deno.removeSync(testDir); - } - }, -}); - -Deno.test({ - name: "Async read returns one file at a time", - async fn() { - const testDir: string = Deno.makeTempDirSync(); - const f1 = Deno.createSync(testDir + "/foo.txt"); - f1.close(); - const f2 = Deno.createSync(testDir + "/bar.txt"); - f2.close(); - - try { - let secondCallback = false; - const dir: Dir = new Dir(testDir); - const firstRead: Dirent | null = await dir.read(); - const secondRead: Dirent | null = await dir.read( - // deno-lint-ignore no-explicit-any - (err: any, secondResult: Dirent) => { - assert( - secondResult.name === "bar.txt" || - secondResult.name === "foo.txt", - ); - secondCallback = true; - }, - ); - const thirdRead: Dirent | null = await dir.read(); - const fourthRead: Dirent | null = await dir.read(); - - if (firstRead?.name === "foo.txt") { - assertEquals(secondRead?.name, "bar.txt"); - } else if (firstRead?.name === "bar.txt") { - assertEquals(secondRead?.name, "foo.txt"); - } else { - fail("File not found during read"); - } - assert(secondCallback); - assert(thirdRead === null); - assert(fourthRead === null); - } finally { - Deno.removeSync(testDir, { recursive: true }); - } - }, -}); - -Deno.test({ - name: "Sync read returns one file at a time", - fn() { - const testDir: string = Deno.makeTempDirSync(); - const f1 = Deno.createSync(testDir + "/foo.txt"); - f1.close(); - const f2 = Deno.createSync(testDir + "/bar.txt"); - f2.close(); - - try { - const dir: Dir = new Dir(testDir); - const firstRead: Dirent | null = dir.readSync(); - const secondRead: Dirent | null = dir.readSync(); - const thirdRead: Dirent | null = dir.readSync(); - const fourthRead: Dirent | null = dir.readSync(); - - if (firstRead?.name === "foo.txt") { - assertEquals(secondRead?.name, "bar.txt"); - } else if (firstRead?.name === "bar.txt") { - assertEquals(secondRead?.name, "foo.txt"); - } else { - fail("File not found during read"); - } - assert(thirdRead === null); - assert(fourthRead === null); - } finally { - Deno.removeSync(testDir, { recursive: true }); - } - }, -}); - -Deno.test({ - name: "Async iteration over existing directory", - async fn() { - const testDir: string = Deno.makeTempDirSync(); - const f1 = Deno.createSync(testDir + "/foo.txt"); - f1.close(); - const f2 = Deno.createSync(testDir + "/bar.txt"); - f2.close(); - - try { - const dir: Dir = new Dir(testDir); - const results: Array = []; - - for await (const file of dir[Symbol.asyncIterator]()) { - results.push(file.name); - } - - assert(results.length === 2); - assert(results.includes("foo.txt")); - assert(results.includes("bar.txt")); - } finally { - Deno.removeSync(testDir, { recursive: true }); - } - }, -}); - -Deno.test("[std/node/fs] Dir.close callback isn't called twice if error is thrown", async () => { - const tempDir = await Deno.makeTempDir(); - const importUrl = new URL("./_fs_dir.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: ` - import Dir from ${JSON.stringify(importUrl)}; - - const dir = new Dir(${JSON.stringify(tempDir)}); - `, - invocation: "dir.close(", - async cleanup() { - await Deno.remove(tempDir); - }, - }); -}); - -Deno.test("[std/node/fs] Dir.read callback isn't called twice if error is thrown", async () => { - const tempDir = await Deno.makeTempDir(); - const importUrl = new URL("./_fs_dir.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: ` - import Dir from ${JSON.stringify(importUrl)}; - - const dir = new Dir(${JSON.stringify(tempDir)}); - `, - invocation: "dir.read(", - async cleanup() { - await Deno.remove(tempDir); - }, - }); -}); diff --git a/std/node/_fs/_fs_dirent.ts b/std/node/_fs/_fs_dirent.ts deleted file mode 100644 index b44ebcb020..0000000000 --- a/std/node/_fs/_fs_dirent.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { notImplemented } from "../_utils.ts"; - -export default class Dirent { - constructor(private entry: Deno.DirEntry) {} - - isBlockDevice(): boolean { - notImplemented("Deno does not yet support identification of block devices"); - return false; - } - - isCharacterDevice(): boolean { - notImplemented( - "Deno does not yet support identification of character devices", - ); - return false; - } - - isDirectory(): boolean { - return this.entry.isDirectory; - } - - isFIFO(): boolean { - notImplemented( - "Deno does not yet support identification of FIFO named pipes", - ); - return false; - } - - isFile(): boolean { - return this.entry.isFile; - } - - isSocket(): boolean { - notImplemented("Deno does not yet support identification of sockets"); - return false; - } - - isSymbolicLink(): boolean { - return this.entry.isSymlink; - } - - get name(): string | null { - return this.entry.name; - } -} diff --git a/std/node/_fs/_fs_dirent_test.ts b/std/node/_fs/_fs_dirent_test.ts deleted file mode 100644 index ad599c84cb..0000000000 --- a/std/node/_fs/_fs_dirent_test.ts +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals, assertThrows } from "../../testing/asserts.ts"; -import Dirent from "./_fs_dirent.ts"; - -class DirEntryMock implements Deno.DirEntry { - name = ""; - isFile = false; - isDirectory = false; - isSymlink = false; -} - -Deno.test({ - name: "Directories are correctly identified", - fn() { - const entry: DirEntryMock = new DirEntryMock(); - entry.isDirectory = true; - entry.isFile = false; - entry.isSymlink = false; - assert(new Dirent(entry).isDirectory()); - assert(!new Dirent(entry).isFile()); - assert(!new Dirent(entry).isSymbolicLink()); - }, -}); - -Deno.test({ - name: "Files are correctly identified", - fn() { - const entry: DirEntryMock = new DirEntryMock(); - entry.isDirectory = false; - entry.isFile = true; - entry.isSymlink = false; - assert(!new Dirent(entry).isDirectory()); - assert(new Dirent(entry).isFile()); - assert(!new Dirent(entry).isSymbolicLink()); - }, -}); - -Deno.test({ - name: "Symlinks are correctly identified", - fn() { - const entry: DirEntryMock = new DirEntryMock(); - entry.isDirectory = false; - entry.isFile = false; - entry.isSymlink = true; - assert(!new Dirent(entry).isDirectory()); - assert(!new Dirent(entry).isFile()); - assert(new Dirent(entry).isSymbolicLink()); - }, -}); - -Deno.test({ - name: "File name is correct", - fn() { - const entry: DirEntryMock = new DirEntryMock(); - entry.name = "my_file"; - assertEquals(new Dirent(entry).name, "my_file"); - }, -}); - -Deno.test({ - name: "Socket and FIFO pipes aren't yet available", - fn() { - const entry: DirEntryMock = new DirEntryMock(); - assertThrows( - () => { - new Dirent(entry).isFIFO(); - }, - Error, - "does not yet support", - ); - assertThrows( - () => { - new Dirent(entry).isSocket(); - }, - Error, - "does not yet support", - ); - }, -}); diff --git a/std/node/_fs/_fs_exists.ts b/std/node/_fs/_fs_exists.ts deleted file mode 100644 index 5cfa26a9d1..0000000000 --- a/std/node/_fs/_fs_exists.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { fromFileUrl } from "../path.ts"; - -type ExitsCallback = (exists: boolean) => void; - -/** - * TODO: Also accept 'path' parameter as a Node polyfill Buffer type once these - * are implemented. See https://github.com/denoland/deno/issues/3403 - * Deprecated in node api - */ -export function exists(path: string | URL, callback: ExitsCallback): void { - path = path instanceof URL ? fromFileUrl(path) : path; - Deno.lstat(path).then(() => callback(true), () => callback(false)); -} - -/** - * TODO: Also accept 'path' parameter as a Node polyfill Buffer or URL type once these - * are implemented. See https://github.com/denoland/deno/issues/3403 - */ -export function existsSync(path: string | URL): boolean { - path = path instanceof URL ? fromFileUrl(path) : path; - try { - Deno.lstatSync(path); - return true; - } catch (err) { - if (err instanceof Deno.errors.NotFound) { - return false; - } - throw err; - } -} diff --git a/std/node/_fs/_fs_exists_test.ts b/std/node/_fs/_fs_exists_test.ts deleted file mode 100644 index ab0ae5c15a..0000000000 --- a/std/node/_fs/_fs_exists_test.ts +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertStringIncludes, -} from "../../testing/asserts.ts"; -import { exists, existsSync } from "./_fs_exists.ts"; - -Deno.test("existsFile", async function () { - const availableFile = await new Promise((resolve) => { - const tmpFilePath = Deno.makeTempFileSync(); - exists(tmpFilePath, (exists: boolean) => { - Deno.removeSync(tmpFilePath); - resolve(exists); - }); - }); - const notAvailableFile = await new Promise((resolve) => { - exists("./notAvailable.txt", (exists: boolean) => resolve(exists)); - }); - assertEquals(availableFile, true); - assertEquals(notAvailableFile, false); -}); - -Deno.test("existsSyncFile", function () { - const tmpFilePath = Deno.makeTempFileSync(); - assertEquals(existsSync(tmpFilePath), true); - Deno.removeSync(tmpFilePath); - assertEquals(existsSync("./notAvailable.txt"), false); -}); - -Deno.test("[std/node/fs] exists callback isn't called twice if error is thrown", async () => { - // This doesn't use `assertCallbackErrorUncaught()` because `exists()` doesn't return a standard node callback, which is what it expects. - const tempFile = await Deno.makeTempFile(); - const importUrl = new URL("./_fs_exists.ts", import.meta.url); - const p = Deno.run({ - cmd: [ - Deno.execPath(), - "eval", - "--no-check", - ` - import { exists } from ${JSON.stringify(importUrl)}; - - exists(${JSON.stringify(tempFile)}, (exists) => { - // If the bug is present and the callback is called again with false (meaning an error occured), - // don't throw another error, so if the subprocess fails we know it had the correct behaviour. - if (exists) throw new Error("success"); - });`, - ], - stderr: "piped", - }); - const status = await p.status(); - const stderr = new TextDecoder().decode(await Deno.readAll(p.stderr)); - p.close(); - p.stderr.close(); - await Deno.remove(tempFile); - assert(!status.success); - assertStringIncludes(stderr, "Error: success"); -}); diff --git a/std/node/_fs/_fs_link.ts b/std/node/_fs/_fs_link.ts deleted file mode 100644 index 14a8189cb5..0000000000 --- a/std/node/_fs/_fs_link.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import type { CallbackWithError } from "./_fs_common.ts"; -import { fromFileUrl } from "../path.ts"; - -/** - * TODO: Also accept 'path' parameter as a Node polyfill Buffer type once these - * are implemented. See https://github.com/denoland/deno/issues/3403 - */ -export function link( - existingPath: string | URL, - newPath: string | URL, - callback: CallbackWithError, -): void { - existingPath = existingPath instanceof URL - ? fromFileUrl(existingPath) - : existingPath; - newPath = newPath instanceof URL ? fromFileUrl(newPath) : newPath; - - Deno.link(existingPath, newPath).then(() => callback(null), callback); -} - -/** - * TODO: Also accept 'path' parameter as a Node polyfill Buffer type once these - * are implemented. See https://github.com/denoland/deno/issues/3403 - */ -export function linkSync( - existingPath: string | URL, - newPath: string | URL, -): void { - existingPath = existingPath instanceof URL - ? fromFileUrl(existingPath) - : existingPath; - newPath = newPath instanceof URL ? fromFileUrl(newPath) : newPath; - - Deno.linkSync(existingPath, newPath); -} diff --git a/std/node/_fs/_fs_link_test.ts b/std/node/_fs/_fs_link_test.ts deleted file mode 100644 index 9e2aeb8b6a..0000000000 --- a/std/node/_fs/_fs_link_test.ts +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import * as path from "../../path/mod.ts"; -import { assert, assertEquals, fail } from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { link, linkSync } from "./_fs_link.ts"; - -Deno.test({ - name: "ASYNC: hard linking files works as expected", - async fn() { - const tempFile: string = await Deno.makeTempFile(); - const linkedFile: string = tempFile + ".link"; - await new Promise((res, rej) => { - link(tempFile, linkedFile, (err) => { - if (err) rej(err); - else res(); - }); - }) - .then(() => { - assertEquals(Deno.statSync(tempFile), Deno.statSync(linkedFile)); - }, () => { - fail("Expected to succeed"); - }) - .finally(() => { - Deno.removeSync(tempFile); - Deno.removeSync(linkedFile); - }); - }, -}); - -Deno.test({ - name: "ASYNC: hard linking files passes error to callback", - async fn() { - let failed = false; - await new Promise((res, rej) => { - link("no-such-file", "no-such-file", (err) => { - if (err) rej(err); - else res(); - }); - }) - .then(() => { - fail("Expected to succeed"); - }, (err) => { - assert(err); - failed = true; - }); - assert(failed); - }, -}); - -Deno.test({ - name: "SYNC: hard linking files works as expected", - fn() { - const tempFile: string = Deno.makeTempFileSync(); - const linkedFile: string = tempFile + ".link"; - linkSync(tempFile, linkedFile); - - assertEquals(Deno.statSync(tempFile), Deno.statSync(linkedFile)); - Deno.removeSync(tempFile); - Deno.removeSync(linkedFile); - }, -}); - -Deno.test("[std/node/fs] link callback isn't called twice if error is thrown", async () => { - const tempDir = await Deno.makeTempDir(); - const tempFile = path.join(tempDir, "file.txt"); - const linkFile = path.join(tempDir, "link.txt"); - await Deno.writeTextFile(tempFile, "hello world"); - const importUrl = new URL("./_fs_link.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { link } from ${JSON.stringify(importUrl)}`, - invocation: `link(${JSON.stringify(tempFile)}, - ${JSON.stringify(linkFile)}, `, - async cleanup() { - await Deno.remove(tempDir, { recursive: true }); - }, - }); -}); diff --git a/std/node/_fs/_fs_lstat.ts b/std/node/_fs/_fs_lstat.ts deleted file mode 100644 index 55624cd5ea..0000000000 --- a/std/node/_fs/_fs_lstat.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - BigIntStats, - CFISBIS, - statCallback, - statCallbackBigInt, - statOptions, - Stats, -} from "./_fs_stat.ts"; - -export function lstat(path: string | URL, callback: statCallback): void; -export function lstat( - path: string | URL, - options: { bigint: false }, - callback: statCallback, -): void; -export function lstat( - path: string | URL, - options: { bigint: true }, - callback: statCallbackBigInt, -): void; -export function lstat( - path: string | URL, - optionsOrCallback: statCallback | statCallbackBigInt | statOptions, - maybeCallback?: statCallback | statCallbackBigInt, -) { - const callback = - (typeof optionsOrCallback === "function" - ? optionsOrCallback - : maybeCallback) as ( - ...args: [Error] | [null, BigIntStats | Stats] - ) => void; - const options = typeof optionsOrCallback === "object" - ? optionsOrCallback - : { bigint: false }; - - if (!callback) throw new Error("No callback function supplied"); - - Deno.lstat(path).then( - (stat) => callback(null, CFISBIS(stat, options.bigint)), - (err) => callback(err), - ); -} - -export function lstatSync(path: string | URL): Stats; -export function lstatSync( - path: string | URL, - options: { bigint: false }, -): Stats; -export function lstatSync( - path: string | URL, - options: { bigint: true }, -): BigIntStats; -export function lstatSync( - path: string | URL, - options?: statOptions, -): Stats | BigIntStats { - const origin = Deno.lstatSync(path); - return CFISBIS(origin, options?.bigint || false); -} diff --git a/std/node/_fs/_fs_lstat_test.ts b/std/node/_fs/_fs_lstat_test.ts deleted file mode 100644 index b8c5625383..0000000000 --- a/std/node/_fs/_fs_lstat_test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { lstat, lstatSync } from "./_fs_lstat.ts"; -import { fail } from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { assertStats, assertStatsBigInt } from "./_fs_stat_test.ts"; -import type { BigIntStats, Stats } from "./_fs_stat.ts"; - -Deno.test({ - name: "ASYNC: get a file Stats (lstat)", - async fn() { - const file = Deno.makeTempFileSync(); - await new Promise((resolve, reject) => { - lstat(file, (err, stat) => { - if (err) reject(err); - resolve(stat); - }); - }) - .then((stat) => { - assertStats(stat, Deno.lstatSync(file)); - }, () => fail()) - .finally(() => { - Deno.removeSync(file); - }); - }, -}); - -Deno.test({ - name: "SYNC: get a file Stats (lstat)", - fn() { - const file = Deno.makeTempFileSync(); - assertStats(lstatSync(file), Deno.lstatSync(file)); - }, -}); - -Deno.test({ - name: "ASYNC: get a file BigInt Stats (lstat)", - async fn() { - const file = Deno.makeTempFileSync(); - await new Promise((resolve, reject) => { - lstat(file, { bigint: true }, (err, stat) => { - if (err) reject(err); - resolve(stat); - }); - }) - .then( - (stat) => assertStatsBigInt(stat, Deno.lstatSync(file)), - () => fail(), - ) - .finally(() => Deno.removeSync(file)); - }, -}); - -Deno.test({ - name: "SYNC: BigInt Stats (lstat)", - fn() { - const file = Deno.makeTempFileSync(); - assertStatsBigInt(lstatSync(file, { bigint: true }), Deno.lstatSync(file)); - }, -}); - -Deno.test("[std/node/fs] lstat callback isn't called twice if error is thrown", async () => { - const tempFile = await Deno.makeTempFile(); - const importUrl = new URL("./_fs_lstat.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { lstat } from ${JSON.stringify(importUrl)}`, - invocation: `lstat(${JSON.stringify(tempFile)}, `, - async cleanup() { - await Deno.remove(tempFile); - }, - }); -}); diff --git a/std/node/_fs/_fs_mkdir.ts b/std/node/_fs/_fs_mkdir.ts deleted file mode 100644 index 94cc24a42f..0000000000 --- a/std/node/_fs/_fs_mkdir.ts +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import type { CallbackWithError } from "./_fs_common.ts"; -import { fromFileUrl } from "../path.ts"; - -/** - * TODO: Also accept 'path' parameter as a Node polyfill Buffer type once these - * are implemented. See https://github.com/denoland/deno/issues/3403 - */ -type MkdirOptions = - | { recursive?: boolean; mode?: number | undefined } - | number - | boolean; - -export function mkdir( - path: string | URL, - options?: MkdirOptions | CallbackWithError, - callback?: CallbackWithError, -): void { - path = path instanceof URL ? fromFileUrl(path) : path; - - let mode = 0o777; - let recursive = false; - - if (typeof options == "function") { - callback = options; - } else if (typeof options === "number") { - mode = options; - } else if (typeof options === "boolean") { - recursive = options; - } else if (options) { - if (options.recursive !== undefined) recursive = options.recursive; - if (options.mode !== undefined) mode = options.mode; - } - if (typeof recursive !== "boolean") { - throw new Deno.errors.InvalidData( - "invalid recursive option , must be a boolean", - ); - } - Deno.mkdir(path, { recursive, mode }) - .then(() => { - if (typeof callback === "function") { - callback(null); - } - }, (err) => { - if (typeof callback === "function") { - callback(err); - } - }); -} - -export function mkdirSync(path: string | URL, options?: MkdirOptions): void { - path = path instanceof URL ? fromFileUrl(path) : path; - let mode = 0o777; - let recursive = false; - - if (typeof options === "number") { - mode = options; - } else if (typeof options === "boolean") { - recursive = options; - } else if (options) { - if (options.recursive !== undefined) recursive = options.recursive; - if (options.mode !== undefined) mode = options.mode; - } - if (typeof recursive !== "boolean") { - throw new Deno.errors.InvalidData( - "invalid recursive option , must be a boolean", - ); - } - - Deno.mkdirSync(path, { recursive, mode }); -} diff --git a/std/node/_fs/_fs_mkdir_test.ts b/std/node/_fs/_fs_mkdir_test.ts deleted file mode 100644 index c13d065fe8..0000000000 --- a/std/node/_fs/_fs_mkdir_test.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import * as path from "../../path/mod.ts"; -import { assert } from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { mkdir, mkdirSync } from "./_fs_mkdir.ts"; -import { existsSync } from "./_fs_exists.ts"; - -const tmpDir = "./tmpdir"; - -Deno.test({ - name: "[node/fs] mkdir", - fn: async () => { - const result = await new Promise((resolve) => { - mkdir(tmpDir, (err) => { - err && resolve(false); - resolve(existsSync(tmpDir)); - Deno.removeSync(tmpDir); - }); - }); - assert(result); - }, -}); - -Deno.test({ - name: "[node/fs] mkdirSync", - fn: () => { - mkdirSync(tmpDir); - assert(existsSync(tmpDir)); - Deno.removeSync(tmpDir); - }, -}); - -Deno.test("[std/node/fs] mkdir callback isn't called twice if error is thrown", async () => { - const tempDir = await Deno.makeTempDir(); - const subdir = path.join(tempDir, "subdir"); - const importUrl = new URL("./_fs_mkdir.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { mkdir } from ${JSON.stringify(importUrl)}`, - invocation: `mkdir(${JSON.stringify(subdir)}, `, - async cleanup() { - await Deno.remove(tempDir, { recursive: true }); - }, - }); -}); diff --git a/std/node/_fs/_fs_mkdtemp.ts b/std/node/_fs/_fs_mkdtemp.ts deleted file mode 100644 index 226399aa4d..0000000000 --- a/std/node/_fs/_fs_mkdtemp.ts +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { existsSync } from "./_fs_exists.ts"; -import { mkdir, mkdirSync } from "./_fs_mkdir.ts"; -import { - ERR_INVALID_CALLBACK, - ERR_INVALID_OPT_VALUE_ENCODING, -} from "../_errors.ts"; - -export type mkdtempCallback = ( - err: Error | null, - directory?: string, -) => void; - -// https://nodejs.org/dist/latest-v15.x/docs/api/fs.html#fs_fs_mkdtemp_prefix_options_callback -export function mkdtemp(prefix: string, callback: mkdtempCallback): void; -export function mkdtemp( - prefix: string, - options: { encoding: string } | string, - callback: mkdtempCallback, -): void; -export function mkdtemp( - prefix: string, - optionsOrCallback: { encoding: string } | string | mkdtempCallback, - maybeCallback?: mkdtempCallback, -): void { - const callback: mkdtempCallback | undefined = - typeof optionsOrCallback == "function" ? optionsOrCallback : maybeCallback; - if (!callback) throw new ERR_INVALID_CALLBACK(callback); - - const encoding: string | undefined = parseEncoding(optionsOrCallback); - const path = tempDirPath(prefix); - - mkdir( - path, - { recursive: false, mode: 0o700 }, - (err: Error | null | undefined) => { - if (err) callback(err); - else callback(null, decode(path, encoding)); - }, - ); -} - -// https://nodejs.org/dist/latest-v15.x/docs/api/fs.html#fs_fs_mkdtempsync_prefix_options -export function mkdtempSync( - prefix: string, - options?: { encoding: string } | string, -): string { - const encoding: string | undefined = parseEncoding(options); - const path = tempDirPath(prefix); - - mkdirSync(path, { recursive: false, mode: 0o700 }); - return decode(path, encoding); -} - -function parseEncoding( - optionsOrCallback?: { encoding: string } | string | mkdtempCallback, -): string | undefined { - let encoding: string | undefined; - if (typeof optionsOrCallback == "function") encoding = undefined; - else if (optionsOrCallback instanceof Object) { - encoding = optionsOrCallback?.encoding; - } else encoding = optionsOrCallback; - - if (encoding) { - try { - new TextDecoder(encoding); - } catch (error) { - throw new ERR_INVALID_OPT_VALUE_ENCODING(encoding); - } - } - - return encoding; -} - -function decode(str: string, encoding?: string): string { - if (!encoding) return str; - else { - const decoder = new TextDecoder(encoding); - const encoder = new TextEncoder(); - return decoder.decode(encoder.encode(str)); - } -} - -const CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; -function randomName(): string { - return [...Array(6)].map(() => - CHARS[Math.floor(Math.random() * CHARS.length)] - ).join(""); -} - -function tempDirPath(prefix: string): string { - let path: string; - do { - path = prefix + randomName(); - } while (existsSync(path)); - - return path; -} diff --git a/std/node/_fs/_fs_mkdtemp_test.ts b/std/node/_fs/_fs_mkdtemp_test.ts deleted file mode 100644 index d41c52794f..0000000000 --- a/std/node/_fs/_fs_mkdtemp_test.ts +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertThrows, - assertThrowsAsync, -} from "../../testing/asserts.ts"; -import { mkdtemp, mkdtempSync } from "./_fs_mkdtemp.ts"; -import { existsSync } from "./_fs_exists.ts"; -import { env } from "../process.ts"; -import { isWindows } from "../../_util/os.ts"; -import { promisify } from "../_util/_util_promisify.ts"; - -const prefix = isWindows ? env.TEMP + "\\" : (env.TMPDIR || "/tmp") + "/"; -const doesNotExists = "/does/not/exists/"; -const options = { encoding: "ascii" }; -const badOptions = { encoding: "bogus" }; - -const mkdtempP = promisify(mkdtemp); - -Deno.test({ - name: "[node/fs] mkdtemp", - fn: async () => { - const directory = await mkdtempP(prefix); - assert(existsSync(directory)); - Deno.removeSync(directory); - }, -}); - -Deno.test({ - name: "[node/fs] mkdtemp (does not exists)", - fn: async () => { - await assertThrowsAsync(() => mkdtempP(doesNotExists)); - }, -}); - -Deno.test({ - name: "[node/fs] mkdtemp (with options)", - fn: async () => { - const directory = await mkdtempP(prefix, options); - assert(existsSync(directory)); - Deno.removeSync(directory); - }, -}); - -Deno.test({ - name: "[node/fs] mkdtemp (with bad options)", - fn: async () => { - await assertThrowsAsync(() => mkdtempP(prefix, badOptions)); - }, -}); - -Deno.test({ - name: "[node/fs] mkdtempSync", - fn: () => { - const directory = mkdtempSync(prefix); - const dirExists = existsSync(directory); - Deno.removeSync(directory); - assert(dirExists); - }, -}); - -Deno.test({ - name: "[node/fs] mkdtempSync (does not exists)", - fn: () => { - assertThrows(() => mkdtempSync(doesNotExists)); - }, -}); - -Deno.test({ - name: "[node/fs] mkdtempSync (with options)", - fn: () => { - const directory = mkdtempSync(prefix, options); - const dirExists = existsSync(directory); - Deno.removeSync(directory); - assert(dirExists); - }, -}); - -Deno.test({ - name: "[node/fs] mkdtempSync (with bad options)", - fn: () => { - assertThrows(() => mkdtempSync(prefix, badOptions)); - }, -}); diff --git a/std/node/_fs/_fs_open.ts b/std/node/_fs/_fs_open.ts deleted file mode 100644 index 55ecbdc1eb..0000000000 --- a/std/node/_fs/_fs_open.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { existsSync } from "../../fs/mod.ts"; -import { fromFileUrl } from "../path.ts"; -import { getOpenOptions } from "./_fs_common.ts"; - -type openFlags = - | "a" - | "ax" - | "a+" - | "ax+" - | "as" - | "as+" - | "r" - | "r+" - | "rs+" - | "w" - | "wx" - | "w+" - | "wx+"; - -type openCallback = (err: Error | null, fd: number) => void; - -function convertFlagAndModeToOptions( - flag?: openFlags, - mode?: number, -): Deno.OpenOptions | undefined { - if (!flag && !mode) return undefined; - if (!flag && mode) return { mode }; - return { ...getOpenOptions(flag), mode }; -} - -export function open(path: string | URL, callback: openCallback): void; -export function open( - path: string | URL, - flags: openFlags, - callback: openCallback, -): void; -export function open( - path: string | URL, - flags: openFlags, - mode: number, - callback: openCallback, -): void; -export function open( - path: string | URL, - flagsOrCallback: openCallback | openFlags, - callbackOrMode?: openCallback | number, - maybeCallback?: openCallback, -) { - const flags = typeof flagsOrCallback === "string" - ? flagsOrCallback - : undefined; - const callback = typeof flagsOrCallback === "function" - ? flagsOrCallback - : typeof callbackOrMode === "function" - ? callbackOrMode - : maybeCallback; - const mode = typeof callbackOrMode === "number" ? callbackOrMode : undefined; - path = path instanceof URL ? fromFileUrl(path) : path; - - if (!callback) throw new Error("No callback function supplied"); - - if (["ax", "ax+", "wx", "wx+"].includes(flags || "") && existsSync(path)) { - const err = new Error(`EEXIST: file already exists, open '${path}'`); - (callback as (err: Error) => void)(err); - } else { - if (flags === "as" || flags === "as+") { - let err: Error | null = null, res: number; - try { - res = openSync(path, flags, mode); - } catch (error) { - err = error; - } - if (err) { - (callback as (err: Error) => void)(err); - } else { - callback(null, res!); - } - return; - } - Deno.open(path, convertFlagAndModeToOptions(flags, mode)).then( - (file) => callback(null, file.rid), - (err) => (callback as (err: Error) => void)(err), - ); - } -} - -export function openSync(path: string | URL): number; -export function openSync(path: string | URL, flags?: openFlags): number; -export function openSync(path: string | URL, mode?: number): number; -export function openSync( - path: string | URL, - flags?: openFlags, - mode?: number, -): number; -export function openSync( - path: string | URL, - flagsOrMode?: openFlags | number, - maybeMode?: number, -) { - const flags = typeof flagsOrMode === "string" ? flagsOrMode : undefined; - const mode = typeof flagsOrMode === "number" ? flagsOrMode : maybeMode; - path = path instanceof URL ? fromFileUrl(path) : path; - - if (["ax", "ax+", "wx", "wx+"].includes(flags || "") && existsSync(path)) { - throw new Error(`EEXIST: file already exists, open '${path}'`); - } - - return Deno.openSync(path, convertFlagAndModeToOptions(flags, mode)).rid; -} diff --git a/std/node/_fs/_fs_open_test.ts b/std/node/_fs/_fs_open_test.ts deleted file mode 100644 index f32a8ce0a7..0000000000 --- a/std/node/_fs/_fs_open_test.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { - assert, - assertEquals, - assertThrows, - fail, -} from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { open, openSync } from "./_fs_open.ts"; -import { join, parse } from "../../path/mod.ts"; -import { existsSync } from "../../fs/mod.ts"; -import { closeSync } from "./_fs_close.ts"; - -const tempDir = parse(Deno.makeTempFileSync()).dir; - -Deno.test({ - name: "ASYNC: open file", - async fn() { - const file = Deno.makeTempFileSync(); - let fd1: number; - await new Promise((resolve, reject) => { - open(file, (err, fd) => { - if (err) reject(err); - resolve(fd); - }); - }) - .then((fd) => { - fd1 = fd; - assert(Deno.resources()[fd], `${fd}`); - }, () => fail()) - .finally(() => closeSync(fd1)); - }, -}); - -Deno.test({ - name: "SYNC: open file", - fn() { - const file = Deno.makeTempFileSync(); - const fd = openSync(file); - assert(Deno.resources()[fd]); - closeSync(fd); - }, -}); - -Deno.test({ - name: "open with flag 'a'", - fn() { - const file = join(tempDir, "some_random_file"); - const fd = openSync(file, "a"); - assertEquals(typeof fd, "number"); - assertEquals(existsSync(file), true); - assert(Deno.resources()[fd]); - closeSync(fd); - }, -}); - -Deno.test({ - name: "open with flag 'ax'", - fn() { - const file = Deno.makeTempFileSync(); - assertThrows( - () => { - openSync(file, "ax"); - }, - Error, - `EEXIST: file already exists, open '${file}'`, - ); - Deno.removeSync(file); - }, -}); - -Deno.test({ - name: "open with flag 'a+'", - fn() { - const file = join(tempDir, "some_random_file2"); - const fd = openSync(file, "a+"); - assertEquals(typeof fd, "number"); - assertEquals(existsSync(file), true); - closeSync(fd); - }, -}); - -Deno.test({ - name: "open with flag 'ax+'", - fn() { - const file = Deno.makeTempFileSync(); - assertThrows( - () => { - openSync(file, "ax+"); - }, - Error, - `EEXIST: file already exists, open '${file}'`, - ); - Deno.removeSync(file); - }, -}); - -Deno.test({ - name: "open with flag 'as'", - fn() { - const file = join(tempDir, "some_random_file10"); - const fd = openSync(file, "as"); - assertEquals(existsSync(file), true); - assertEquals(typeof fd, "number"); - closeSync(fd); - }, -}); - -Deno.test({ - name: "open with flag 'as+'", - fn() { - const file = join(tempDir, "some_random_file10"); - const fd = openSync(file, "as+"); - assertEquals(existsSync(file), true); - assertEquals(typeof fd, "number"); - closeSync(fd); - }, -}); - -Deno.test({ - name: "open with flag 'r'", - fn() { - const file = join(tempDir, "some_random_file3"); - assertThrows(() => { - openSync(file, "r"); - }, Error); - }, -}); - -Deno.test({ - name: "open with flag 'r+'", - fn() { - const file = join(tempDir, "some_random_file4"); - assertThrows(() => { - openSync(file, "r+"); - }, Error); - }, -}); - -Deno.test({ - name: "open with flag 'w'", - fn() { - const file = Deno.makeTempFileSync(); - Deno.writeTextFileSync(file, "hi there"); - const fd = openSync(file, "w"); - assertEquals(typeof fd, "number"); - assertEquals(Deno.readTextFileSync(file), ""); - closeSync(fd); - - const file2 = join(tempDir, "some_random_file5"); - const fd2 = openSync(file2, "w"); - assertEquals(typeof fd2, "number"); - assertEquals(existsSync(file2), true); - closeSync(fd2); - }, -}); - -Deno.test({ - name: "open with flag 'wx'", - fn() { - const file = Deno.makeTempFileSync(); - Deno.writeTextFileSync(file, "hi there"); - const fd = openSync(file, "w"); - assertEquals(typeof fd, "number"); - assertEquals(Deno.readTextFileSync(file), ""); - closeSync(fd); - - const file2 = Deno.makeTempFileSync(); - assertThrows( - () => { - openSync(file2, "wx"); - }, - Error, - `EEXIST: file already exists, open '${file2}'`, - ); - }, -}); - -Deno.test({ - name: "open with flag 'w+'", - fn() { - const file = Deno.makeTempFileSync(); - Deno.writeTextFileSync(file, "hi there"); - const fd = openSync(file, "w+"); - assertEquals(typeof fd, "number"); - assertEquals(Deno.readTextFileSync(file), ""); - closeSync(fd); - - const file2 = join(tempDir, "some_random_file6"); - const fd2 = openSync(file2, "w+"); - assertEquals(typeof fd2, "number"); - assertEquals(existsSync(file2), true); - closeSync(fd2); - }, -}); - -Deno.test({ - name: "open with flag 'wx+'", - fn() { - const file = Deno.makeTempFileSync(); - assertThrows( - () => { - openSync(file, "wx+"); - }, - Error, - `EEXIST: file already exists, open '${file}'`, - ); - Deno.removeSync(file); - }, -}); - -Deno.test("[std/node/fs] open callback isn't called twice if error is thrown", async () => { - const tempFile = await Deno.makeTempFile(); - const importUrl = new URL("./_fs_open.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { open } from ${JSON.stringify(importUrl)}`, - invocation: `open(${JSON.stringify(tempFile)}, `, - async cleanup() { - await Deno.remove(tempFile); - }, - }); -}); diff --git a/std/node/_fs/_fs_readFile.ts b/std/node/_fs/_fs_readFile.ts deleted file mode 100644 index 4ad763e25c..0000000000 --- a/std/node/_fs/_fs_readFile.ts +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - BinaryOptionsArgument, - FileOptionsArgument, - getEncoding, - TextOptionsArgument, -} from "./_fs_common.ts"; -import { Buffer } from "../buffer.ts"; -import { fromFileUrl } from "../path.ts"; -import { BinaryEncodings, Encodings, TextEncodings } from "../_utils.ts"; - -function maybeDecode(data: Uint8Array, encoding: TextEncodings): string; -function maybeDecode( - data: Uint8Array, - encoding: BinaryEncodings | null, -): Buffer; -function maybeDecode( - data: Uint8Array, - encoding: Encodings | null, -): string | Buffer { - const buffer = new Buffer(data.buffer, data.byteOffset, data.byteLength); - if (encoding && encoding !== "binary") return buffer.toString(encoding); - return buffer; -} - -type TextCallback = (err: Error | null, data?: string) => void; -type BinaryCallback = (err: Error | null, data?: Buffer) => void; -type GenericCallback = (err: Error | null, data?: string | Buffer) => void; -type Callback = TextCallback | BinaryCallback | GenericCallback; - -export function readFile( - path: string | URL, - options: TextOptionsArgument, - callback: TextCallback, -): void; -export function readFile( - path: string | URL, - options: BinaryOptionsArgument, - callback: BinaryCallback, -): void; -export function readFile( - path: string | URL, - options: null | undefined | FileOptionsArgument, - callback: BinaryCallback, -): void; -export function readFile(path: string | URL, callback: BinaryCallback): void; -export function readFile( - path: string | URL, - optOrCallback?: FileOptionsArgument | Callback | null | undefined, - callback?: Callback, -): void { - path = path instanceof URL ? fromFileUrl(path) : path; - let cb: Callback | undefined; - if (typeof optOrCallback === "function") { - cb = optOrCallback; - } else { - cb = callback; - } - - const encoding = getEncoding(optOrCallback); - - const p = Deno.readFile(path); - - if (cb) { - p.then((data: Uint8Array) => { - if (encoding && encoding !== "binary") { - const text = maybeDecode(data, encoding); - return (cb as TextCallback)(null, text); - } - const buffer = maybeDecode(data, encoding); - (cb as BinaryCallback)(null, buffer); - }, (err) => cb && cb(err)); - } -} - -export function readFileSync( - path: string | URL, - opt: TextOptionsArgument, -): string; -export function readFileSync( - path: string | URL, - opt?: BinaryOptionsArgument, -): Buffer; -export function readFileSync( - path: string | URL, - opt?: FileOptionsArgument, -): string | Buffer { - path = path instanceof URL ? fromFileUrl(path) : path; - const data = Deno.readFileSync(path); - const encoding = getEncoding(opt); - if (encoding && encoding !== "binary") { - const text = maybeDecode(data, encoding); - return text; - } - const buffer = maybeDecode(data, encoding); - return buffer; -} diff --git a/std/node/_fs/_fs_readFile_test.ts b/std/node/_fs/_fs_readFile_test.ts deleted file mode 100644 index 7af32c8a13..0000000000 --- a/std/node/_fs/_fs_readFile_test.ts +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { readFile, readFileSync } from "./_fs_readFile.ts"; -import * as path from "../../path/mod.ts"; -import { assert, assertEquals } from "../../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 data = await new Promise((res, rej) => { - readFile(testData, (err, data) => { - if (err) { - rej(err); - } - res(data); - }); - }); - - assert(data instanceof Uint8Array); - assertEquals(new TextDecoder().decode(data as Uint8Array), "hello world"); -}); - -Deno.test("readFileEncodeUtf8Success", async function () { - const data = await new Promise((res, rej) => { - readFile(testData, { encoding: "utf8" }, (err, data) => { - if (err) { - rej(err); - } - res(data); - }); - }); - assertEquals(typeof data, "string"); - assertEquals(data as string, "hello world"); -}); - -Deno.test("readFileEncodeHexSuccess", async function () { - const data = await new Promise((res, rej) => { - readFile(testData, { encoding: "hex" }, (err, data) => { - if (err) { - rej(err); - } - res(data); - }); - }); - - assertEquals(typeof data, "string"); - assertEquals(data as string, "68656c6c6f20776f726c64"); -}); - -Deno.test("readFileEncodeBase64Success", async function () { - const data = await new Promise((res, rej) => { - readFile(testData, { encoding: "base64" }, (err, data) => { - if (err) { - rej(err); - } - res(data); - }); - }); - assertEquals(typeof data, "string"); - assertEquals(data as string, "aGVsbG8gd29ybGQ="); -}); - -Deno.test("readFileEncodingAsString", async function () { - const data = await new Promise((res, rej) => { - readFile(testData, "utf8", (err, data) => { - if (err) { - rej(err); - } - res(data); - }); - }); - - assertEquals(typeof data, "string"); - assertEquals(data as string, "hello world"); -}); - -Deno.test("readFileSyncSuccess", function () { - const data = readFileSync(testData); - assert(data instanceof Uint8Array); - assertEquals(new TextDecoder().decode(data as Uint8Array), "hello world"); -}); - -Deno.test("readFileEncodeUtf8Success", function () { - const data = readFileSync(testData, { encoding: "utf8" }); - assertEquals(typeof data, "string"); - assertEquals(data as string, "hello world"); -}); - -Deno.test("readFileEncodeHexSuccess", function () { - const data = readFileSync(testData, { encoding: "hex" }); - assertEquals(typeof data, "string"); - assertEquals(data as string, "68656c6c6f20776f726c64"); -}); - -Deno.test("readFileEncodeBase64Success", function () { - const data = readFileSync(testData, { encoding: "base64" }); - assertEquals(typeof data, "string"); - assertEquals(data as string, "aGVsbG8gd29ybGQ="); -}); - -Deno.test("readFileEncodeAsString", function () { - const data = readFileSync(testData, "utf8"); - assertEquals(typeof data, "string"); - assertEquals(data as string, "hello world"); -}); - -Deno.test("[std/node/fs] readFile callback isn't called twice if error is thrown", async () => { - const tempFile = await Deno.makeTempFile(); - const importUrl = new URL("./_fs_readFile.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { readFile } from ${JSON.stringify(importUrl)}`, - invocation: `readFile(${JSON.stringify(tempFile)}, `, - async cleanup() { - await Deno.remove(tempFile); - }, - }); -}); diff --git a/std/node/_fs/_fs_readdir.ts b/std/node/_fs/_fs_readdir.ts deleted file mode 100644 index e36bf5ecf9..0000000000 --- a/std/node/_fs/_fs_readdir.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { asyncIterableToCallback } from "./_fs_watch.ts"; -import Dirent from "./_fs_dirent.ts"; -import { fromFileUrl } from "../path.ts"; - -function toDirent(val: Deno.DirEntry): Dirent { - return new Dirent(val); -} - -type readDirOptions = { - encoding?: string; - withFileTypes?: boolean; -}; - -type readDirCallback = (err: Error | null, files: string[]) => void; - -type readDirCallbackDirent = (err: Error | null, files: Dirent[]) => void; - -type readDirBoth = ( - ...args: [Error] | [null, string[] | Dirent[] | Array] -) => void; - -export function readdir( - path: string | URL, - options: { withFileTypes?: false; encoding?: string }, - callback: readDirCallback, -): void; -export function readdir( - path: string | URL, - options: { withFileTypes: true; encoding?: string }, - callback: readDirCallbackDirent, -): void; -export function readdir(path: string | URL, callback: readDirCallback): void; -export function readdir( - path: string | URL, - optionsOrCallback: readDirOptions | readDirCallback | readDirCallbackDirent, - maybeCallback?: readDirCallback | readDirCallbackDirent, -) { - const callback = - (typeof optionsOrCallback === "function" - ? optionsOrCallback - : maybeCallback) as readDirBoth | undefined; - const options = typeof optionsOrCallback === "object" - ? optionsOrCallback - : null; - const result: Array = []; - path = path instanceof URL ? fromFileUrl(path) : path; - - if (!callback) throw new Error("No callback function supplied"); - - if (options?.encoding) { - try { - new TextDecoder(options.encoding); - } catch (error) { - throw new Error( - `TypeError [ERR_INVALID_OPT_VALUE_ENCODING]: The value "${options.encoding}" is invalid for option "encoding"`, - ); - } - } - - try { - asyncIterableToCallback(Deno.readDir(path), (val, done) => { - if (typeof path !== "string") return; - if (done) { - callback(null, result); - return; - } - if (options?.withFileTypes) { - result.push(toDirent(val)); - } else result.push(decode(val.name)); - }); - } catch (error) { - callback(error); - } -} - -function decode(str: string, encoding?: string): string { - if (!encoding) return str; - else { - const decoder = new TextDecoder(encoding); - const encoder = new TextEncoder(); - return decoder.decode(encoder.encode(str)); - } -} - -export function readdirSync( - path: string | URL, - options: { withFileTypes: true; encoding?: string }, -): Dirent[]; -export function readdirSync( - path: string | URL, - options?: { withFileTypes?: false; encoding?: string }, -): string[]; -export function readdirSync( - path: string | URL, - options?: readDirOptions, -): Array { - const result = []; - path = path instanceof URL ? fromFileUrl(path) : path; - - if (options?.encoding) { - try { - new TextDecoder(options.encoding); - } catch (error) { - throw new Error( - `TypeError [ERR_INVALID_OPT_VALUE_ENCODING]: The value "${options.encoding}" is invalid for option "encoding"`, - ); - } - } - - for (const file of Deno.readDirSync(path)) { - if (options?.withFileTypes) { - result.push(toDirent(file)); - } else result.push(decode(file.name)); - } - return result; -} diff --git a/std/node/_fs/_fs_readdir_test.ts b/std/node/_fs/_fs_readdir_test.ts deleted file mode 100644 index 165cb81410..0000000000 --- a/std/node/_fs/_fs_readdir_test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { assertEquals, assertNotEquals, fail } from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { readdir, readdirSync } from "./_fs_readdir.ts"; -import { join } from "../../path/mod.ts"; - -Deno.test({ - name: "ASYNC: reading empty directory", - async fn() { - const dir = Deno.makeTempDirSync(); - await new Promise((resolve, reject) => { - readdir(dir, (err, files) => { - if (err) reject(err); - resolve(files); - }); - }) - .then((files) => assertEquals(files, []), () => fail()) - .finally(() => Deno.removeSync(dir)); - }, -}); - -function assertEqualsArrayAnyOrder(actual: T[], expected: T[]) { - assertEquals(actual.length, expected.length); - for (const item of expected) { - const index = actual.indexOf(item); - assertNotEquals(index, -1); - expected = expected.splice(index, 1); - } -} - -Deno.test({ - name: "ASYNC: reading non-empty directory", - async fn() { - const dir = Deno.makeTempDirSync(); - Deno.writeTextFileSync(join(dir, "file1.txt"), "hi"); - Deno.writeTextFileSync(join(dir, "file2.txt"), "hi"); - Deno.mkdirSync(join(dir, "some_dir")); - await new Promise((resolve, reject) => { - readdir(dir, (err, files) => { - if (err) reject(err); - resolve(files); - }); - }) - .then( - (files) => - assertEqualsArrayAnyOrder( - files, - ["file1.txt", "some_dir", "file2.txt"], - ), - () => fail(), - ) - .finally(() => Deno.removeSync(dir, { recursive: true })); - }, -}); - -Deno.test({ - name: "SYNC: reading empty the directory", - fn() { - const dir = Deno.makeTempDirSync(); - assertEquals(readdirSync(dir), []); - }, -}); - -Deno.test({ - name: "SYNC: reading non-empty directory", - fn() { - const dir = Deno.makeTempDirSync(); - Deno.writeTextFileSync(join(dir, "file1.txt"), "hi"); - Deno.writeTextFileSync(join(dir, "file2.txt"), "hi"); - Deno.mkdirSync(join(dir, "some_dir")); - assertEqualsArrayAnyOrder( - readdirSync(dir), - ["file1.txt", "some_dir", "file2.txt"], - ); - }, -}); - -Deno.test("[std/node/fs] readdir callback isn't called twice if error is thrown", async () => { - // The correct behaviour is not to catch any errors thrown, - // but that means there'll be an uncaught error and the test will fail. - // So the only way to test this is to spawn a subprocess, and succeed if it has a non-zero exit code. - // (assertThrowsAsync won't work because there's no way to catch the error.) - const tempDir = await Deno.makeTempDir(); - const importUrl = new URL("./_fs_readdir.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { readdir } from ${JSON.stringify(importUrl)}`, - invocation: `readdir(${JSON.stringify(tempDir)}, `, - async cleanup() { - await Deno.remove(tempDir); - }, - }); -}); diff --git a/std/node/_fs/_fs_readlink.ts b/std/node/_fs/_fs_readlink.ts deleted file mode 100644 index 37a7a108fe..0000000000 --- a/std/node/_fs/_fs_readlink.ts +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - intoCallbackAPIWithIntercept, - MaybeEmpty, - notImplemented, -} from "../_utils.ts"; -import { fromFileUrl } from "../path.ts"; - -type ReadlinkCallback = ( - err: MaybeEmpty, - linkString: MaybeEmpty, -) => void; - -interface ReadlinkOptions { - encoding?: string | null; -} - -function maybeEncode( - data: string, - encoding: string | null, -): string | Uint8Array { - if (encoding === "buffer") { - return new TextEncoder().encode(data); - } - return data; -} - -function getEncoding( - optOrCallback?: ReadlinkOptions | ReadlinkCallback, -): string | null { - if (!optOrCallback || typeof optOrCallback === "function") { - return null; - } else { - if (optOrCallback.encoding) { - if ( - optOrCallback.encoding === "utf8" || - optOrCallback.encoding === "utf-8" - ) { - return "utf8"; - } else if (optOrCallback.encoding === "buffer") { - return "buffer"; - } else { - notImplemented(); - } - } - return null; - } -} - -export function readlink( - path: string | URL, - optOrCallback: ReadlinkCallback | ReadlinkOptions, - callback?: ReadlinkCallback, -): void { - path = path instanceof URL ? fromFileUrl(path) : path; - - let cb: ReadlinkCallback | undefined; - if (typeof optOrCallback === "function") { - cb = optOrCallback; - } else { - cb = callback; - } - - const encoding = getEncoding(optOrCallback); - - intoCallbackAPIWithIntercept( - Deno.readLink, - (data: string): string | Uint8Array => maybeEncode(data, encoding), - cb, - path, - ); -} - -export function readlinkSync( - path: string | URL, - opt?: ReadlinkOptions, -): string | Uint8Array { - path = path instanceof URL ? fromFileUrl(path) : path; - - return maybeEncode(Deno.readLinkSync(path), getEncoding(opt)); -} diff --git a/std/node/_fs/_fs_readlink_test.ts b/std/node/_fs/_fs_readlink_test.ts deleted file mode 100644 index cb3dd25b42..0000000000 --- a/std/node/_fs/_fs_readlink_test.ts +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { readlink, readlinkSync } from "./_fs_readlink.ts"; -import { assert, assertEquals } from "../../testing/asserts.ts"; -import * as path from "../path.ts"; - -const testDir = Deno.makeTempDirSync(); -const oldname = path.join(testDir, "oldname"); -const newname = path.join(testDir, "newname"); - -if (Deno.build.os === "windows") { - Deno.symlinkSync(oldname, newname, { type: "file" }); -} else { - Deno.symlinkSync(oldname, newname); -} - -Deno.test({ - name: "readlinkSuccess", - async fn() { - const data = await new Promise((res, rej) => { - readlink(newname, (err, data) => { - if (err) { - rej(err); - } - res(data); - }); - }); - - assertEquals(typeof data, "string"); - assertEquals(data as string, oldname); - }, -}); - -Deno.test({ - name: "readlinkEncodeBufferSuccess", - async fn() { - const data = await new Promise((res, rej) => { - readlink(newname, { encoding: "buffer" }, (err, data) => { - if (err) { - rej(err); - } - res(data); - }); - }); - - assert(data instanceof Uint8Array); - assertEquals(new TextDecoder().decode(data as Uint8Array), oldname); - }, -}); - -Deno.test({ - name: "readlinkSyncSuccess", - fn() { - const data = readlinkSync(newname); - assertEquals(typeof data, "string"); - assertEquals(data as string, oldname); - }, -}); - -Deno.test({ - name: "readlinkEncodeBufferSuccess", - fn() { - const data = readlinkSync(newname, { encoding: "buffer" }); - assert(data instanceof Uint8Array); - assertEquals(new TextDecoder().decode(data as Uint8Array), oldname); - }, -}); - -Deno.test("[std/node/fs] readlink callback isn't called twice if error is thrown", async () => { - const importUrl = new URL("./_fs_readlink.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { readlink } from ${JSON.stringify(importUrl)}`, - invocation: `readlink(${JSON.stringify(newname)}, `, - }); -}); diff --git a/std/node/_fs/_fs_realpath.ts b/std/node/_fs/_fs_realpath.ts deleted file mode 100644 index 95e699d655..0000000000 --- a/std/node/_fs/_fs_realpath.ts +++ /dev/null @@ -1,23 +0,0 @@ -type Options = { encoding: string }; -type Callback = (err: Error | null, path?: string) => void; - -export function realpath( - path: string, - options?: Options | Callback, - callback?: Callback, -) { - if (typeof options === "function") { - callback = options; - } - if (!callback) { - throw new Error("No callback function supplied"); - } - Deno.realPath(path).then( - (path) => callback!(null, path), - (err) => callback!(err), - ); -} - -export function realpathSync(path: string): string { - return Deno.realPathSync(path); -} diff --git a/std/node/_fs/_fs_realpath_test.ts b/std/node/_fs/_fs_realpath_test.ts deleted file mode 100644 index 08eb3ef160..0000000000 --- a/std/node/_fs/_fs_realpath_test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as path from "../../path/mod.ts"; -import { assertEquals } from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { realpath, realpathSync } from "./_fs_realpath.ts"; - -Deno.test("realpath", async function () { - const tempFile = await Deno.makeTempFile(); - const tempFileAlias = tempFile + ".alias"; - await Deno.symlink(tempFile, tempFileAlias); - const realPath = await new Promise((resolve, reject) => { - realpath(tempFile, (err, path) => { - if (err) { - reject(err); - return; - } - resolve(path); - }); - }); - const realSymLinkPath = await new Promise((resolve, reject) => { - realpath(tempFileAlias, (err, path) => { - if (err) { - reject(err); - return; - } - resolve(path); - }); - }); - assertEquals(realPath, realSymLinkPath); -}); - -Deno.test("realpathSync", function () { - const tempFile = Deno.makeTempFileSync(); - const tempFileAlias = tempFile + ".alias"; - Deno.symlinkSync(tempFile, tempFileAlias); - const realPath = realpathSync(tempFile); - const realSymLinkPath = realpathSync(tempFileAlias); - assertEquals(realPath, realSymLinkPath); -}); - -Deno.test("[std/node/fs] realpath callback isn't called twice if error is thrown", async () => { - const tempDir = await Deno.makeTempDir(); - const tempFile = path.join(tempDir, "file.txt"); - const linkFile = path.join(tempDir, "link.txt"); - await Deno.writeTextFile(tempFile, "hello world"); - await Deno.symlink(tempFile, linkFile, { type: "file" }); - const importUrl = new URL("./_fs_realpath.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { realpath } from ${JSON.stringify(importUrl)}`, - invocation: `realpath(${JSON.stringify(`${tempDir}/link.txt`)}, `, - async cleanup() { - await Deno.remove(tempDir, { recursive: true }); - }, - }); -}); diff --git a/std/node/_fs/_fs_rename.ts b/std/node/_fs/_fs_rename.ts deleted file mode 100644 index b121eacf9c..0000000000 --- a/std/node/_fs/_fs_rename.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { fromFileUrl } from "../path.ts"; - -export function rename( - oldPath: string | URL, - newPath: string | URL, - callback: (err?: Error) => void, -) { - oldPath = oldPath instanceof URL ? fromFileUrl(oldPath) : oldPath; - newPath = newPath instanceof URL ? fromFileUrl(newPath) : newPath; - - if (!callback) throw new Error("No callback function supplied"); - - Deno.rename(oldPath, newPath).then((_) => callback(), callback); -} - -export function renameSync(oldPath: string | URL, newPath: string | URL) { - oldPath = oldPath instanceof URL ? fromFileUrl(oldPath) : oldPath; - newPath = newPath instanceof URL ? fromFileUrl(newPath) : newPath; - - Deno.renameSync(oldPath, newPath); -} diff --git a/std/node/_fs/_fs_rename_test.ts b/std/node/_fs/_fs_rename_test.ts deleted file mode 100644 index e35e5282e7..0000000000 --- a/std/node/_fs/_fs_rename_test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { assertEquals, fail } from "../../testing/asserts.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { rename, renameSync } from "./_fs_rename.ts"; -import { existsSync } from "../../fs/mod.ts"; -import { join, parse } from "../../path/mod.ts"; - -Deno.test({ - name: "ASYNC: renaming a file", - async fn() { - const file = Deno.makeTempFileSync(); - const newPath = join(parse(file).dir, `${parse(file).base}_renamed`); - await new Promise((resolve, reject) => { - rename(file, newPath, (err) => { - if (err) reject(err); - resolve(); - }); - }) - .then(() => { - assertEquals(existsSync(newPath), true); - assertEquals(existsSync(file), false); - }, () => fail()) - .finally(() => { - if (existsSync(file)) Deno.removeSync(file); - if (existsSync(newPath)) Deno.removeSync(newPath); - }); - }, -}); - -Deno.test({ - name: "SYNC: renaming a file", - fn() { - const file = Deno.makeTempFileSync(); - const newPath = join(parse(file).dir, `${parse(file).base}_renamed`); - renameSync(file, newPath); - assertEquals(existsSync(newPath), true); - assertEquals(existsSync(file), false); - }, -}); - -Deno.test("[std/node/fs] rename callback isn't called twice if error is thrown", async () => { - const tempFile = await Deno.makeTempFile(); - const importUrl = new URL("./_fs_rename.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { rename } from ${JSON.stringify(importUrl)}`, - invocation: `rename(${JSON.stringify(tempFile)}, - ${JSON.stringify(`${tempFile}.newname`)}, `, - async cleanup() { - await Deno.remove(`${tempFile}.newname`); - }, - }); -}); diff --git a/std/node/_fs/_fs_rmdir.ts b/std/node/_fs/_fs_rmdir.ts deleted file mode 100644 index e82e696e3d..0000000000 --- a/std/node/_fs/_fs_rmdir.ts +++ /dev/null @@ -1,35 +0,0 @@ -type rmdirOptions = { - maxRetries?: number; - recursive?: boolean; - retryDelay?: number; -}; - -type rmdirCallback = (err?: Error) => void; - -export function rmdir(path: string | URL, callback: rmdirCallback): void; -export function rmdir( - path: string | URL, - options: rmdirOptions, - callback: rmdirCallback, -): void; -export function rmdir( - path: string | URL, - optionsOrCallback: rmdirOptions | rmdirCallback, - maybeCallback?: rmdirCallback, -) { - const callback = typeof optionsOrCallback === "function" - ? optionsOrCallback - : maybeCallback; - const options = typeof optionsOrCallback === "object" - ? optionsOrCallback - : undefined; - - if (!callback) throw new Error("No callback function supplied"); - - Deno.remove(path, { recursive: options?.recursive }) - .then((_) => callback(), callback); -} - -export function rmdirSync(path: string | URL, options?: rmdirOptions) { - Deno.removeSync(path, { recursive: options?.recursive }); -} diff --git a/std/node/_fs/_fs_rmdir_test.ts b/std/node/_fs/_fs_rmdir_test.ts deleted file mode 100644 index 6f9c332747..0000000000 --- a/std/node/_fs/_fs_rmdir_test.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { assertEquals, fail } from "../../testing/asserts.ts"; -import { rmdir, rmdirSync } from "./_fs_rmdir.ts"; -import { closeSync } from "./_fs_close.ts"; -import { existsSync } from "../../fs/mod.ts"; -import { join } from "../../path/mod.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; - -Deno.test({ - name: "ASYNC: removing empty folder", - async fn() { - const dir = Deno.makeTempDirSync(); - await new Promise((resolve, reject) => { - rmdir(dir, (err) => { - if (err) reject(err); - resolve(); - }); - }) - .then(() => assertEquals(existsSync(dir), false), () => fail()) - .finally(() => { - if (existsSync(dir)) Deno.removeSync(dir); - }); - }, -}); - -Deno.test({ - name: "SYNC: removing empty folder", - fn() { - const dir = Deno.makeTempDirSync(); - rmdirSync(dir); - assertEquals(existsSync(dir), false); - }, -}); - -function closeRes(before: Deno.ResourceMap, after: Deno.ResourceMap) { - for (const key in after) { - if (!before[key]) { - try { - closeSync(Number(key)); - } catch (error) { - return error; - } - } - } -} - -Deno.test({ - name: "ASYNC: removing non-empty folder", - async fn() { - const rBefore = Deno.resources(); - const dir = Deno.makeTempDirSync(); - Deno.createSync(join(dir, "file1.txt")); - Deno.createSync(join(dir, "file2.txt")); - Deno.mkdirSync(join(dir, "some_dir")); - Deno.createSync(join(dir, "some_dir", "file.txt")); - await new Promise((resolve, reject) => { - rmdir(dir, { recursive: true }, (err) => { - if (err) reject(err); - resolve(); - }); - }) - .then(() => assertEquals(existsSync(dir), false), () => fail()) - .finally(() => { - if (existsSync(dir)) Deno.removeSync(dir, { recursive: true }); - const rAfter = Deno.resources(); - closeRes(rBefore, rAfter); - }); - }, - ignore: Deno.build.os === "windows", -}); - -Deno.test({ - name: "SYNC: removing non-empty folder", - fn() { - const rBefore = Deno.resources(); - const dir = Deno.makeTempDirSync(); - Deno.createSync(join(dir, "file1.txt")); - Deno.createSync(join(dir, "file2.txt")); - Deno.mkdirSync(join(dir, "some_dir")); - Deno.createSync(join(dir, "some_dir", "file.txt")); - rmdirSync(dir, { recursive: true }); - assertEquals(existsSync(dir), false); - // closing resources - const rAfter = Deno.resources(); - closeRes(rBefore, rAfter); - }, - ignore: Deno.build.os === "windows", -}); - -Deno.test("[std/node/fs] rmdir callback isn't called twice if error is thrown", async () => { - // The correct behaviour is not to catch any errors thrown, - // but that means there'll be an uncaught error and the test will fail. - // So the only way to test this is to spawn a subprocess, and succeed if it has a non-zero exit code. - // (assertThrowsAsync won't work because there's no way to catch the error.) - const tempDir = await Deno.makeTempDir(); - const importUrl = new URL("./_fs_rmdir.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { rmdir } from ${JSON.stringify(importUrl)}`, - invocation: `rmdir(${JSON.stringify(tempDir)}, `, - }); -}); diff --git a/std/node/_fs/_fs_stat.ts b/std/node/_fs/_fs_stat.ts deleted file mode 100644 index 4f7903d16e..0000000000 --- a/std/node/_fs/_fs_stat.ts +++ /dev/null @@ -1,289 +0,0 @@ -export type statOptions = { - bigint: boolean; -}; - -export type Stats = { - /** ID of the device containing the file. - * - * _Linux/Mac OS only._ */ - dev: number | null; - /** Inode number. - * - * _Linux/Mac OS only._ */ - ino: number | null; - /** **UNSTABLE**: Match behavior with Go on Windows for `mode`. - * - * The underlying raw `st_mode` bits that contain the standard Unix - * permissions for this file/directory. */ - mode: number | null; - /** Number of hard links pointing to this file. - * - * _Linux/Mac OS only._ */ - nlink: number | null; - /** User ID of the owner of this file. - * - * _Linux/Mac OS only._ */ - uid: number | null; - /** Group ID of the owner of this file. - * - * _Linux/Mac OS only._ */ - gid: number | null; - /** Device ID of this file. - * - * _Linux/Mac OS only._ */ - rdev: number | null; - /** The size of the file, in bytes. */ - size: number; - /** Blocksize for filesystem I/O. - * - * _Linux/Mac OS only._ */ - blksize: number | null; - /** Number of blocks allocated to the file, in 512-byte units. - * - * _Linux/Mac OS only._ */ - blocks: number | null; - /** The last modification time of the file. This corresponds to the `mtime` - * field from `stat` on Linux/Mac OS and `ftLastWriteTime` on Windows. This - * may not be available on all platforms. */ - mtime: Date | null; - /** The last access time of the file. This corresponds to the `atime` - * field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not - * be available on all platforms. */ - atime: Date | null; - /** The creation time of the file. This corresponds to the `birthtime` - * field from `stat` on Mac/BSD and `ftCreationTime` on Windows. This may - * not be available on all platforms. */ - birthtime: Date | null; - /** change time */ - ctime: Date | null; - /** atime in milliseconds */ - atimeMs: number | null; - /** atime in milliseconds */ - mtimeMs: number | null; - /** atime in milliseconds */ - ctimeMs: number | null; - /** atime in milliseconds */ - birthtimeMs: number | null; - isBlockDevice: () => boolean; - isCharacterDevice: () => boolean; - isDirectory: () => boolean; - isFIFO: () => boolean; - isFile: () => boolean; - isSocket: () => boolean; - isSymbolicLink: () => boolean; -}; - -export type BigIntStats = { - /** ID of the device containing the file. - * - * _Linux/Mac OS only._ */ - dev: BigInt | null; - /** Inode number. - * - * _Linux/Mac OS only._ */ - ino: BigInt | null; - /** **UNSTABLE**: Match behavior with Go on Windows for `mode`. - * - * The underlying raw `st_mode` bits that contain the standard Unix - * permissions for this file/directory. */ - mode: BigInt | null; - /** Number of hard links pointing to this file. - * - * _Linux/Mac OS only._ */ - nlink: BigInt | null; - /** User ID of the owner of this file. - * - * _Linux/Mac OS only._ */ - uid: BigInt | null; - /** Group ID of the owner of this file. - * - * _Linux/Mac OS only._ */ - gid: BigInt | null; - /** Device ID of this file. - * - * _Linux/Mac OS only._ */ - rdev: BigInt | null; - /** The size of the file, in bytes. */ - size: BigInt; - /** Blocksize for filesystem I/O. - * - * _Linux/Mac OS only._ */ - blksize: BigInt | null; - /** Number of blocks allocated to the file, in 512-byte units. - * - * _Linux/Mac OS only._ */ - blocks: BigInt | null; - /** The last modification time of the file. This corresponds to the `mtime` - * field from `stat` on Linux/Mac OS and `ftLastWriteTime` on Windows. This - * may not be available on all platforms. */ - mtime: Date | null; - /** The last access time of the file. This corresponds to the `atime` - * field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not - * be available on all platforms. */ - atime: Date | null; - /** The creation time of the file. This corresponds to the `birthtime` - * field from `stat` on Mac/BSD and `ftCreationTime` on Windows. This may - * not be available on all platforms. */ - birthtime: Date | null; - /** change time */ - ctime: Date | null; - /** atime in milliseconds */ - atimeMs: BigInt | null; - /** atime in milliseconds */ - mtimeMs: BigInt | null; - /** atime in milliseconds */ - ctimeMs: BigInt | null; - /** atime in nanoseconds */ - birthtimeMs: BigInt | null; - /** atime in nanoseconds */ - atimeNs: BigInt | null; - /** atime in nanoseconds */ - mtimeNs: BigInt | null; - /** atime in nanoseconds */ - ctimeNs: BigInt | null; - /** atime in nanoseconds */ - birthtimeNs: BigInt | null; - isBlockDevice: () => boolean; - isCharacterDevice: () => boolean; - isDirectory: () => boolean; - isFIFO: () => boolean; - isFile: () => boolean; - isSocket: () => boolean; - isSymbolicLink: () => boolean; -}; - -export function convertFileInfoToStats(origin: Deno.FileInfo): Stats { - return { - dev: origin.dev, - ino: origin.ino, - mode: origin.mode, - nlink: origin.nlink, - uid: origin.uid, - gid: origin.gid, - rdev: origin.rdev, - size: origin.size, - blksize: origin.blksize, - blocks: origin.blocks, - mtime: origin.mtime, - atime: origin.atime, - birthtime: origin.birthtime, - mtimeMs: origin.mtime?.getTime() || null, - atimeMs: origin.atime?.getTime() || null, - birthtimeMs: origin.birthtime?.getTime() || null, - isFile: () => origin.isFile, - isDirectory: () => origin.isDirectory, - isSymbolicLink: () => origin.isSymlink, - // not sure about those - isBlockDevice: () => false, - isFIFO: () => false, - isCharacterDevice: () => false, - isSocket: () => false, - ctime: origin.mtime, - ctimeMs: origin.mtime?.getTime() || null, - }; -} - -function toBigInt(number?: number | null) { - if (number === null || number === undefined) return null; - return BigInt(number); -} - -export function convertFileInfoToBigIntStats( - origin: Deno.FileInfo, -): BigIntStats { - return { - dev: toBigInt(origin.dev), - ino: toBigInt(origin.ino), - mode: toBigInt(origin.mode), - nlink: toBigInt(origin.nlink), - uid: toBigInt(origin.uid), - gid: toBigInt(origin.gid), - rdev: toBigInt(origin.rdev), - size: toBigInt(origin.size) || 0n, - blksize: toBigInt(origin.blksize), - blocks: toBigInt(origin.blocks), - mtime: origin.mtime, - atime: origin.atime, - birthtime: origin.birthtime, - mtimeMs: origin.mtime ? BigInt(origin.mtime.getTime()) : null, - atimeMs: origin.atime ? BigInt(origin.atime.getTime()) : null, - birthtimeMs: origin.birthtime ? BigInt(origin.birthtime.getTime()) : null, - mtimeNs: origin.mtime ? BigInt(origin.mtime.getTime()) * 1000000n : null, - atimeNs: origin.atime ? BigInt(origin.atime.getTime()) * 1000000n : null, - birthtimeNs: origin.birthtime - ? BigInt(origin.birthtime.getTime()) * 1000000n - : null, - isFile: () => origin.isFile, - isDirectory: () => origin.isDirectory, - isSymbolicLink: () => origin.isSymlink, - // not sure about those - isBlockDevice: () => false, - isFIFO: () => false, - isCharacterDevice: () => false, - isSocket: () => false, - ctime: origin.mtime, - ctimeMs: origin.mtime ? BigInt(origin.mtime.getTime()) : null, - ctimeNs: origin.mtime ? BigInt(origin.mtime.getTime()) * 1000000n : null, - }; -} - -// shortcut for Convert File Info to Stats or BigIntStats -export function CFISBIS(fileInfo: Deno.FileInfo, bigInt: boolean) { - if (bigInt) return convertFileInfoToBigIntStats(fileInfo); - return convertFileInfoToStats(fileInfo); -} - -export type statCallbackBigInt = ( - err: Error | null, - stat: BigIntStats, -) => void; - -export type statCallback = (err: Error | null, stat: Stats) => void; - -export function stat(path: string | URL, callback: statCallback): void; -export function stat( - path: string | URL, - options: { bigint: false }, - callback: statCallback, -): void; -export function stat( - path: string | URL, - options: { bigint: true }, - callback: statCallbackBigInt, -): void; -export function stat( - path: string | URL, - optionsOrCallback: statCallback | statCallbackBigInt | statOptions, - maybeCallback?: statCallback | statCallbackBigInt, -) { - const callback = - (typeof optionsOrCallback === "function" - ? optionsOrCallback - : maybeCallback) as ( - ...args: [Error] | [null, BigIntStats | Stats] - ) => void; - const options = typeof optionsOrCallback === "object" - ? optionsOrCallback - : { bigint: false }; - - if (!callback) throw new Error("No callback function supplied"); - - Deno.stat(path).then( - (stat) => callback(null, CFISBIS(stat, options.bigint)), - (err) => callback(err), - ); -} - -export function statSync(path: string | URL): Stats; -export function statSync(path: string | URL, options: { bigint: false }): Stats; -export function statSync( - path: string | URL, - options: { bigint: true }, -): BigIntStats; -export function statSync( - path: string | URL, - options: statOptions = { bigint: false }, -): Stats | BigIntStats { - const origin = Deno.statSync(path); - return CFISBIS(origin, options.bigint); -} diff --git a/std/node/_fs/_fs_stat_test.ts b/std/node/_fs/_fs_stat_test.ts deleted file mode 100644 index 5a25cddcc8..0000000000 --- a/std/node/_fs/_fs_stat_test.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { BigIntStats, stat, Stats, statSync } from "./_fs_stat.ts"; -import { assertEquals, fail } from "../../testing/asserts.ts"; - -export function assertStats(actual: Stats, expected: Deno.FileInfo) { - assertEquals(actual.dev, expected.dev); - assertEquals(actual.gid, expected.gid); - assertEquals(actual.size, expected.size); - assertEquals(actual.blksize, expected.blksize); - assertEquals(actual.blocks, expected.blocks); - assertEquals(actual.ino, expected.ino); - assertEquals(actual.gid, expected.gid); - assertEquals(actual.mode, expected.mode); - assertEquals(actual.nlink, expected.nlink); - assertEquals(actual.rdev, expected.rdev); - assertEquals(actual.uid, expected.uid); - assertEquals(actual.atime?.getTime(), expected.atime?.getTime()); - assertEquals(actual.mtime?.getTime(), expected.mtime?.getTime()); - assertEquals(actual.birthtime?.getTime(), expected.birthtime?.getTime()); - assertEquals(actual.atimeMs ?? undefined, expected.atime?.getTime()); - assertEquals(actual.mtimeMs ?? undefined, expected.mtime?.getTime()); - assertEquals(actual.birthtimeMs ?? undefined, expected.birthtime?.getTime()); - assertEquals(actual.isFile(), expected.isFile); - assertEquals(actual.isDirectory(), expected.isDirectory); - assertEquals(actual.isSymbolicLink(), expected.isSymlink); -} - -function toBigInt(num?: number | null) { - if (num === undefined || num === null) return null; - return BigInt(num); -} - -export function assertStatsBigInt( - actual: BigIntStats, - expected: Deno.FileInfo, -) { - assertEquals(actual.dev, toBigInt(expected.dev)); - assertEquals(actual.gid, toBigInt(expected.gid)); - assertEquals(actual.size, toBigInt(expected.size)); - assertEquals(actual.blksize, toBigInt(expected.blksize)); - assertEquals(actual.blocks, toBigInt(expected.blocks)); - assertEquals(actual.ino, toBigInt(expected.ino)); - assertEquals(actual.gid, toBigInt(expected.gid)); - assertEquals(actual.mode, toBigInt(expected.mode)); - assertEquals(actual.nlink, toBigInt(expected.nlink)); - assertEquals(actual.rdev, toBigInt(expected.rdev)); - assertEquals(actual.uid, toBigInt(expected.uid)); - assertEquals(actual.atime?.getTime(), expected.atime?.getTime()); - assertEquals(actual.mtime?.getTime(), expected.mtime?.getTime()); - assertEquals(actual.birthtime?.getTime(), expected.birthtime?.getTime()); - assertEquals( - actual.atimeMs === null ? undefined : Number(actual.atimeMs), - expected.atime?.getTime(), - ); - assertEquals( - actual.mtimeMs === null ? undefined : Number(actual.mtimeMs), - expected.mtime?.getTime(), - ); - assertEquals( - actual.birthtimeMs === null ? undefined : Number(actual.birthtimeMs), - expected.birthtime?.getTime(), - ); - assertEquals(actual.atimeNs === null, actual.atime === null); - assertEquals(actual.mtimeNs === null, actual.mtime === null); - assertEquals(actual.birthtimeNs === null, actual.birthtime === null); - assertEquals(actual.isFile(), expected.isFile); - assertEquals(actual.isDirectory(), expected.isDirectory); - assertEquals(actual.isSymbolicLink(), expected.isSymlink); -} - -Deno.test({ - name: "ASYNC: get a file Stats", - async fn() { - const file = Deno.makeTempFileSync(); - await new Promise((resolve, reject) => { - stat(file, (err, stat) => { - if (err) reject(err); - resolve(stat); - }); - }) - .then((stat) => assertStats(stat, Deno.statSync(file)), () => fail()) - .finally(() => Deno.removeSync(file)); - }, -}); - -Deno.test({ - name: "SYNC: get a file Stats", - fn() { - const file = Deno.makeTempFileSync(); - assertStats(statSync(file), Deno.statSync(file)); - }, -}); - -Deno.test({ - name: "ASYNC: get a file BigInt Stats", - async fn() { - const file = Deno.makeTempFileSync(); - await new Promise((resolve, reject) => { - stat(file, { bigint: true }, (err, stat) => { - if (err) reject(err); - resolve(stat); - }); - }) - .then( - (stat) => assertStatsBigInt(stat, Deno.statSync(file)), - () => fail(), - ) - .finally(() => Deno.removeSync(file)); - }, -}); - -Deno.test({ - name: "SYNC: get a file BigInt Stats", - fn() { - const file = Deno.makeTempFileSync(); - assertStatsBigInt(statSync(file, { bigint: true }), Deno.statSync(file)); - }, -}); - -Deno.test("[std/node/fs] stat callback isn't called twice if error is thrown", async () => { - const tempFile = await Deno.makeTempFile(); - const importUrl = new URL("./_fs_stat.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { stat } from ${JSON.stringify(importUrl)}`, - invocation: `stat(${JSON.stringify(tempFile)}, `, - async cleanup() { - await Deno.remove(tempFile); - }, - }); -}); diff --git a/std/node/_fs/_fs_unlink.ts b/std/node/_fs/_fs_unlink.ts deleted file mode 100644 index 7349bee46e..0000000000 --- a/std/node/_fs/_fs_unlink.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function unlink(path: string | URL, callback: (err?: Error) => void) { - if (!callback) throw new Error("No callback function supplied"); - Deno.remove(path).then((_) => callback(), callback); -} - -export function unlinkSync(path: string | URL) { - Deno.removeSync(path); -} diff --git a/std/node/_fs/_fs_unlink_test.ts b/std/node/_fs/_fs_unlink_test.ts deleted file mode 100644 index 5021b1c380..0000000000 --- a/std/node/_fs/_fs_unlink_test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { assertEquals, fail } from "../../testing/asserts.ts"; -import { existsSync } from "../../fs/mod.ts"; -import { assertCallbackErrorUncaught } from "../_utils.ts"; -import { unlink, unlinkSync } from "./_fs_unlink.ts"; - -Deno.test({ - name: "ASYNC: deleting a file", - async fn() { - const file = Deno.makeTempFileSync(); - await new Promise((resolve, reject) => { - unlink(file, (err) => { - if (err) reject(err); - resolve(); - }); - }) - .then(() => assertEquals(existsSync(file), false), () => fail()) - .finally(() => { - if (existsSync(file)) Deno.removeSync(file); - }); - }, -}); - -Deno.test({ - name: "SYNC: Test deleting a file", - fn() { - const file = Deno.makeTempFileSync(); - unlinkSync(file); - assertEquals(existsSync(file), false); - }, -}); - -Deno.test("[std/node/fs] unlink callback isn't called twice if error is thrown", async () => { - const tempFile = await Deno.makeTempFile(); - const importUrl = new URL("./_fs_unlink.ts", import.meta.url); - await assertCallbackErrorUncaught({ - prelude: `import { unlink } from ${JSON.stringify(importUrl)}`, - invocation: `unlink(${JSON.stringify(tempFile)}, `, - }); -}); diff --git a/std/node/_fs/_fs_watch.ts b/std/node/_fs/_fs_watch.ts deleted file mode 100644 index a5f3bb9c17..0000000000 --- a/std/node/_fs/_fs_watch.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { fromFileUrl } from "../path.ts"; -import { EventEmitter } from "../events.ts"; -import { notImplemented } from "../_utils.ts"; - -export function asyncIterableIteratorToCallback( - iterator: AsyncIterableIterator, - callback: (val: T, done?: boolean) => void, -) { - function next() { - iterator.next().then((obj) => { - if (obj.done) { - callback(obj.value, true); - return; - } - callback(obj.value); - next(); - }); - } - next(); -} - -export function asyncIterableToCallback( - iter: AsyncIterable, - callback: (val: T, done?: boolean) => void, -) { - const iterator = iter[Symbol.asyncIterator](); - function next() { - iterator.next().then((obj) => { - if (obj.done) { - callback(obj.value, true); - return; - } - callback(obj.value); - next(); - }); - } - next(); -} - -type watchOptions = { - persistent?: boolean; - recursive?: boolean; - encoding?: string; -}; - -type watchListener = (eventType: string, filename: string) => void; - -export function watch( - filename: string | URL, - options: watchOptions, - listener: watchListener, -): FSWatcher; -export function watch( - filename: string | URL, - listener: watchListener, -): FSWatcher; -export function watch( - filename: string | URL, - options: watchOptions, -): FSWatcher; -export function watch(filename: string | URL): FSWatcher; -export function watch( - filename: string | URL, - optionsOrListener?: watchOptions | watchListener, - optionsOrListener2?: watchOptions | watchListener, -) { - const listener = typeof optionsOrListener === "function" - ? optionsOrListener - : typeof optionsOrListener2 === "function" - ? optionsOrListener2 - : undefined; - const options = typeof optionsOrListener === "object" - ? optionsOrListener - : typeof optionsOrListener2 === "object" - ? optionsOrListener2 - : undefined; - filename = filename instanceof URL ? fromFileUrl(filename) : filename; - - const iterator = Deno.watchFs(filename, { - recursive: options?.recursive || false, - }); - - if (!listener) throw new Error("No callback function supplied"); - - const fsWatcher = new FSWatcher(() => { - if (iterator.return) iterator.return(); - }); - - fsWatcher.on("change", listener); - - asyncIterableIteratorToCallback(iterator, (val, done) => { - if (done) return; - fsWatcher.emit("change", val.kind, val.paths[0]); - }); - - return fsWatcher; -} - -class FSWatcher extends EventEmitter { - close: () => void; - constructor(closer: () => void) { - super(); - this.close = closer; - } - ref() { - notImplemented("FSWatcher.ref() is not implemented"); - } - unref() { - notImplemented("FSWatcher.unref() is not implemented"); - } -} diff --git a/std/node/_fs/_fs_watch_test.ts b/std/node/_fs/_fs_watch_test.ts deleted file mode 100644 index 00fce4ffdc..0000000000 --- a/std/node/_fs/_fs_watch_test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { watch } from "./_fs_watch.ts"; -import { assertEquals, fail } from "../../testing/asserts.ts"; - -function wait(time: number) { - return new Promise((resolve) => { - setTimeout(resolve, time); - }); -} - -Deno.test({ - name: "watching a file", - async fn() { - const file = Deno.makeTempFileSync(); - const result: Array<[string, string]> = []; - const watcher = watch( - file, - (eventType, filename) => result.push([eventType, filename]), - ); - await wait(100); - Deno.writeTextFileSync(file, "something"); - await wait(100); - watcher.close(); - await wait(100); - assertEquals(result.length >= 1, true); - }, -}); diff --git a/std/node/_fs/_fs_writeFile.ts b/std/node/_fs/_fs_writeFile.ts deleted file mode 100644 index e68bd88843..0000000000 --- a/std/node/_fs/_fs_writeFile.ts +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { Encodings, notImplemented } from "../_utils.ts"; -import { fromFileUrl } from "../path.ts"; -import { Buffer } from "../buffer.ts"; -import { - CallbackWithError, - checkEncoding, - getEncoding, - getOpenOptions, - isFileOptions, - WriteFileOptions, -} from "./_fs_common.ts"; - -export function writeFile( - pathOrRid: string | number | URL, - data: string | Uint8Array, - optOrCallback: Encodings | CallbackWithError | WriteFileOptions | undefined, - callback?: CallbackWithError, -): void { - const callbackFn: CallbackWithError | undefined = - optOrCallback instanceof Function ? optOrCallback : callback; - const options: Encodings | WriteFileOptions | undefined = - optOrCallback instanceof Function ? undefined : optOrCallback; - - if (!callbackFn) { - throw new TypeError("Callback must be a function."); - } - - pathOrRid = pathOrRid instanceof URL ? fromFileUrl(pathOrRid) : pathOrRid; - - const flag: string | undefined = isFileOptions(options) - ? options.flag - : undefined; - - const mode: number | undefined = isFileOptions(options) - ? options.mode - : undefined; - - const encoding = checkEncoding(getEncoding(options)) || "utf8"; - const openOptions = getOpenOptions(flag || "w"); - - if (typeof data === "string") data = Buffer.from(data, encoding); - - const isRid = typeof pathOrRid === "number"; - let file; - - let error: Error | null = null; - (async (): Promise => { - try { - file = isRid - ? new Deno.File(pathOrRid as number) - : await Deno.open(pathOrRid as string, openOptions); - - if (!isRid && mode) { - if (Deno.build.os === "windows") notImplemented(`"mode" on Windows`); - await Deno.chmod(pathOrRid as string, mode); - } - - await Deno.writeAll(file, data as Uint8Array); - } catch (e) { - error = e; - } finally { - // Make sure to close resource - if (!isRid && file) file.close(); - callbackFn(error); - } - })(); -} - -export function writeFileSync( - pathOrRid: string | number | URL, - data: string | Uint8Array, - options?: Encodings | WriteFileOptions, -): void { - pathOrRid = pathOrRid instanceof URL ? fromFileUrl(pathOrRid) : pathOrRid; - - const flag: string | undefined = isFileOptions(options) - ? options.flag - : undefined; - - const mode: number | undefined = isFileOptions(options) - ? options.mode - : undefined; - - const encoding = checkEncoding(getEncoding(options)) || "utf8"; - const openOptions = getOpenOptions(flag || "w"); - - if (typeof data === "string") data = Buffer.from(data, encoding); - - const isRid = typeof pathOrRid === "number"; - let file; - - let error: Error | null = null; - try { - file = isRid - ? new Deno.File(pathOrRid as number) - : Deno.openSync(pathOrRid as string, openOptions); - - if (!isRid && mode) { - if (Deno.build.os === "windows") notImplemented(`"mode" on Windows`); - Deno.chmodSync(pathOrRid as string, mode); - } - - Deno.writeAllSync(file, data as Uint8Array); - } catch (e) { - error = e; - } finally { - // Make sure to close resource - if (!isRid && file) file.close(); - - if (error) throw error; - } -} diff --git a/std/node/_fs/_fs_writeFile_test.ts b/std/node/_fs/_fs_writeFile_test.ts deleted file mode 100644 index 561f71f88a..0000000000 --- a/std/node/_fs/_fs_writeFile_test.ts +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertNotEquals, - assertThrows, -} from "../../testing/asserts.ts"; -import { writeFile, writeFileSync } from "./_fs_writeFile.ts"; -import type { TextEncodings } from "../_utils.ts"; -import * as path from "../../path/mod.ts"; - -const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); -const testDataDir = path.resolve(moduleDir, "testdata"); -const decoder = new TextDecoder("utf-8"); - -Deno.test("Callback must be a function error", function fn() { - assertThrows( - () => { - writeFile("some/path", "some data", "utf8"); - }, - TypeError, - "Callback must be a function.", - ); -}); - -Deno.test("Invalid encoding results in error()", function testEncodingErrors() { - assertThrows( - () => { - // @ts-expect-error Type '"made-up-encoding"' is not assignable to type - writeFile("some/path", "some data", "made-up-encoding", () => {}); - }, - Error, - `The value "made-up-encoding" is invalid for option "encoding"`, - ); - - assertThrows( - () => { - // @ts-expect-error Type '"made-up-encoding"' is not assignable to type - writeFileSync("some/path", "some data", "made-up-encoding"); - }, - Error, - `The value "made-up-encoding" is invalid for option "encoding"`, - ); - - assertThrows( - () => { - writeFile( - "some/path", - "some data", - { - // @ts-expect-error Type '"made-up-encoding"' is not assignable to type - encoding: "made-up-encoding", - }, - () => {}, - ); - }, - Error, - `The value "made-up-encoding" is invalid for option "encoding"`, - ); - - assertThrows( - () => { - writeFileSync("some/path", "some data", { - // @ts-expect-error Type '"made-up-encoding"' is not assignable to type - encoding: "made-up-encoding", - }); - }, - Error, - `The value "made-up-encoding" is invalid for option "encoding"`, - ); -}); - -Deno.test( - "Unsupported encoding results in error()", - function testUnsupportedEncoding() { - assertThrows( - () => { - writeFile("some/path", "some data", "utf16le", () => {}); - }, - Error, - `Not implemented: "utf16le" encoding`, - ); - - assertThrows( - () => { - writeFileSync("some/path", "some data", "utf16le"); - }, - Error, - `Not implemented: "utf16le" encoding`, - ); - }, -); - -Deno.test( - "Data is written to correct rid", - async function testCorrectWriteUsingRid() { - const tempFile: string = await Deno.makeTempFile(); - const file: Deno.File = await Deno.open(tempFile, { - create: true, - write: true, - read: true, - }); - - await new Promise((resolve, reject) => { - writeFile(file.rid, "hello world", (err) => { - if (err) return reject(err); - resolve(); - }); - }); - Deno.close(file.rid); - - const data = await Deno.readFile(tempFile); - await Deno.remove(tempFile); - assertEquals(decoder.decode(data), "hello world"); - }, -); - -Deno.test( - "Data is written to correct file", - async function testCorrectWriteUsingPath() { - const res = await new Promise((resolve) => { - writeFile("_fs_writeFile_test_file.txt", "hello world", resolve); - }); - - const data = await Deno.readFile("_fs_writeFile_test_file.txt"); - await Deno.remove("_fs_writeFile_test_file.txt"); - assertEquals(res, null); - assertEquals(decoder.decode(data), "hello world"); - }, -); - -Deno.test( - "Data is written to correct file encodings", - async function testCorrectWriteUsingDifferentEncodings() { - const encodings = [ - ["hex", "68656c6c6f20776f726c64"], - ["HEX", "68656c6c6f20776f726c64"], - ["base64", "aGVsbG8gd29ybGQ="], - ["BASE64", "aGVsbG8gd29ybGQ="], - ["utf8", "hello world"], - ["utf-8", "hello world"], - ]; - - for (const [encoding, value] of encodings) { - const res = await new Promise((resolve) => { - writeFile( - "_fs_writeFile_test_file.txt", - value, - encoding as TextEncodings, - resolve, - ); - }); - - const data = await Deno.readFile("_fs_writeFile_test_file.txt"); - await Deno.remove("_fs_writeFile_test_file.txt"); - assertEquals(res, null); - assertEquals(decoder.decode(data), "hello world"); - } - }, -); - -Deno.test("Path can be an URL", async function testCorrectWriteUsingURL() { - const url = new URL( - Deno.build.os === "windows" - ? "file:///" + - path - .join(testDataDir, "_fs_writeFile_test_file_url.txt") - .replace(/\\/g, "/") - : "file://" + path.join(testDataDir, "_fs_writeFile_test_file_url.txt"), - ); - const filePath = path.fromFileUrl(url); - const res = await new Promise((resolve) => { - writeFile(url, "hello world", resolve); - }); - assert(res === null); - - const data = await Deno.readFile(filePath); - await Deno.remove(filePath); - assertEquals(res, null); - assertEquals(decoder.decode(data), "hello world"); -}); - -Deno.test("Mode is correctly set", async function testCorrectFileMode() { - if (Deno.build.os === "windows") return; - const filename = "_fs_writeFile_test_file.txt"; - - const res = await new Promise((resolve) => { - writeFile(filename, "hello world", { mode: 0o777 }, resolve); - }); - - const fileInfo = await Deno.stat(filename); - await Deno.remove(filename); - assertEquals(res, null); - assert(fileInfo && fileInfo.mode); - assertEquals(fileInfo.mode & 0o777, 0o777); -}); - -Deno.test( - "Mode is not set when rid is passed", - async function testCorrectFileModeRid() { - if (Deno.build.os === "windows") return; - - const filename: string = await Deno.makeTempFile(); - const file: Deno.File = await Deno.open(filename, { - create: true, - write: true, - read: true, - }); - - await new Promise((resolve, reject) => { - writeFile(file.rid, "hello world", { mode: 0o777 }, (err) => { - if (err) return reject(err); - resolve(); - }); - }); - Deno.close(file.rid); - - const fileInfo = await Deno.stat(filename); - await Deno.remove(filename); - assert(fileInfo.mode); - assertNotEquals(fileInfo.mode & 0o777, 0o777); - }, -); - -Deno.test( - "Data is written synchronously to correct rid", - function testCorrectWriteSyncUsingRid() { - const tempFile: string = Deno.makeTempFileSync(); - const file: Deno.File = Deno.openSync(tempFile, { - create: true, - write: true, - read: true, - }); - - writeFileSync(file.rid, "hello world"); - Deno.close(file.rid); - - const data = Deno.readFileSync(tempFile); - Deno.removeSync(tempFile); - assertEquals(decoder.decode(data), "hello world"); - }, -); - -Deno.test( - "Data is written to correct file encodings", - function testCorrectWriteSyncUsingDifferentEncodings() { - const encodings = [ - ["hex", "68656c6c6f20776f726c64"], - ["HEX", "68656c6c6f20776f726c64"], - ["base64", "aGVsbG8gd29ybGQ="], - ["BASE64", "aGVsbG8gd29ybGQ="], - ["utf8", "hello world"], - ["utf-8", "hello world"], - ]; - - for (const [encoding, value] of encodings) { - const file = "_fs_writeFileSync_test_file"; - writeFileSync(file, value, encoding as TextEncodings); - - const data = Deno.readFileSync(file); - Deno.removeSync(file); - assertEquals(decoder.decode(data), "hello world"); - } - }, -); - -Deno.test( - "Data is written synchronously to correct file", - function testCorrectWriteSyncUsingPath() { - const file = "_fs_writeFileSync_test_file"; - - writeFileSync(file, "hello world"); - - const data = Deno.readFileSync(file); - Deno.removeSync(file); - assertEquals(decoder.decode(data), "hello world"); - }, -); - -Deno.test("sync: Path can be an URL", function testCorrectWriteSyncUsingURL() { - const filePath = path.join( - testDataDir, - "_fs_writeFileSync_test_file_url.txt", - ); - const url = new URL( - Deno.build.os === "windows" - ? "file:///" + filePath.replace(/\\/g, "/") - : "file://" + filePath, - ); - writeFileSync(url, "hello world"); - - const data = Deno.readFileSync(filePath); - Deno.removeSync(filePath); - assertEquals(decoder.decode(data), "hello world"); -}); - -Deno.test( - "Mode is correctly set when writing synchronously", - function testCorrectFileModeSync() { - if (Deno.build.os === "windows") return; - const filename = "_fs_writeFileSync_test_file.txt"; - - writeFileSync(filename, "hello world", { mode: 0o777 }); - - const fileInfo = Deno.statSync(filename); - Deno.removeSync(filename); - assert(fileInfo && fileInfo.mode); - assertEquals(fileInfo.mode & 0o777, 0o777); - }, -); diff --git a/std/node/_fs/promises/_fs_readFile.ts b/std/node/_fs/promises/_fs_readFile.ts deleted file mode 100644 index 3067b301f8..0000000000 --- a/std/node/_fs/promises/_fs_readFile.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import type { - BinaryOptionsArgument, - FileOptionsArgument, - TextOptionsArgument, -} from "../_fs_common.ts"; -import { readFile as readFileCallback } from "../_fs_readFile.ts"; - -export function readFile( - path: string | URL, - options: TextOptionsArgument, -): Promise; -export function readFile( - path: string | URL, - options?: BinaryOptionsArgument, -): Promise; -export function readFile( - path: string | URL, - options?: FileOptionsArgument, -): Promise { - return new Promise((resolve, reject) => { - readFileCallback(path, options, (err, data): void => { - if (err) return reject(err); - if (data == null) { - return reject(new Error("Invalid state: data missing, but no error")); - } - resolve(data); - }); - }); -} diff --git a/std/node/_fs/promises/_fs_readFile_test.ts b/std/node/_fs/promises/_fs_readFile_test.ts deleted file mode 100644 index 2810d1773e..0000000000 --- a/std/node/_fs/promises/_fs_readFile_test.ts +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { readFile } from "./_fs_readFile.ts"; -import * as path from "../../../path/mod.ts"; -import { assert, assertEquals } from "../../../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 data: Uint8Array = await readFile(testData); - - assert(data instanceof Uint8Array); - assertEquals(new TextDecoder().decode(data), "hello world"); -}); - -Deno.test("readFileBinarySuccess", async function () { - const data: Uint8Array = await readFile(testData, "binary"); - - assert(data instanceof Uint8Array); - assertEquals(new TextDecoder().decode(data), "hello world"); -}); - -Deno.test("readFileBinaryObjectSuccess", async function () { - const data: Uint8Array = await readFile(testData, { encoding: "binary" }); - - assert(data instanceof Uint8Array); - assertEquals(new TextDecoder().decode(data), "hello world"); -}); - -Deno.test("readFileStringObjectSuccess", async function () { - const data: string = await readFile(testData, { encoding: "utf8" }); - - assertEquals(typeof data, "string"); - assertEquals(data, "hello world"); -}); - -Deno.test("readFileEncodeHexSuccess", async function () { - const data: string = await readFile(testData, { encoding: "hex" }); - assertEquals(typeof data, "string"); - assertEquals(data as string, "68656c6c6f20776f726c64"); -}); - -Deno.test("readFileEncodeBase64Success", async function () { - const data: string = await readFile(testData, { encoding: "base64" }); - assertEquals(typeof data, "string"); - assertEquals(data as string, "aGVsbG8gd29ybGQ="); -}); - -Deno.test("readFileStringSuccess", async function () { - const data: string = await readFile(testData, "utf8"); - - assertEquals(typeof data, "string"); - assertEquals(data, "hello world"); -}); - -Deno.test("readFileError", async function () { - try { - await readFile("invalid-file", "utf8"); - } catch (e) { - assert(e instanceof Deno.errors.NotFound); - } -}); diff --git a/std/node/_fs/promises/_fs_writeFile.ts b/std/node/_fs/promises/_fs_writeFile.ts deleted file mode 100644 index 554b65d24d..0000000000 --- a/std/node/_fs/promises/_fs_writeFile.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import type { WriteFileOptions } from "../_fs_common.ts"; -import type { Encodings } from "../../_utils.ts"; - -import { writeFile as writeFileCallback } from "../_fs_writeFile.ts"; - -export function writeFile( - pathOrRid: string | number | URL, - data: string | Uint8Array, - options?: Encodings | WriteFileOptions, -): Promise { - return new Promise((resolve, reject) => { - writeFileCallback(pathOrRid, data, options, (err?: Error | null) => { - if (err) return reject(err); - resolve(); - }); - }); -} diff --git a/std/node/_fs/promises/_fs_writeFile_test.ts b/std/node/_fs/promises/_fs_writeFile_test.ts deleted file mode 100644 index 644a416ca0..0000000000 --- a/std/node/_fs/promises/_fs_writeFile_test.ts +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertNotEquals, - assertThrowsAsync, -} from "../../../testing/asserts.ts"; -import { writeFile } from "./_fs_writeFile.ts"; -import type { TextEncodings } from "../../_utils.ts"; - -const decoder = new TextDecoder("utf-8"); - -Deno.test("Invalid encoding results in error()", function testEncodingErrors() { - assertThrowsAsync( - async () => { - // @ts-expect-error Type '"made-up-encoding"' is not assignable to type - await writeFile("some/path", "some data", "made-up-encoding"); - }, - Error, - `The value "made-up-encoding" is invalid for option "encoding"`, - ); - assertThrowsAsync( - async () => { - await writeFile("some/path", "some data", { - // @ts-expect-error Type '"made-up-encoding"' is not assignable to type - encoding: "made-up-encoding", - }); - }, - Error, - `The value "made-up-encoding" is invalid for option "encoding"`, - ); -}); - -Deno.test( - "Unsupported encoding results in error()", - function testUnsupportedEncoding() { - assertThrowsAsync( - async () => { - await writeFile("some/path", "some data", "utf16le"); - }, - Error, - `Not implemented: "utf16le" encoding`, - ); - }, -); - -Deno.test( - "Data is written to correct rid", - async function testCorrectWriteUsingRid() { - const tempFile: string = await Deno.makeTempFile(); - const file: Deno.File = await Deno.open(tempFile, { - create: true, - write: true, - read: true, - }); - - await writeFile(file.rid, "hello world"); - Deno.close(file.rid); - - const data = await Deno.readFile(tempFile); - await Deno.remove(tempFile); - assertEquals(decoder.decode(data), "hello world"); - }, -); - -Deno.test( - "Data is written to correct file", - async function testCorrectWriteUsingPath() { - const openResourcesBeforeWrite: Deno.ResourceMap = Deno.resources(); - - await writeFile("_fs_writeFile_test_file.txt", "hello world"); - - assertEquals(Deno.resources(), openResourcesBeforeWrite); - const data = await Deno.readFile("_fs_writeFile_test_file.txt"); - await Deno.remove("_fs_writeFile_test_file.txt"); - assertEquals(decoder.decode(data), "hello world"); - }, -); - -Deno.test( - "Data is written to correct file encodings", - async function testCorrectWritePromiseUsingDifferentEncodings() { - const encodings = [ - ["hex", "68656c6c6f20776f726c64"], - ["HEX", "68656c6c6f20776f726c64"], - ["base64", "aGVsbG8gd29ybGQ="], - ["BASE64", "aGVsbG8gd29ybGQ="], - ["utf8", "hello world"], - ["utf-8", "hello world"], - ]; - - for (const [encoding, value] of encodings) { - await writeFile( - "_fs_writeFile_test_file.txt", - value, - encoding as TextEncodings, - ); - - const data = await Deno.readFile("_fs_writeFile_test_file.txt"); - await Deno.remove("_fs_writeFile_test_file.txt"); - assertEquals(decoder.decode(data), "hello world"); - } - }, -); - -Deno.test("Mode is correctly set", async function testCorrectFileMode() { - if (Deno.build.os === "windows") return; - const filename = "_fs_writeFile_test_file.txt"; - await writeFile(filename, "hello world", { mode: 0o777 }); - - const fileInfo = await Deno.stat(filename); - await Deno.remove(filename); - assert(fileInfo && fileInfo.mode); - assertEquals(fileInfo.mode & 0o777, 0o777); -}); - -Deno.test( - "Mode is not set when rid is passed", - async function testCorrectFileModeRid() { - if (Deno.build.os === "windows") return; - - const filename: string = await Deno.makeTempFile(); - const file: Deno.File = await Deno.open(filename, { - create: true, - write: true, - read: true, - }); - - await writeFile(file.rid, "hello world", { mode: 0o777 }); - Deno.close(file.rid); - - const fileInfo = await Deno.stat(filename); - await Deno.remove(filename); - assert(fileInfo.mode); - assertNotEquals(fileInfo.mode & 0o777, 0o777); - }, -); diff --git a/std/node/_fs/promises/mod.ts b/std/node/_fs/promises/mod.ts deleted file mode 100644 index 4cc6462b9c..0000000000 --- a/std/node/_fs/promises/mod.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { writeFile } from "./_fs_writeFile.ts"; -export { readFile } from "./_fs_readFile.ts"; diff --git a/std/node/_fs/testdata/hello.txt b/std/node/_fs/testdata/hello.txt deleted file mode 100644 index 95d09f2b10..0000000000 --- a/std/node/_fs/testdata/hello.txt +++ /dev/null @@ -1 +0,0 @@ -hello world \ No newline at end of file diff --git a/std/node/_stream/async_iterator.ts b/std/node/_stream/async_iterator.ts deleted file mode 100644 index 5369ef39c7..0000000000 --- a/std/node/_stream/async_iterator.ts +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import type { Buffer } from "../buffer.ts"; -import finished from "./end_of_stream.ts"; -import Readable from "./readable.ts"; -import type Stream from "./stream.ts"; -import { destroyer } from "./destroy.ts"; - -const kLastResolve = Symbol("lastResolve"); -const kLastReject = Symbol("lastReject"); -const kError = Symbol("error"); -const kEnded = Symbol("ended"); -const kLastPromise = Symbol("lastPromise"); -const kHandlePromise = Symbol("handlePromise"); -const kStream = Symbol("stream"); - -// TODO(Soremwar) -// Add Duplex streams -type IterableStreams = Stream | Readable; - -type IterableItem = Buffer | string | Uint8Array | undefined; -type ReadableIteratorResult = IteratorResult; - -function initIteratorSymbols( - o: ReadableStreamAsyncIterator, - symbols: symbol[], -) { - const properties: PropertyDescriptorMap = {}; - for (const sym in symbols) { - properties[sym] = { - configurable: false, - enumerable: false, - writable: true, - }; - } - Object.defineProperties(o, properties); -} - -function createIterResult( - value: IterableItem, - done: boolean, -): ReadableIteratorResult { - return { value, done }; -} - -function readAndResolve(iter: ReadableStreamAsyncIterator) { - const resolve = iter[kLastResolve]; - if (resolve !== null) { - const data = iter[kStream].read(); - if (data !== null) { - iter[kLastPromise] = null; - iter[kLastResolve] = null; - iter[kLastReject] = null; - resolve(createIterResult(data, false)); - } - } -} - -function onReadable(iter: ReadableStreamAsyncIterator) { - queueMicrotask(() => readAndResolve(iter)); -} - -function wrapForNext( - lastPromise: Promise, - iter: ReadableStreamAsyncIterator, -) { - return ( - resolve: (value: ReadableIteratorResult) => void, - reject: (error: Error) => void, - ) => { - lastPromise.then(() => { - if (iter[kEnded]) { - resolve(createIterResult(undefined, true)); - return; - } - - iter[kHandlePromise](resolve, reject); - }, reject); - }; -} - -function finish(self: ReadableStreamAsyncIterator, err?: Error) { - return new Promise( - ( - resolve: (result: ReadableIteratorResult) => void, - reject: (error: Error) => void, - ) => { - const stream = self[kStream]; - - finished(stream, (err) => { - if (err && err.code !== "ERR_STREAM_PREMATURE_CLOSE") { - reject(err); - } else { - resolve(createIterResult(undefined, true)); - } - }); - destroyer(stream, err); - }, - ); -} - -const AsyncIteratorPrototype = Object.getPrototypeOf( - Object.getPrototypeOf(async function* () {}).prototype, -); - -export class ReadableStreamAsyncIterator - implements AsyncIterableIterator { - [kEnded]: boolean; - [kError]: Error | null = null; - [kHandlePromise] = ( - resolve: (value: ReadableIteratorResult) => void, - reject: (value: Error) => void, - ) => { - const data = this[kStream].read(); - if (data) { - this[kLastPromise] = null; - this[kLastResolve] = null; - this[kLastReject] = null; - resolve(createIterResult(data, false)); - } else { - this[kLastResolve] = resolve; - this[kLastReject] = reject; - } - }; - [kLastPromise]: null | Promise; - [kLastReject]: null | ((value: Error) => void) = null; - [kLastResolve]: null | ((value: ReadableIteratorResult) => void) = null; - [kStream]: Readable; - [Symbol.asyncIterator] = AsyncIteratorPrototype[Symbol.asyncIterator]; - - constructor(stream: Readable) { - this[kEnded] = stream.readableEnded || stream._readableState.endEmitted; - this[kStream] = stream; - initIteratorSymbols(this, [ - kEnded, - kError, - kHandlePromise, - kLastPromise, - kLastReject, - kLastResolve, - kStream, - ]); - } - - get stream() { - return this[kStream]; - } - - next(): Promise { - const error = this[kError]; - if (error !== null) { - return Promise.reject(error); - } - - if (this[kEnded]) { - return Promise.resolve(createIterResult(undefined, true)); - } - - if (this[kStream].destroyed) { - return new Promise((resolve, reject) => { - if (this[kError]) { - reject(this[kError]); - } else if (this[kEnded]) { - resolve(createIterResult(undefined, true)); - } else { - finished(this[kStream], (err) => { - if (err && err.code !== "ERR_STREAM_PREMATURE_CLOSE") { - reject(err); - } else { - resolve(createIterResult(undefined, true)); - } - }); - } - }); - } - - const lastPromise = this[kLastPromise]; - let promise; - - if (lastPromise) { - promise = new Promise(wrapForNext(lastPromise, this)); - } else { - const data = this[kStream].read(); - if (data !== null) { - return Promise.resolve(createIterResult(data, false)); - } - - promise = new Promise(this[kHandlePromise]); - } - - this[kLastPromise] = promise; - - return promise; - } - - return(): Promise { - return finish(this); - } - - throw(err: Error): Promise { - return finish(this, err); - } -} - -const createReadableStreamAsyncIterator = (stream: IterableStreams) => { - // deno-lint-ignore no-explicit-any - if (typeof (stream as any).read !== "function") { - const src = stream; - stream = new Readable({ objectMode: true }).wrap(src); - finished(stream, (err) => destroyer(src, err)); - } - - const iterator = new ReadableStreamAsyncIterator(stream as Readable); - iterator[kLastPromise] = null; - - finished(stream, { writable: false }, (err) => { - if (err && err.code !== "ERR_STREAM_PREMATURE_CLOSE") { - const reject = iterator[kLastReject]; - if (reject !== null) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - reject(err); - } - iterator[kError] = err; - return; - } - - const resolve = iterator[kLastResolve]; - if (resolve !== null) { - iterator[kLastPromise] = null; - iterator[kLastResolve] = null; - iterator[kLastReject] = null; - resolve(createIterResult(undefined, true)); - } - iterator[kEnded] = true; - }); - - stream.on("readable", onReadable.bind(null, iterator)); - - return iterator; -}; - -export default createReadableStreamAsyncIterator; diff --git a/std/node/_stream/async_iterator_test.ts b/std/node/_stream/async_iterator_test.ts deleted file mode 100644 index 17698e0fd2..0000000000 --- a/std/node/_stream/async_iterator_test.ts +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import Readable from "./readable.ts"; -import Stream from "./stream.ts"; -import toReadableAsyncIterator from "./async_iterator.ts"; -import { deferred } from "../../async/mod.ts"; -import { assertEquals, assertThrowsAsync } from "../../testing/asserts.ts"; - -Deno.test("Stream to async iterator", async () => { - let destroyExecuted = 0; - const destroyExecutedExpected = 1; - const destroyExpectedExecutions = deferred(); - - class AsyncIteratorStream extends Stream { - constructor() { - super(); - } - - destroy() { - destroyExecuted++; - if (destroyExecuted == destroyExecutedExpected) { - destroyExpectedExecutions.resolve(); - } - } - - [Symbol.asyncIterator] = Readable.prototype[Symbol.asyncIterator]; - } - - const stream = new AsyncIteratorStream(); - - queueMicrotask(() => { - stream.emit("data", "hello"); - stream.emit("data", "world"); - stream.emit("end"); - }); - - let res = ""; - - for await (const d of stream) { - res += d; - } - assertEquals(res, "helloworld"); - - const destroyTimeout = setTimeout( - () => destroyExpectedExecutions.reject(), - 1000, - ); - await destroyExpectedExecutions; - clearTimeout(destroyTimeout); - assertEquals(destroyExecuted, destroyExecutedExpected); -}); - -Deno.test("Stream to async iterator throws on 'error' emitted", async () => { - let closeExecuted = 0; - const closeExecutedExpected = 1; - const closeExpectedExecutions = deferred(); - - let errorExecuted = 0; - const errorExecutedExpected = 1; - const errorExpectedExecutions = deferred(); - - class StreamImplementation extends Stream { - close() { - closeExecuted++; - if (closeExecuted == closeExecutedExpected) { - closeExpectedExecutions.resolve(); - } - } - } - - const stream = new StreamImplementation(); - queueMicrotask(() => { - stream.emit("data", 0); - stream.emit("data", 1); - stream.emit("error", new Error("asd")); - }); - - toReadableAsyncIterator(stream) - .next() - .catch((err) => { - errorExecuted++; - if (errorExecuted == errorExecutedExpected) { - errorExpectedExecutions.resolve(); - } - assertEquals(err.message, "asd"); - }); - - const closeTimeout = setTimeout( - () => closeExpectedExecutions.reject(), - 1000, - ); - const errorTimeout = setTimeout( - () => errorExpectedExecutions.reject(), - 1000, - ); - await closeExpectedExecutions; - await errorExpectedExecutions; - clearTimeout(closeTimeout); - clearTimeout(errorTimeout); - assertEquals(closeExecuted, closeExecutedExpected); - assertEquals(errorExecuted, errorExecutedExpected); -}); - -Deno.test("Async iterator matches values of Readable", async () => { - const readable = new Readable({ - objectMode: true, - read() {}, - }); - readable.push(0); - readable.push(1); - readable.push(null); - - const iter = readable[Symbol.asyncIterator](); - - assertEquals( - await iter.next().then(({ value }) => value), - 0, - ); - for await (const d of iter) { - assertEquals(d, 1); - } -}); - -Deno.test("Async iterator throws on Readable destroyed sync", async () => { - const message = "kaboom from read"; - - const readable = new Readable({ - objectMode: true, - read() { - this.destroy(new Error(message)); - }, - }); - - await assertThrowsAsync( - async () => { - // deno-lint-ignore no-empty - for await (const k of readable) {} - }, - Error, - message, - ); -}); - -Deno.test("Async iterator throws on Readable destroyed async", async () => { - const message = "kaboom"; - const readable = new Readable({ - read() {}, - }); - const iterator = readable[Symbol.asyncIterator](); - - readable.destroy(new Error(message)); - - await assertThrowsAsync( - iterator.next.bind(iterator), - Error, - message, - ); -}); - -Deno.test("Async iterator finishes the iterator when Readable destroyed", async () => { - const readable = new Readable({ - read() {}, - }); - - readable.destroy(); - - const { done } = await readable[Symbol.asyncIterator]().next(); - assertEquals(done, true); -}); - -Deno.test("Async iterator finishes all item promises when Readable destroyed", async () => { - const r = new Readable({ - objectMode: true, - read() { - }, - }); - - const b = r[Symbol.asyncIterator](); - const c = b.next(); - const d = b.next(); - r.destroy(); - assertEquals(await c, { done: true, value: undefined }); - assertEquals(await d, { done: true, value: undefined }); -}); - -Deno.test("Async iterator: 'next' is triggered by Readable push", async () => { - const max = 42; - let readed = 0; - let received = 0; - const readable = new Readable({ - objectMode: true, - read() { - this.push("hello"); - if (++readed === max) { - this.push(null); - } - }, - }); - - for await (const k of readable) { - received++; - assertEquals(k, "hello"); - } - - assertEquals(readed, received); -}); - -Deno.test("Async iterator: 'close' called on forced iteration end", async () => { - let closeExecuted = 0; - const closeExecutedExpected = 1; - const closeExpectedExecutions = deferred(); - - class IndestructibleReadable extends Readable { - constructor() { - super({ - autoDestroy: false, - read() {}, - }); - } - - close() { - closeExecuted++; - if (closeExecuted == closeExecutedExpected) { - closeExpectedExecutions.resolve(); - } - readable.emit("close"); - } - - // deno-lint-ignore ban-ts-comment - //@ts-ignore - destroy = null; - } - - const readable = new IndestructibleReadable(); - readable.push("asd"); - readable.push("asd"); - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - for await (const d of readable) { - break; - } - - const closeTimeout = setTimeout( - () => closeExpectedExecutions.reject(), - 1000, - ); - await closeExpectedExecutions; - clearTimeout(closeTimeout); - assertEquals(closeExecuted, closeExecutedExpected); -}); diff --git a/std/node/_stream/buffer_list.ts b/std/node/_stream/buffer_list.ts deleted file mode 100644 index fe1a693c0e..0000000000 --- a/std/node/_stream/buffer_list.ts +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { Buffer } from "../buffer.ts"; - -type BufferListItem = { - data: Buffer | string | Uint8Array; - next: BufferListItem | null; -}; - -export default class BufferList { - head: BufferListItem | null = null; - tail: BufferListItem | null = null; - length: number; - - constructor() { - this.head = null; - this.tail = null; - this.length = 0; - } - - push(v: Buffer | string | Uint8Array) { - const entry = { data: v, next: null }; - if (this.length > 0) { - (this.tail as BufferListItem).next = entry; - } else { - this.head = entry; - } - this.tail = entry; - ++this.length; - } - - unshift(v: Buffer | string | Uint8Array) { - const entry = { data: v, next: this.head }; - if (this.length === 0) { - this.tail = entry; - } - this.head = entry; - ++this.length; - } - - shift() { - if (this.length === 0) { - return; - } - const ret = (this.head as BufferListItem).data; - if (this.length === 1) { - this.head = this.tail = null; - } else { - this.head = (this.head as BufferListItem).next; - } - --this.length; - return ret; - } - - clear() { - this.head = this.tail = null; - this.length = 0; - } - - join(s: string) { - if (this.length === 0) { - return ""; - } - let p: BufferListItem | null = (this.head as BufferListItem); - let ret = "" + p.data; - p = p.next; - while (p) { - ret += s + p.data; - p = p.next; - } - return ret; - } - - concat(n: number) { - if (this.length === 0) { - return Buffer.alloc(0); - } - const ret = Buffer.allocUnsafe(n >>> 0); - let p = this.head; - let i = 0; - while (p) { - ret.set(p.data as Buffer, i); - i += p.data.length; - p = p.next; - } - return ret; - } - - // Consumes a specified amount of bytes or characters from the buffered data. - consume(n: number, hasStrings: boolean) { - const data = (this.head as BufferListItem).data; - if (n < data.length) { - // `slice` is the same for buffers and strings. - const slice = data.slice(0, n); - (this.head as BufferListItem).data = data.slice(n); - return slice; - } - if (n === data.length) { - // First chunk is a perfect match. - return this.shift(); - } - // Result spans more than one buffer. - return hasStrings ? this._getString(n) : this._getBuffer(n); - } - - first() { - return (this.head as BufferListItem).data; - } - - *[Symbol.iterator]() { - for (let p = this.head; p; p = p.next) { - yield p.data; - } - } - - // Consumes a specified amount of characters from the buffered data. - _getString(n: number) { - let ret = ""; - let p: BufferListItem | null = (this.head as BufferListItem); - let c = 0; - p = p.next as BufferListItem; - do { - const str = p.data; - if (n > str.length) { - ret += str; - n -= str.length; - } else { - if (n === str.length) { - ret += str; - ++c; - if (p.next) { - this.head = p.next; - } else { - this.head = this.tail = null; - } - } else { - ret += str.slice(0, n); - this.head = p; - p.data = str.slice(n); - } - break; - } - ++c; - p = p.next; - } while (p); - this.length -= c; - return ret; - } - - // Consumes a specified amount of bytes from the buffered data. - _getBuffer(n: number) { - const ret = Buffer.allocUnsafe(n); - const retLen = n; - let p: BufferListItem | null = (this.head as BufferListItem); - let c = 0; - p = p.next as BufferListItem; - do { - const buf = p.data as Buffer; - if (n > buf.length) { - ret.set(buf, retLen - n); - n -= buf.length; - } else { - if (n === buf.length) { - ret.set(buf, retLen - n); - ++c; - if (p.next) { - this.head = p.next; - } else { - this.head = this.tail = null; - } - } else { - ret.set(new Uint8Array(buf.buffer, buf.byteOffset, n), retLen - n); - this.head = p; - p.data = buf.slice(n); - } - break; - } - ++c; - p = p.next; - } while (p); - this.length -= c; - return ret; - } -} diff --git a/std/node/_stream/destroy.ts b/std/node/_stream/destroy.ts deleted file mode 100644 index d13e12de26..0000000000 --- a/std/node/_stream/destroy.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import type Duplex from "./duplex.ts"; -import type Readable from "./readable.ts"; -import type Stream from "./stream.ts"; -import type Writable from "./writable.ts"; - -//This whole module acts as a 'normalizer' -//Idea behind it is you can pass any kind of streams and functions will execute anyways - -//TODO(Soremwar) -//Should be any implementation of stream -//This is a guard to check executed methods exist inside the implementation -type StreamImplementations = Duplex | Readable | Writable; - -// TODO(Soremwar) -// Bring back once requests are implemented -// function isRequest(stream: any) { -// return stream && stream.setHeader && typeof stream.abort === "function"; -// } - -export function destroyer(stream: Stream, err?: Error | null) { - // TODO(Soremwar) - // Bring back once requests are implemented - // if (isRequest(stream)) return stream.abort(); - // if (isRequest(stream.req)) return stream.req.abort(); - if ( - typeof (stream as StreamImplementations).destroy === "function" - ) { - return (stream as StreamImplementations).destroy(err); - } - // A test of async iterator mocks an upcoming implementation of stream - // his is casted to any in the meanwhile - // deno-lint-ignore no-explicit-any - if (typeof (stream as any).close === "function") { - // deno-lint-ignore no-explicit-any - return (stream as any).close(); - } -} diff --git a/std/node/_stream/duplex.ts b/std/node/_stream/duplex.ts deleted file mode 100644 index b5c429f0af..0000000000 --- a/std/node/_stream/duplex.ts +++ /dev/null @@ -1,682 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { captureRejectionSymbol } from "../events.ts"; -import Readable, { ReadableState } from "./readable.ts"; -import Stream from "./stream.ts"; -import Writable, { WritableState } from "./writable.ts"; -import { Buffer } from "../buffer.ts"; -import { - ERR_STREAM_ALREADY_FINISHED, - ERR_STREAM_DESTROYED, - ERR_UNKNOWN_ENCODING, -} from "../_errors.ts"; -import type { Encodings } from "../_utils.ts"; -import createReadableStreamAsyncIterator from "./async_iterator.ts"; -import type { ReadableStreamAsyncIterator } from "./async_iterator.ts"; -import { - _destroy, - computeNewHighWaterMark, - emitReadable, - fromList, - howMuchToRead, - nReadingNextTick, - updateReadableListening, -} from "./readable_internal.ts"; -import { kOnFinished, writeV } from "./writable_internal.ts"; -import { - endDuplex, - finishMaybe, - onwrite, - readableAddChunk, -} from "./duplex_internal.ts"; -export { errorOrDestroy } from "./duplex_internal.ts"; - -export interface DuplexOptions { - allowHalfOpen?: boolean; - autoDestroy?: boolean; - decodeStrings?: boolean; - defaultEncoding?: Encodings; - destroy?( - this: Duplex, - error: Error | null, - callback: (error: Error | null) => void, - ): void; - emitClose?: boolean; - encoding?: Encodings; - final?(this: Duplex, callback: (error?: Error | null) => void): void; - highWaterMark?: number; - objectMode?: boolean; - read?(this: Duplex, size: number): void; - readable?: boolean; - readableHighWaterMark?: number; - readableObjectMode?: boolean; - writable?: boolean; - writableCorked?: number; - writableHighWaterMark?: number; - writableObjectMode?: boolean; - write?( - this: Duplex, - // deno-lint-ignore no-explicit-any - chunk: any, - encoding: Encodings, - callback: (error?: Error | null) => void, - ): void; - writev?: writeV; -} - -interface Duplex extends Readable, Writable {} - -/** - * A duplex is an implementation of a stream that has both Readable and Writable - * attributes and capabilities - */ -class Duplex extends Stream { - allowHalfOpen = true; - _final?: ( - callback: (error?: Error | null | undefined) => void, - ) => void; - _readableState: ReadableState; - _writableState: WritableState; - _writev?: writeV | null; - - constructor(options?: DuplexOptions) { - super(); - - if (options) { - if (options.allowHalfOpen === false) { - this.allowHalfOpen = false; - } - if (typeof options.destroy === "function") { - this._destroy = options.destroy; - } - if (typeof options.final === "function") { - this._final = options.final; - } - if (typeof options.read === "function") { - this._read = options.read; - } - if (options.readable === false) { - this.readable = false; - } - if (options.writable === false) { - this.writable = false; - } - if (typeof options.write === "function") { - this._write = options.write; - } - if (typeof options.writev === "function") { - this._writev = options.writev; - } - } - - const readableOptions = { - autoDestroy: options?.autoDestroy, - defaultEncoding: options?.defaultEncoding, - destroy: options?.destroy as unknown as ( - this: Readable, - error: Error | null, - callback: (error: Error | null) => void, - ) => void, - emitClose: options?.emitClose, - encoding: options?.encoding, - highWaterMark: options?.highWaterMark ?? options?.readableHighWaterMark, - objectMode: options?.objectMode ?? options?.readableObjectMode, - read: options?.read as unknown as (this: Readable) => void, - }; - - const writableOptions = { - autoDestroy: options?.autoDestroy, - decodeStrings: options?.decodeStrings, - defaultEncoding: options?.defaultEncoding, - destroy: options?.destroy as unknown as ( - this: Writable, - error: Error | null, - callback: (error: Error | null) => void, - ) => void, - emitClose: options?.emitClose, - final: options?.final as unknown as ( - this: Writable, - callback: (error?: Error | null) => void, - ) => void, - highWaterMark: options?.highWaterMark ?? options?.writableHighWaterMark, - objectMode: options?.objectMode ?? options?.writableObjectMode, - write: options?.write as unknown as ( - this: Writable, - // deno-lint-ignore no-explicit-any - chunk: any, - encoding: string, - callback: (error?: Error | null) => void, - ) => void, - writev: options?.writev as unknown as ( - this: Writable, - // deno-lint-ignore no-explicit-any - chunks: Array<{ chunk: any; encoding: Encodings }>, - callback: (error?: Error | null) => void, - ) => void, - }; - - this._readableState = new ReadableState(readableOptions); - this._writableState = new WritableState( - writableOptions, - this as unknown as Writable, - ); - //Very important to override onwrite here, duplex implementation adds a check - //on the readable side - this._writableState.onwrite = onwrite.bind(undefined, this); - } - - [captureRejectionSymbol](err?: Error) { - this.destroy(err); - } - - [Symbol.asyncIterator](): ReadableStreamAsyncIterator { - return createReadableStreamAsyncIterator(this); - } - - _destroy( - error: Error | null, - callback: (error?: Error | null) => void, - ): void { - callback(error); - } - - _read = Readable.prototype._read; - - _undestroy = Readable.prototype._undestroy; - - destroy(err?: Error | null, cb?: (error?: Error | null) => void) { - const r = this._readableState; - const w = this._writableState; - - if (w.destroyed || r.destroyed) { - if (typeof cb === "function") { - cb(); - } - - return this; - } - - if (err) { - // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 - err.stack; - - if (!w.errored) { - w.errored = err; - } - if (!r.errored) { - r.errored = err; - } - } - - w.destroyed = true; - r.destroyed = true; - - this._destroy(err || null, (err) => { - if (err) { - // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 - err.stack; - - if (!w.errored) { - w.errored = err; - } - if (!r.errored) { - r.errored = err; - } - } - - w.closed = true; - r.closed = true; - - if (typeof cb === "function") { - cb(err); - } - - if (err) { - queueMicrotask(() => { - const r = this._readableState; - const w = this._writableState; - - if (!w.errorEmitted && !r.errorEmitted) { - w.errorEmitted = true; - r.errorEmitted = true; - - this.emit("error", err); - } - - r.closeEmitted = true; - - if (w.emitClose || r.emitClose) { - this.emit("close"); - } - }); - } else { - queueMicrotask(() => { - const r = this._readableState; - const w = this._writableState; - - r.closeEmitted = true; - - if (w.emitClose || r.emitClose) { - this.emit("close"); - } - }); - } - }); - - return this; - } - - isPaused = Readable.prototype.isPaused; - - off = this.removeListener; - - on( - event: "close" | "end" | "pause" | "readable" | "resume", - listener: () => void, - ): this; - // deno-lint-ignore no-explicit-any - on(event: "data", listener: (chunk: any) => void): this; - on(event: "error", listener: (err: Error) => void): this; - // deno-lint-ignore no-explicit-any - on(event: string | symbol, listener: (...args: any[]) => void): this; - on( - ev: string | symbol, - fn: - | (() => void) - // deno-lint-ignore no-explicit-any - | ((chunk: any) => void) - | ((err: Error) => void) - // deno-lint-ignore no-explicit-any - | ((...args: any[]) => void), - ) { - const res = super.on.call(this, ev, fn); - const state = this._readableState; - - if (ev === "data") { - state.readableListening = this.listenerCount("readable") > 0; - - if (state.flowing !== false) { - this.resume(); - } - } else if (ev === "readable") { - if (!state.endEmitted && !state.readableListening) { - state.readableListening = state.needReadable = true; - state.flowing = false; - state.emittedReadable = false; - if (state.length) { - emitReadable(this); - } else if (!state.reading) { - queueMicrotask(() => nReadingNextTick(this)); - } - } - } - - return res; - } - - pause = Readable.prototype.pause as () => this; - - pipe = Readable.prototype.pipe; - - // deno-lint-ignore no-explicit-any - push(chunk: any, encoding?: Encodings): boolean { - return readableAddChunk(this, chunk, encoding, false); - } - - /** You can override either this method, or the async `_read` method */ - read(n?: number) { - // Same as parseInt(undefined, 10), however V8 7.3 performance regressed - // in this scenario, so we are doing it manually. - if (n === undefined) { - n = NaN; - } - const state = this._readableState; - const nOrig = n; - - if (n > state.highWaterMark) { - state.highWaterMark = computeNewHighWaterMark(n); - } - - if (n !== 0) { - state.emittedReadable = false; - } - - if ( - n === 0 && - state.needReadable && - ((state.highWaterMark !== 0 - ? state.length >= state.highWaterMark - : state.length > 0) || - state.ended) - ) { - if (state.length === 0 && state.ended) { - endDuplex(this); - } else { - emitReadable(this); - } - return null; - } - - n = howMuchToRead(n, state); - - if (n === 0 && state.ended) { - if (state.length === 0) { - endDuplex(this); - } - return null; - } - - let doRead = state.needReadable; - if ( - state.length === 0 || state.length - (n as number) < state.highWaterMark - ) { - doRead = true; - } - - if ( - state.ended || state.reading || state.destroyed || state.errored || - !state.constructed - ) { - doRead = false; - } else if (doRead) { - state.reading = true; - state.sync = true; - if (state.length === 0) { - state.needReadable = true; - } - this._read(); - state.sync = false; - if (!state.reading) { - n = howMuchToRead(nOrig, state); - } - } - - let ret; - if ((n as number) > 0) { - ret = fromList((n as number), state); - } else { - ret = null; - } - - if (ret === null) { - state.needReadable = state.length <= state.highWaterMark; - n = 0; - } else { - state.length -= n as number; - if (state.multiAwaitDrain) { - (state.awaitDrainWriters as Set).clear(); - } else { - state.awaitDrainWriters = null; - } - } - - if (state.length === 0) { - if (!state.ended) { - state.needReadable = true; - } - - if (nOrig !== n && state.ended) { - endDuplex(this); - } - } - - if (ret !== null) { - this.emit("data", ret); - } - - return ret; - } - - removeAllListeners( - ev: - | "close" - | "data" - | "end" - | "error" - | "pause" - | "readable" - | "resume" - | symbol - | undefined, - ) { - const res = super.removeAllListeners(ev); - - if (ev === "readable" || ev === undefined) { - queueMicrotask(() => updateReadableListening(this)); - } - - return res; - } - - removeListener( - event: "close" | "end" | "pause" | "readable" | "resume", - listener: () => void, - ): this; - // deno-lint-ignore no-explicit-any - removeListener(event: "data", listener: (chunk: any) => void): this; - removeListener(event: "error", listener: (err: Error) => void): this; - removeListener( - event: string | symbol, - // deno-lint-ignore no-explicit-any - listener: (...args: any[]) => void, - ): this; - removeListener( - ev: string | symbol, - fn: - | (() => void) - // deno-lint-ignore no-explicit-any - | ((chunk: any) => void) - | ((err: Error) => void) - // deno-lint-ignore no-explicit-any - | ((...args: any[]) => void), - ) { - const res = super.removeListener.call(this, ev, fn); - - if (ev === "readable") { - queueMicrotask(() => updateReadableListening(this)); - } - - return res; - } - - resume = Readable.prototype.resume as () => this; - - setEncoding = Readable.prototype.setEncoding as (enc: string) => this; - - // deno-lint-ignore no-explicit-any - unshift(chunk: any, encoding?: Encodings): boolean { - return readableAddChunk(this, chunk, encoding, true); - } - - unpipe = Readable.prototype.unpipe as (dest?: Writable | undefined) => this; - - wrap = Readable.prototype.wrap as (stream: Stream) => this; - - get readable(): boolean { - return this._readableState?.readable && - !this._readableState?.destroyed && - !this._readableState?.errorEmitted && - !this._readableState?.endEmitted; - } - set readable(val: boolean) { - if (this._readableState) { - this._readableState.readable = val; - } - } - - get readableHighWaterMark(): number { - return this._readableState.highWaterMark; - } - - get readableBuffer() { - return this._readableState && this._readableState.buffer; - } - - get readableFlowing(): boolean | null { - return this._readableState.flowing; - } - - set readableFlowing(state: boolean | null) { - if (this._readableState) { - this._readableState.flowing = state; - } - } - - get readableLength() { - return this._readableState.length; - } - - get readableObjectMode() { - return this._readableState ? this._readableState.objectMode : false; - } - - get readableEncoding() { - return this._readableState ? this._readableState.encoding : null; - } - - get readableEnded() { - return this._readableState ? this._readableState.endEmitted : false; - } - - _write = Writable.prototype._write; - - write = Writable.prototype.write; - - cork = Writable.prototype.cork; - - uncork = Writable.prototype.uncork; - - setDefaultEncoding(encoding: string) { - // node::ParseEncoding() requires lower case. - if (typeof encoding === "string") { - encoding = encoding.toLowerCase(); - } - if (!Buffer.isEncoding(encoding)) { - throw new ERR_UNKNOWN_ENCODING(encoding); - } - this._writableState.defaultEncoding = encoding as Encodings; - return this; - } - - end(cb?: () => void): void; - // deno-lint-ignore no-explicit-any - end(chunk: any, cb?: () => void): void; - // deno-lint-ignore no-explicit-any - end(chunk: any, encoding: Encodings, cb?: () => void): void; - - end( - // deno-lint-ignore no-explicit-any - x?: any | (() => void), - y?: Encodings | (() => void), - z?: () => void, - ) { - const state = this._writableState; - // deno-lint-ignore no-explicit-any - let chunk: any | null; - let encoding: Encodings | null; - let cb: undefined | ((error?: Error) => void); - - if (typeof x === "function") { - chunk = null; - encoding = null; - cb = x; - } else if (typeof y === "function") { - chunk = x; - encoding = null; - cb = y; - } else { - chunk = x; - encoding = y as Encodings; - cb = z; - } - - if (chunk !== null && chunk !== undefined) { - this.write(chunk, encoding); - } - - if (state.corked) { - state.corked = 1; - this.uncork(); - } - - let err: Error | undefined; - if (!state.errored && !state.ending) { - state.ending = true; - finishMaybe(this, state, true); - state.ended = true; - } else if (state.finished) { - err = new ERR_STREAM_ALREADY_FINISHED("end"); - } else if (state.destroyed) { - err = new ERR_STREAM_DESTROYED("end"); - } - - if (typeof cb === "function") { - if (err || state.finished) { - queueMicrotask(() => { - (cb as (error?: Error | undefined) => void)(err); - }); - } else { - state[kOnFinished].push(cb); - } - } - - return this; - } - - get destroyed() { - if ( - this._readableState === undefined || - this._writableState === undefined - ) { - return false; - } - return this._readableState.destroyed && this._writableState.destroyed; - } - - set destroyed(value: boolean) { - if (this._readableState && this._writableState) { - this._readableState.destroyed = value; - this._writableState.destroyed = value; - } - } - - get writable() { - const w = this._writableState; - return !w.destroyed && !w.errored && !w.ending && !w.ended; - } - - set writable(val) { - if (this._writableState) { - this._writableState.writable = !!val; - } - } - - get writableFinished() { - return this._writableState ? this._writableState.finished : false; - } - - get writableObjectMode() { - return this._writableState ? this._writableState.objectMode : false; - } - - get writableBuffer() { - return this._writableState && this._writableState.getBuffer(); - } - - get writableEnded() { - return this._writableState ? this._writableState.ending : false; - } - - get writableHighWaterMark() { - return this._writableState && this._writableState.highWaterMark; - } - - get writableCorked() { - return this._writableState ? this._writableState.corked : 0; - } - - get writableLength() { - return this._writableState && this._writableState.length; - } -} - -export default Duplex; diff --git a/std/node/_stream/duplex_internal.ts b/std/node/_stream/duplex_internal.ts deleted file mode 100644 index bfd9749f8b..0000000000 --- a/std/node/_stream/duplex_internal.ts +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import type { ReadableState } from "./readable.ts"; -import { addChunk, maybeReadMore, onEofChunk } from "./readable_internal.ts"; -import type Writable from "./writable.ts"; -import type { WritableState } from "./writable.ts"; -import { - afterWrite, - AfterWriteTick, - afterWriteTick, - clearBuffer, - errorBuffer, - kOnFinished, - needFinish, - prefinish, -} from "./writable_internal.ts"; -import { Buffer } from "../buffer.ts"; -import type Duplex from "./duplex.ts"; -import { - ERR_MULTIPLE_CALLBACK, - ERR_STREAM_PUSH_AFTER_EOF, - ERR_STREAM_UNSHIFT_AFTER_END_EVENT, -} from "../_errors.ts"; - -export function endDuplex(stream: Duplex) { - const state = stream._readableState; - - if (!state.endEmitted) { - state.ended = true; - queueMicrotask(() => endReadableNT(state, stream)); - } -} - -function endReadableNT(state: ReadableState, stream: Duplex) { - // Check that we didn't get one last unshift. - if ( - !state.errorEmitted && !state.closeEmitted && - !state.endEmitted && state.length === 0 - ) { - state.endEmitted = true; - stream.emit("end"); - - if (stream.writable && stream.allowHalfOpen === false) { - queueMicrotask(() => endWritableNT(state, stream)); - } else if (state.autoDestroy) { - // In case of duplex streams we need a way to detect - // if the writable side is ready for autoDestroy as well. - const wState = stream._writableState; - const autoDestroy = !wState || ( - wState.autoDestroy && - // We don't expect the writable to ever 'finish' - // if writable is explicitly set to false. - (wState.finished || wState.writable === false) - ); - - if (autoDestroy) { - stream.destroy(); - } - } - } -} - -function endWritableNT(state: ReadableState, stream: Duplex) { - const writable = stream.writable && - !stream.writableEnded && - !stream.destroyed; - if (writable) { - stream.end(); - } -} - -export function errorOrDestroy( - // deno-lint-ignore no-explicit-any - this: any, - stream: Duplex, - err: Error, - sync = false, -) { - const r = stream._readableState; - const w = stream._writableState; - - if (w.destroyed || r.destroyed) { - return this; - } - - if (r.autoDestroy || w.autoDestroy) { - stream.destroy(err); - } else if (err) { - // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 - err.stack; - - if (w && !w.errored) { - w.errored = err; - } - if (r && !r.errored) { - r.errored = err; - } - - if (sync) { - queueMicrotask(() => { - if (w.errorEmitted || r.errorEmitted) { - return; - } - - w.errorEmitted = true; - r.errorEmitted = true; - - stream.emit("error", err); - }); - } else { - if (w.errorEmitted || r.errorEmitted) { - return; - } - - w.errorEmitted = true; - r.errorEmitted = true; - - stream.emit("error", err); - } - } -} - -function finish(stream: Duplex, state: WritableState) { - state.pendingcb--; - if (state.errorEmitted || state.closeEmitted) { - return; - } - - state.finished = true; - - for (const callback of state[kOnFinished].splice(0)) { - callback(); - } - - stream.emit("finish"); - - if (state.autoDestroy) { - stream.destroy(); - } -} - -export function finishMaybe( - stream: Duplex, - state: WritableState, - sync?: boolean, -) { - if (needFinish(state)) { - prefinish(stream as Writable, state); - if (state.pendingcb === 0 && needFinish(state)) { - state.pendingcb++; - if (sync) { - queueMicrotask(() => finish(stream, state)); - } else { - finish(stream, state); - } - } - } -} - -export function onwrite(stream: Duplex, er?: Error | null) { - const state = stream._writableState; - const sync = state.sync; - const cb = state.writecb; - - if (typeof cb !== "function") { - errorOrDestroy(stream, new ERR_MULTIPLE_CALLBACK()); - return; - } - - state.writing = false; - state.writecb = null; - state.length -= state.writelen; - state.writelen = 0; - - if (er) { - // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 - er.stack; - - if (!state.errored) { - state.errored = er; - } - - if (stream._readableState && !stream._readableState.errored) { - stream._readableState.errored = er; - } - - if (sync) { - queueMicrotask(() => onwriteError(stream, state, er, cb)); - } else { - onwriteError(stream, state, er, cb); - } - } else { - if (state.buffered.length > state.bufferedIndex) { - clearBuffer(stream, state); - } - - if (sync) { - if ( - state.afterWriteTickInfo !== null && - state.afterWriteTickInfo.cb === cb - ) { - state.afterWriteTickInfo.count++; - } else { - state.afterWriteTickInfo = { - count: 1, - cb: (cb as (error?: Error) => void), - stream: stream as Writable, - state, - }; - queueMicrotask(() => - afterWriteTick(state.afterWriteTickInfo as AfterWriteTick) - ); - } - } else { - afterWrite(stream as Writable, state, 1, cb as (error?: Error) => void); - } - } -} - -function onwriteError( - stream: Duplex, - state: WritableState, - er: Error, - cb: (error: Error) => void, -) { - --state.pendingcb; - - cb(er); - errorBuffer(state); - errorOrDestroy(stream, er); -} - -export function readableAddChunk( - stream: Duplex, - chunk: string | Buffer | Uint8Array | null, - encoding: undefined | string = undefined, - addToFront: boolean, -) { - const state = stream._readableState; - let usedEncoding = encoding; - - let err; - if (!state.objectMode) { - if (typeof chunk === "string") { - usedEncoding = encoding || state.defaultEncoding; - if (state.encoding !== usedEncoding) { - if (addToFront && state.encoding) { - chunk = Buffer.from(chunk, usedEncoding).toString(state.encoding); - } else { - chunk = Buffer.from(chunk, usedEncoding); - usedEncoding = ""; - } - } - } else if (chunk instanceof Uint8Array) { - chunk = Buffer.from(chunk); - } - } - - if (err) { - errorOrDestroy(stream, err); - } else if (chunk === null) { - state.reading = false; - onEofChunk(stream, state); - } else if (state.objectMode || (chunk.length > 0)) { - if (addToFront) { - if (state.endEmitted) { - errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT()); - } else { - addChunk(stream, state, chunk, true); - } - } else if (state.ended) { - errorOrDestroy(stream, new ERR_STREAM_PUSH_AFTER_EOF()); - } else if (state.destroyed || state.errored) { - return false; - } else { - state.reading = false; - if (state.decoder && !usedEncoding) { - //TODO(Soremwar) - //I don't think this cast is right - chunk = state.decoder.write(Buffer.from(chunk as Uint8Array)); - if (state.objectMode || chunk.length !== 0) { - addChunk(stream, state, chunk, false); - } else { - maybeReadMore(stream, state); - } - } else { - addChunk(stream, state, chunk, false); - } - } - } else if (!addToFront) { - state.reading = false; - maybeReadMore(stream, state); - } - - return !state.ended && - (state.length < state.highWaterMark || state.length === 0); -} diff --git a/std/node/_stream/duplex_test.ts b/std/node/_stream/duplex_test.ts deleted file mode 100644 index 1596ec2187..0000000000 --- a/std/node/_stream/duplex_test.ts +++ /dev/null @@ -1,698 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { Buffer } from "../buffer.ts"; -import Duplex from "./duplex.ts"; -import finished from "./end_of_stream.ts"; -import { - assert, - assertEquals, - assertStrictEquals, - assertThrows, -} from "../../testing/asserts.ts"; -import { deferred, delay } from "../../async/mod.ts"; - -Deno.test("Duplex stream works normally", () => { - const stream = new Duplex({ objectMode: true }); - - assert(stream._readableState.objectMode); - assert(stream._writableState.objectMode); - assert(stream.allowHalfOpen); - assertEquals(stream.listenerCount("end"), 0); - - let written: { val: number }; - let read: { val: number }; - - stream._write = (obj, _, cb) => { - written = obj; - cb(); - }; - - stream._read = () => {}; - - stream.on("data", (obj) => { - read = obj; - }); - - stream.push({ val: 1 }); - stream.end({ val: 2 }); - - stream.on("finish", () => { - assertEquals(read.val, 1); - assertEquals(written.val, 2); - }); -}); - -Deno.test("Duplex stream gets constructed correctly", () => { - const d1 = new Duplex({ - objectMode: true, - highWaterMark: 100, - }); - - assertEquals(d1.readableObjectMode, true); - assertEquals(d1.readableHighWaterMark, 100); - assertEquals(d1.writableObjectMode, true); - assertEquals(d1.writableHighWaterMark, 100); - - const d2 = new Duplex({ - readableObjectMode: false, - readableHighWaterMark: 10, - writableObjectMode: true, - writableHighWaterMark: 100, - }); - - assertEquals(d2.writableObjectMode, true); - assertEquals(d2.writableHighWaterMark, 100); - assertEquals(d2.readableObjectMode, false); - assertEquals(d2.readableHighWaterMark, 10); -}); - -Deno.test("Duplex stream can be paused", () => { - const readable = new Duplex(); - - // _read is a noop, here. - readable._read = () => {}; - - // Default state of a stream is not "paused" - assert(!readable.isPaused()); - - // Make the stream start flowing... - readable.on("data", () => {}); - - // still not paused. - assert(!readable.isPaused()); - - readable.pause(); - assert(readable.isPaused()); - readable.resume(); - assert(!readable.isPaused()); -}); - -Deno.test("Duplex stream sets enconding correctly", () => { - const readable = new Duplex({ - read() {}, - }); - - readable.setEncoding("utf8"); - - readable.push(new TextEncoder().encode("DEF")); - readable.unshift(new TextEncoder().encode("ABC")); - - assertStrictEquals(readable.read(), "ABCDEF"); -}); - -Deno.test("Duplex stream sets encoding correctly", () => { - const readable = new Duplex({ - read() {}, - }); - - readable.setEncoding("utf8"); - - readable.push(new TextEncoder().encode("DEF")); - readable.unshift(new TextEncoder().encode("ABC")); - - assertStrictEquals(readable.read(), "ABCDEF"); -}); - -Deno.test("Duplex stream holds up a big push", async () => { - let readExecuted = 0; - const readExecutedExpected = 3; - const readExpectedExecutions = deferred(); - - let endExecuted = 0; - const endExecutedExpected = 1; - const endExpectedExecutions = deferred(); - - const str = "asdfasdfasdfasdfasdf"; - - const r = new Duplex({ - highWaterMark: 5, - encoding: "utf8", - }); - - let reads = 0; - - function _read() { - if (reads === 0) { - setTimeout(() => { - r.push(str); - }, 1); - reads++; - } else if (reads === 1) { - const ret = r.push(str); - assertEquals(ret, false); - reads++; - } else { - r.push(null); - } - } - - r._read = () => { - readExecuted++; - if (readExecuted == readExecutedExpected) { - readExpectedExecutions.resolve(); - } - _read(); - }; - - r.on("end", () => { - endExecuted++; - if (endExecuted == endExecutedExpected) { - endExpectedExecutions.resolve(); - } - }); - - // Push some data in to start. - // We've never gotten any read event at this point. - const ret = r.push(str); - assert(!ret); - let chunk = r.read(); - assertEquals(chunk, str); - chunk = r.read(); - assertEquals(chunk, null); - - r.once("readable", () => { - // This time, we'll get *all* the remaining data, because - // it's been added synchronously, as the read WOULD take - // us below the hwm, and so it triggered a _read() again, - // which synchronously added more, which we then return. - chunk = r.read(); - assertEquals(chunk, str + str); - - chunk = r.read(); - assertEquals(chunk, null); - }); - - const readTimeout = setTimeout( - () => readExpectedExecutions.reject(), - 1000, - ); - const endTimeout = setTimeout( - () => endExpectedExecutions.reject(), - 1000, - ); - await readExpectedExecutions; - await endExpectedExecutions; - clearTimeout(readTimeout); - clearTimeout(endTimeout); - assertEquals(readExecuted, readExecutedExpected); - assertEquals(endExecuted, endExecutedExpected); -}); - -Deno.test("Duplex stream: 'readable' event is emitted but 'read' is not on highWaterMark length exceeded", async () => { - let readableExecuted = 0; - const readableExecutedExpected = 1; - const readableExpectedExecutions = deferred(); - - const r = new Duplex({ - highWaterMark: 3, - }); - - r._read = () => { - throw new Error("_read must not be called"); - }; - r.push(Buffer.from("blerg")); - - setTimeout(function () { - assert(!r._readableState.reading); - r.on("readable", () => { - readableExecuted++; - if (readableExecuted == readableExecutedExpected) { - readableExpectedExecutions.resolve(); - } - }); - }, 1); - - const readableTimeout = setTimeout( - () => readableExpectedExecutions.reject(), - 1000, - ); - await readableExpectedExecutions; - clearTimeout(readableTimeout); - assertEquals(readableExecuted, readableExecutedExpected); -}); - -Deno.test("Duplex stream: 'readable' and 'read' events are emitted on highWaterMark length not reached", async () => { - let readableExecuted = 0; - const readableExecutedExpected = 1; - const readableExpectedExecutions = deferred(); - - let readExecuted = 0; - const readExecutedExpected = 1; - const readExpectedExecutions = deferred(); - - const r = new Duplex({ - highWaterMark: 3, - }); - - r._read = () => { - readExecuted++; - if (readExecuted == readExecutedExpected) { - readExpectedExecutions.resolve(); - } - }; - - r.push(Buffer.from("bl")); - - setTimeout(function () { - assert(r._readableState.reading); - r.on("readable", () => { - readableExecuted++; - if (readableExecuted == readableExecutedExpected) { - readableExpectedExecutions.resolve(); - } - }); - }, 1); - - const readableTimeout = setTimeout( - () => readableExpectedExecutions.reject(), - 1000, - ); - const readTimeout = setTimeout( - () => readExpectedExecutions.reject(), - 1000, - ); - await readableExpectedExecutions; - await readExpectedExecutions; - clearTimeout(readableTimeout); - clearTimeout(readTimeout); - assertEquals(readableExecuted, readableExecutedExpected); - assertEquals(readExecuted, readExecutedExpected); -}); - -Deno.test("Duplex stream: 'readable' event is emitted but 'read' is not on highWaterMark length not reached and stream ended", async () => { - let readableExecuted = 0; - const readableExecutedExpected = 1; - const readableExpectedExecutions = deferred(); - - const r = new Duplex({ - highWaterMark: 30, - }); - - r._read = () => { - throw new Error("Must not be executed"); - }; - - r.push(Buffer.from("blerg")); - //This ends the stream and triggers end - r.push(null); - - setTimeout(function () { - // Assert we're testing what we think we are - assert(!r._readableState.reading); - r.on("readable", () => { - readableExecuted++; - if (readableExecuted == readableExecutedExpected) { - readableExpectedExecutions.resolve(); - } - }); - }, 1); - - const readableTimeout = setTimeout( - () => readableExpectedExecutions.reject(), - 1000, - ); - await readableExpectedExecutions; - clearTimeout(readableTimeout); - assertEquals(readableExecuted, readableExecutedExpected); -}); - -Deno.test("Duplex stream: 'read' is emitted on empty string pushed in non-object mode", async () => { - let endExecuted = 0; - const endExecutedExpected = 1; - const endExpectedExecutions = deferred(); - - const underlyingData = ["", "x", "y", "", "z"]; - const expected = underlyingData.filter((data) => data); - const result: unknown[] = []; - - const r = new Duplex({ - encoding: "utf8", - }); - r._read = function () { - queueMicrotask(() => { - if (!underlyingData.length) { - this.push(null); - } else { - this.push(underlyingData.shift()); - } - }); - }; - - r.on("readable", () => { - const data = r.read(); - if (data !== null) result.push(data); - }); - - r.on("end", () => { - endExecuted++; - if (endExecuted == endExecutedExpected) { - endExpectedExecutions.resolve(); - } - assertEquals(result, expected); - }); - - const endTimeout = setTimeout( - () => endExpectedExecutions.reject(), - 1000, - ); - await endExpectedExecutions; - clearTimeout(endTimeout); - assertEquals(endExecuted, endExecutedExpected); -}); - -Deno.test("Duplex stream: listeners can be removed", () => { - const r = new Duplex(); - r._read = () => {}; - r.on("data", () => {}); - - r.removeAllListeners("data"); - - assertEquals(r.eventNames().length, 0); -}); - -Deno.test("Duplex stream writes correctly", async () => { - let callback: undefined | ((error?: Error | null | undefined) => void); - - let writeExecuted = 0; - const writeExecutedExpected = 1; - const writeExpectedExecutions = deferred(); - - let writevExecuted = 0; - const writevExecutedExpected = 1; - const writevExpectedExecutions = deferred(); - - const writable = new Duplex({ - write: (chunk, encoding, cb) => { - writeExecuted++; - if (writeExecuted == writeExecutedExpected) { - writeExpectedExecutions.resolve(); - } - assert(chunk instanceof Buffer); - assertStrictEquals(encoding, "buffer"); - assertStrictEquals(String(chunk), "ABC"); - callback = cb; - }, - writev: (chunks) => { - writevExecuted++; - if (writevExecuted == writevExecutedExpected) { - writevExpectedExecutions.resolve(); - } - assertStrictEquals(chunks.length, 2); - assertStrictEquals(chunks[0].encoding, "buffer"); - assertStrictEquals(chunks[1].encoding, "buffer"); - assertStrictEquals(chunks[0].chunk + chunks[1].chunk, "DEFGHI"); - }, - }); - - writable.write(new TextEncoder().encode("ABC")); - writable.write(new TextEncoder().encode("DEF")); - writable.end(new TextEncoder().encode("GHI")); - callback?.(); - - const writeTimeout = setTimeout( - () => writeExpectedExecutions.reject(), - 1000, - ); - const writevTimeout = setTimeout( - () => writevExpectedExecutions.reject(), - 1000, - ); - await writeExpectedExecutions; - await writevExpectedExecutions; - clearTimeout(writeTimeout); - clearTimeout(writevTimeout); - assertEquals(writeExecuted, writeExecutedExpected); - assertEquals(writevExecuted, writevExecutedExpected); -}); - -Deno.test("Duplex stream writes Uint8Array in object mode", async () => { - let writeExecuted = 0; - const writeExecutedExpected = 1; - const writeExpectedExecutions = deferred(); - - const ABC = new TextEncoder().encode("ABC"); - - const writable = new Duplex({ - objectMode: true, - write: (chunk, encoding, cb) => { - writeExecuted++; - if (writeExecuted == writeExecutedExpected) { - writeExpectedExecutions.resolve(); - } - assert(!(chunk instanceof Buffer)); - assert(chunk instanceof Uint8Array); - assertEquals(chunk, ABC); - assertEquals(encoding, "utf8"); - cb(); - }, - }); - - writable.end(ABC); - - const writeTimeout = setTimeout( - () => writeExpectedExecutions.reject(), - 1000, - ); - await writeExpectedExecutions; - clearTimeout(writeTimeout); - assertEquals(writeExecuted, writeExecutedExpected); -}); - -Deno.test("Duplex stream throws on unexpected close", async () => { - let finishedExecuted = 0; - const finishedExecutedExpected = 1; - const finishedExpectedExecutions = deferred(); - - const writable = new Duplex({ - write: () => {}, - }); - writable.writable = false; - writable.destroy(); - - finished(writable, (err) => { - finishedExecuted++; - if (finishedExecuted == finishedExecutedExpected) { - finishedExpectedExecutions.resolve(); - } - assertEquals(err?.code, "ERR_STREAM_PREMATURE_CLOSE"); - }); - - const finishedTimeout = setTimeout( - () => finishedExpectedExecutions.reject(), - 1000, - ); - await finishedExpectedExecutions; - clearTimeout(finishedTimeout); - assertEquals(finishedExecuted, finishedExecutedExpected); -}); - -Deno.test("Duplex stream finishes correctly after error", async () => { - let errorExecuted = 0; - const errorExecutedExpected = 1; - const errorExpectedExecutions = deferred(); - - let finishedExecuted = 0; - const finishedExecutedExpected = 1; - const finishedExpectedExecutions = deferred(); - - const w = new Duplex({ - write(_chunk, _encoding, cb) { - cb(new Error()); - }, - autoDestroy: false, - }); - w.write("asd"); - w.on("error", () => { - errorExecuted++; - if (errorExecuted == errorExecutedExpected) { - errorExpectedExecutions.resolve(); - } - finished(w, () => { - finishedExecuted++; - if (finishedExecuted == finishedExecutedExpected) { - finishedExpectedExecutions.resolve(); - } - }); - }); - - const errorTimeout = setTimeout( - () => errorExpectedExecutions.reject(), - 1000, - ); - const finishedTimeout = setTimeout( - () => finishedExpectedExecutions.reject(), - 1000, - ); - await finishedExpectedExecutions; - await errorExpectedExecutions; - clearTimeout(finishedTimeout); - clearTimeout(errorTimeout); - assertEquals(finishedExecuted, finishedExecutedExpected); - assertEquals(errorExecuted, errorExecutedExpected); -}); - -Deno.test("Duplex stream fails on 'write' null value", () => { - const writable = new Duplex(); - assertThrows(() => writable.write(null)); -}); - -Deno.test("Duplex stream is destroyed correctly", async () => { - let closeExecuted = 0; - const closeExecutedExpected = 1; - const closeExpectedExecutions = deferred(); - - const unexpectedExecution = deferred(); - - const duplex = new Duplex({ - write(_chunk, _enc, cb) { - cb(); - }, - read() {}, - }); - - duplex.resume(); - - function never() { - unexpectedExecution.reject(); - } - - duplex.on("end", never); - duplex.on("finish", never); - duplex.on("close", () => { - closeExecuted++; - if (closeExecuted == closeExecutedExpected) { - closeExpectedExecutions.resolve(); - } - }); - - duplex.destroy(); - assertEquals(duplex.destroyed, true); - - const closeTimeout = setTimeout( - () => closeExpectedExecutions.reject(), - 1000, - ); - await Promise.race([ - unexpectedExecution, - delay(100), - ]); - await closeExpectedExecutions; - clearTimeout(closeTimeout); - assertEquals(closeExecuted, closeExecutedExpected); -}); - -Deno.test("Duplex stream errors correctly on destroy", async () => { - let errorExecuted = 0; - const errorExecutedExpected = 1; - const errorExpectedExecutions = deferred(); - - const unexpectedExecution = deferred(); - - const duplex = new Duplex({ - write(_chunk, _enc, cb) { - cb(); - }, - read() {}, - }); - duplex.resume(); - - const expected = new Error("kaboom"); - - function never() { - unexpectedExecution.reject(); - } - - duplex.on("end", never); - duplex.on("finish", never); - duplex.on("error", (err) => { - errorExecuted++; - if (errorExecuted == errorExecutedExpected) { - errorExpectedExecutions.resolve(); - } - assertStrictEquals(err, expected); - }); - - duplex.destroy(expected); - assertEquals(duplex.destroyed, true); - - const errorTimeout = setTimeout( - () => errorExpectedExecutions.reject(), - 1000, - ); - await Promise.race([ - unexpectedExecution, - delay(100), - ]); - await errorExpectedExecutions; - clearTimeout(errorTimeout); - assertEquals(errorExecuted, errorExecutedExpected); -}); - -Deno.test("Duplex stream doesn't finish on allowHalfOpen", async () => { - const unexpectedExecution = deferred(); - - const duplex = new Duplex({ - read() {}, - }); - - assertEquals(duplex.allowHalfOpen, true); - duplex.on("finish", () => unexpectedExecution.reject()); - assertEquals(duplex.listenerCount("end"), 0); - duplex.resume(); - duplex.push(null); - - await Promise.race([ - unexpectedExecution, - delay(100), - ]); -}); - -Deno.test("Duplex stream finishes when allowHalfOpen is disabled", async () => { - let finishExecuted = 0; - const finishExecutedExpected = 1; - const finishExpectedExecutions = deferred(); - - const duplex = new Duplex({ - read() {}, - allowHalfOpen: false, - }); - - assertEquals(duplex.allowHalfOpen, false); - duplex.on("finish", () => { - finishExecuted++; - if (finishExecuted == finishExecutedExpected) { - finishExpectedExecutions.resolve(); - } - }); - assertEquals(duplex.listenerCount("end"), 0); - duplex.resume(); - duplex.push(null); - - const finishTimeout = setTimeout( - () => finishExpectedExecutions.reject(), - 1000, - ); - await finishExpectedExecutions; - clearTimeout(finishTimeout); - assertEquals(finishExecuted, finishExecutedExpected); -}); - -Deno.test("Duplex stream doesn't finish when allowHalfOpen is disabled but stream ended", async () => { - const unexpectedExecution = deferred(); - - const duplex = new Duplex({ - read() {}, - allowHalfOpen: false, - }); - - assertEquals(duplex.allowHalfOpen, false); - duplex._writableState.ended = true; - duplex.on("finish", () => unexpectedExecution.reject()); - assertEquals(duplex.listenerCount("end"), 0); - duplex.resume(); - duplex.push(null); - - await Promise.race([ - unexpectedExecution, - delay(100), - ]); -}); diff --git a/std/node/_stream/end_of_stream.ts b/std/node/_stream/end_of_stream.ts deleted file mode 100644 index 6179e7fc42..0000000000 --- a/std/node/_stream/end_of_stream.ts +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { once } from "../_utils.ts"; -import type Duplex from "./duplex.ts"; -import type Readable from "./readable.ts"; -import type Stream from "./stream.ts"; -import type { ReadableState } from "./readable.ts"; -import type Writable from "./writable.ts"; -import type { WritableState } from "./writable.ts"; -import { - ERR_INVALID_ARG_TYPE, - ERR_STREAM_PREMATURE_CLOSE, - NodeErrorAbstraction, -} from "../_errors.ts"; - -export type StreamImplementations = Duplex | Readable | Stream | Writable; - -// TODO(Soremwar) -// Bring back once requests are implemented -// function isRequest(stream: Stream) { -// return stream.setHeader && typeof stream.abort === "function"; -// } - -// deno-lint-ignore no-explicit-any -function isReadable(stream: any) { - return typeof stream.readable === "boolean" || - typeof stream.readableEnded === "boolean" || - !!stream._readableState; -} - -// deno-lint-ignore no-explicit-any -function isWritable(stream: any) { - return typeof stream.writable === "boolean" || - typeof stream.writableEnded === "boolean" || - !!stream._writableState; -} - -function isWritableFinished(stream: Writable) { - if (stream.writableFinished) return true; - const wState = stream._writableState; - if (!wState || wState.errored) return false; - return wState.finished || (wState.ended && wState.length === 0); -} - -function nop() {} - -function isReadableEnded(stream: Readable) { - if (stream.readableEnded) return true; - const rState = stream._readableState; - if (!rState || rState.errored) return false; - return rState.endEmitted || (rState.ended && rState.length === 0); -} - -export interface FinishedOptions { - error?: boolean; - readable?: boolean; - writable?: boolean; -} - -/** - * Appends an ending callback triggered when a stream is no longer readable, - * writable or has experienced an error or a premature close event -*/ -export default function eos( - stream: StreamImplementations, - options: FinishedOptions | null, - callback: (err?: NodeErrorAbstraction | null) => void, -): () => void; -export default function eos( - stream: StreamImplementations, - callback: (err?: NodeErrorAbstraction | null) => void, -): () => void; -export default function eos( - stream: StreamImplementations, - x: FinishedOptions | ((err?: NodeErrorAbstraction | null) => void) | null, - y?: (err?: NodeErrorAbstraction | null) => void, -) { - let opts: FinishedOptions; - let callback: (err?: NodeErrorAbstraction | null) => void; - - if (!y) { - if (typeof x !== "function") { - throw new ERR_INVALID_ARG_TYPE("callback", "function", x); - } - opts = {}; - callback = x; - } else { - if (!x || Array.isArray(x) || typeof x !== "object") { - throw new ERR_INVALID_ARG_TYPE("opts", "object", x); - } - opts = x; - - if (typeof y !== "function") { - throw new ERR_INVALID_ARG_TYPE("callback", "function", y); - } - callback = y; - } - - callback = once(callback); - - const readable = opts.readable ?? isReadable(stream); - const writable = opts.writable ?? isWritable(stream); - - // deno-lint-ignore no-explicit-any - const wState: WritableState | undefined = (stream as any)._writableState; - // deno-lint-ignore no-explicit-any - const rState: ReadableState | undefined = (stream as any)._readableState; - const validState = wState || rState; - - const onlegacyfinish = () => { - if (!(stream as Writable).writable) { - onfinish(); - } - }; - - let willEmitClose = ( - validState?.autoDestroy && - validState?.emitClose && - validState?.closed === false && - isReadable(stream) === readable && - isWritable(stream) === writable - ); - - let writableFinished = (stream as Writable).writableFinished || - wState?.finished; - const onfinish = () => { - writableFinished = true; - // deno-lint-ignore no-explicit-any - if ((stream as any).destroyed) { - willEmitClose = false; - } - - if (willEmitClose && (!(stream as Readable).readable || readable)) { - return; - } - if (!readable || readableEnded) { - callback.call(stream); - } - }; - - let readableEnded = (stream as Readable).readableEnded || rState?.endEmitted; - const onend = () => { - readableEnded = true; - // deno-lint-ignore no-explicit-any - if ((stream as any).destroyed) { - willEmitClose = false; - } - - if (willEmitClose && (!(stream as Writable).writable || writable)) { - return; - } - if (!writable || writableFinished) { - callback.call(stream); - } - }; - - const onerror = (err: NodeErrorAbstraction) => { - callback.call(stream, err); - }; - - const onclose = () => { - if (readable && !readableEnded) { - if (!isReadableEnded(stream as Readable)) { - return callback.call(stream, new ERR_STREAM_PREMATURE_CLOSE()); - } - } - if (writable && !writableFinished) { - if (!isWritableFinished(stream as Writable)) { - return callback.call(stream, new ERR_STREAM_PREMATURE_CLOSE()); - } - } - callback.call(stream); - }; - - // TODO(Soremwar) - // Bring back once requests are implemented - // const onrequest = () => { - // stream.req.on("finish", onfinish); - // }; - - // TODO(Soremwar) - // Bring back once requests are implemented - // if (isRequest(stream)) { - // stream.on("complete", onfinish); - // stream.on("abort", onclose); - // if (stream.req) { - // onrequest(); - // } else { - // stream.on("request", onrequest); - // } - // } else - if (writable && !wState) { - stream.on("end", onlegacyfinish); - stream.on("close", onlegacyfinish); - } - - // TODO(Soremwar) - // Bring back once requests are implemented - // if (typeof stream.aborted === "boolean") { - // stream.on("aborted", onclose); - // } - - stream.on("end", onend); - stream.on("finish", onfinish); - if (opts.error !== false) stream.on("error", onerror); - stream.on("close", onclose); - - const closed = ( - wState?.closed || - rState?.closed || - wState?.errorEmitted || - rState?.errorEmitted || - // TODO(Soremwar) - // Bring back once requests are implemented - // (rState && stream.req && stream.aborted) || - ( - (!writable || wState?.finished) && - (!readable || rState?.endEmitted) - ) - ); - - if (closed) { - queueMicrotask(callback); - } - - return function () { - callback = nop; - stream.removeListener("aborted", onclose); - stream.removeListener("complete", onfinish); - stream.removeListener("abort", onclose); - // TODO(Soremwar) - // Bring back once requests are implemented - // stream.removeListener("request", onrequest); - // if (stream.req) stream.req.removeListener("finish", onfinish); - stream.removeListener("end", onlegacyfinish); - stream.removeListener("close", onlegacyfinish); - stream.removeListener("finish", onfinish); - stream.removeListener("end", onend); - stream.removeListener("error", onerror); - stream.removeListener("close", onclose); - }; -} diff --git a/std/node/_stream/end_of_stream_test.ts b/std/node/_stream/end_of_stream_test.ts deleted file mode 100644 index 571e75b99a..0000000000 --- a/std/node/_stream/end_of_stream_test.ts +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import finished from "./end_of_stream.ts"; -import Readable from "./readable.ts"; -import Transform from "./transform.ts"; -import Writable from "./writable.ts"; -import { mustCall } from "../_utils.ts"; -import { assert, fail } from "../../testing/asserts.ts"; -import { deferred, delay } from "../../async/mod.ts"; - -Deno.test("Finished appends to Readable correctly", async () => { - const rs = new Readable({ - read() {}, - }); - - const [finishedExecution, finishedCb] = mustCall((err) => { - assert(!err); - }); - - finished(rs, finishedCb); - - rs.push(null); - rs.resume(); - - await finishedExecution; -}); - -Deno.test("Finished appends to Writable correctly", async () => { - const ws = new Writable({ - write(_data, _enc, cb) { - cb(); - }, - }); - - const [finishedExecution, finishedCb] = mustCall((err) => { - assert(!err); - }); - - finished(ws, finishedCb); - - ws.end(); - - await finishedExecution; -}); - -Deno.test("Finished appends to Transform correctly", async () => { - const tr = new Transform({ - transform(_data, _enc, cb) { - cb(); - }, - }); - - let finish = false; - let ended = false; - - tr.on("end", () => { - ended = true; - }); - - tr.on("finish", () => { - finish = true; - }); - - const [finishedExecution, finishedCb] = mustCall((err) => { - assert(!err); - assert(finish); - assert(ended); - }); - - finished(tr, finishedCb); - - tr.end(); - tr.resume(); - - await finishedExecution; -}); - -Deno.test("The function returned by Finished clears the listeners", async () => { - const finishedExecution = deferred(); - - const ws = new Writable({ - write(_data, _env, cb) { - cb(); - }, - }); - - const removeListener = finished(ws, () => { - finishedExecution.reject(); - }); - removeListener(); - ws.end(); - - await Promise.race([ - delay(100), - finishedExecution, - ]) - .catch(() => fail("Finished was executed")); -}); diff --git a/std/node/_stream/from.ts b/std/node/_stream/from.ts deleted file mode 100644 index 652c177151..0000000000 --- a/std/node/_stream/from.ts +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { Buffer } from "../buffer.ts"; -import Readable from "./readable.ts"; -import type { ReadableOptions } from "./readable.ts"; -import { ERR_INVALID_ARG_TYPE, ERR_STREAM_NULL_VALUES } from "../_errors.ts"; - -export default function from( - // deno-lint-ignore no-explicit-any - iterable: Iterable | AsyncIterable, - opts?: ReadableOptions, -) { - let iterator: - // deno-lint-ignore no-explicit-any - | Iterator - // deno-lint-ignore no-explicit-any - | AsyncIterator; - if (typeof iterable === "string" || iterable instanceof Buffer) { - return new Readable({ - objectMode: true, - ...opts, - read() { - this.push(iterable); - this.push(null); - }, - }); - } - - if (Symbol.asyncIterator in iterable) { - // deno-lint-ignore no-explicit-any - iterator = (iterable as AsyncIterable)[Symbol.asyncIterator](); - } else if (Symbol.iterator in iterable) { - // deno-lint-ignore no-explicit-any - iterator = (iterable as Iterable)[Symbol.iterator](); - } else { - throw new ERR_INVALID_ARG_TYPE("iterable", ["Iterable"], iterable); - } - - const readable = new Readable({ - objectMode: true, - highWaterMark: 1, - ...opts, - }); - - // Reading boolean to protect against _read - // being called before last iteration completion. - let reading = false; - - // needToClose boolean if iterator needs to be explicitly closed - let needToClose = false; - - readable._read = function () { - if (!reading) { - reading = true; - next(); - } - }; - - readable._destroy = function (error, cb) { - if (needToClose) { - needToClose = false; - close().then( - () => queueMicrotask(() => cb(error)), - (e) => queueMicrotask(() => cb(error || e)), - ); - } else { - cb(error); - } - }; - - async function close() { - if (typeof iterator.return === "function") { - const { value } = await iterator.return(); - await value; - } - } - - async function next() { - try { - needToClose = false; - const { value, done } = await iterator.next(); - needToClose = !done; - if (done) { - readable.push(null); - } else if (readable.destroyed) { - await close(); - } else { - const res = await value; - if (res === null) { - reading = false; - throw new ERR_STREAM_NULL_VALUES(); - } else if (readable.push(res)) { - next(); - } else { - reading = false; - } - } - } catch (err) { - readable.destroy(err); - } - } - return readable; -} diff --git a/std/node/_stream/passthrough.ts b/std/node/_stream/passthrough.ts deleted file mode 100644 index 9126420e53..0000000000 --- a/std/node/_stream/passthrough.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import Transform from "./transform.ts"; -import type { TransformOptions } from "./transform.ts"; -import type { Encodings } from "../_utils.ts"; - -export default class PassThrough extends Transform { - constructor(options?: TransformOptions) { - super(options); - } - - _transform( - // deno-lint-ignore no-explicit-any - chunk: any, - _encoding: Encodings, - // deno-lint-ignore no-explicit-any - cb: (error?: Error | null, data?: any) => void, - ) { - cb(null, chunk); - } -} diff --git a/std/node/_stream/pipeline.ts b/std/node/_stream/pipeline.ts deleted file mode 100644 index d02a928701..0000000000 --- a/std/node/_stream/pipeline.ts +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { once } from "../_utils.ts"; -import { destroyer as implDestroyer } from "./destroy.ts"; -import eos from "./end_of_stream.ts"; -import createReadableStreamAsyncIterator from "./async_iterator.ts"; -import * as events from "../events.ts"; -import PassThrough from "./passthrough.ts"; -import { - ERR_INVALID_ARG_TYPE, - ERR_INVALID_CALLBACK, - ERR_INVALID_RETURN_VALUE, - ERR_MISSING_ARGS, - ERR_STREAM_DESTROYED, - NodeErrorAbstraction, -} from "../_errors.ts"; -import type Duplex from "./duplex.ts"; -import type Readable from "./readable.ts"; -import type Stream from "./stream.ts"; -import type Transform from "./transform.ts"; -import type Writable from "./writable.ts"; - -type Streams = Duplex | Readable | Writable; -// deno-lint-ignore no-explicit-any -type EndCallback = (err?: NodeErrorAbstraction | null, val?: any) => void; -type TransformCallback = - // deno-lint-ignore no-explicit-any - | ((value?: any) => AsyncGenerator) - // deno-lint-ignore no-explicit-any - | ((value?: any) => Promise); -/** - * This type represents an array that contains a data source, - * many Transform Streams, a writable stream destination - * and end in an optional callback - * */ -type DataSource = - // deno-lint-ignore no-explicit-any - | (() => AsyncGenerator) - | // deno-lint-ignore no-explicit-any - AsyncIterable - | Duplex - | // deno-lint-ignore no-explicit-any - Iterable - | // deno-lint-ignore no-explicit-any - (() => Generator) - | Readable; -type Transformers = Duplex | Transform | TransformCallback | Writable; -export type PipelineArguments = [ - DataSource, - ...Array, -]; - -function destroyer( - stream: Streams, - reading: boolean, - writing: boolean, - callback: EndCallback, -) { - callback = once(callback); - - let finished = false; - stream.on("close", () => { - finished = true; - }); - - eos(stream, { readable: reading, writable: writing }, (err) => { - finished = !err; - - // deno-lint-ignore no-explicit-any - const rState = (stream as any)?._readableState; - if ( - err && - err.code === "ERR_STREAM_PREMATURE_CLOSE" && - reading && - (rState?.ended && !rState?.errored && !rState?.errorEmitted) - ) { - stream - .once("end", callback) - .once("error", callback); - } else { - callback(err); - } - }); - - return (err: NodeErrorAbstraction) => { - if (finished) return; - finished = true; - implDestroyer(stream, err); - callback(err || new ERR_STREAM_DESTROYED("pipe")); - }; -} - -function popCallback(streams: PipelineArguments): EndCallback { - if (typeof streams[streams.length - 1] !== "function") { - throw new ERR_INVALID_CALLBACK(streams[streams.length - 1]); - } - return streams.pop() as EndCallback; -} - -// function isPromise(obj) { -// return !!(obj && typeof obj.then === "function"); -// } - -// deno-lint-ignore no-explicit-any -function isReadable(obj: any): obj is Stream { - return !!(obj && typeof obj.pipe === "function"); -} - -// deno-lint-ignore no-explicit-any -function isWritable(obj: any) { - return !!(obj && typeof obj.write === "function"); -} - -// deno-lint-ignore no-explicit-any -function isStream(obj: any) { - return isReadable(obj) || isWritable(obj); -} - -// deno-lint-ignore no-explicit-any -function isIterable(obj: any, isAsync?: boolean) { - if (!obj) return false; - if (isAsync === true) return typeof obj[Symbol.asyncIterator] === "function"; - if (isAsync === false) return typeof obj[Symbol.iterator] === "function"; - return typeof obj[Symbol.asyncIterator] === "function" || - typeof obj[Symbol.iterator] === "function"; -} - -// deno-lint-ignore no-explicit-any -function makeAsyncIterable(val: Readable | Iterable | AsyncIterable) { - if (isIterable(val)) { - return val; - } else if (isReadable(val)) { - return fromReadable(val as Readable); - } - throw new ERR_INVALID_ARG_TYPE( - "val", - ["Readable", "Iterable", "AsyncIterable"], - val, - ); -} - -async function* fromReadable(val: Readable) { - yield* createReadableStreamAsyncIterator(val); -} - -async function pump( - // deno-lint-ignore no-explicit-any - iterable: Iterable, - writable: Duplex | Writable, - finish: (err?: NodeErrorAbstraction | null) => void, -) { - let error; - try { - for await (const chunk of iterable) { - if (!writable.write(chunk)) { - if (writable.destroyed) return; - await events.once(writable, "drain"); - } - } - writable.end(); - } catch (err) { - error = err; - } finally { - finish(error); - } -} - -export default function pipeline(...args: PipelineArguments) { - const callback: EndCallback = once(popCallback(args)); - - let streams: [DataSource, ...Transformers[]]; - if (args.length > 1) { - streams = args as [DataSource, ...Transformers[]]; - } else { - throw new ERR_MISSING_ARGS("streams"); - } - - let error: NodeErrorAbstraction; - // deno-lint-ignore no-explicit-any - let value: any; - const destroys: Array<(err: NodeErrorAbstraction) => void> = []; - - let finishCount = 0; - - function finish(err?: NodeErrorAbstraction | null) { - const final = --finishCount === 0; - - if (err && (!error || error.code === "ERR_STREAM_PREMATURE_CLOSE")) { - error = err; - } - - if (!error && !final) { - return; - } - - while (destroys.length) { - (destroys.shift() as (err: NodeErrorAbstraction) => void)(error); - } - - if (final) { - callback(error, value); - } - } - - // TODO(Soremwar) - // Simplify the hell out of this - // deno-lint-ignore no-explicit-any - let ret: any; - for (let i = 0; i < streams.length; i++) { - const stream = streams[i]; - const reading = i < streams.length - 1; - const writing = i > 0; - - if (isStream(stream)) { - finishCount++; - destroys.push(destroyer(stream as Streams, reading, writing, finish)); - } - - if (i === 0) { - if (typeof stream === "function") { - ret = stream(); - if (!isIterable(ret)) { - throw new ERR_INVALID_RETURN_VALUE( - "Iterable, AsyncIterable or Stream", - "source", - ret, - ); - } - } else if (isIterable(stream) || isReadable(stream)) { - ret = stream; - } else { - throw new ERR_INVALID_ARG_TYPE( - "source", - ["Stream", "Iterable", "AsyncIterable", "Function"], - stream, - ); - } - } else if (typeof stream === "function") { - ret = makeAsyncIterable(ret); - ret = stream(ret); - - if (reading) { - if (!isIterable(ret, true)) { - throw new ERR_INVALID_RETURN_VALUE( - "AsyncIterable", - `transform[${i - 1}]`, - ret, - ); - } - } else { - // If the last argument to pipeline is not a stream - // we must create a proxy stream so that pipeline(...) - // always returns a stream which can be further - // composed through `.pipe(stream)`. - const pt = new PassThrough({ - objectMode: true, - }); - if (ret instanceof Promise) { - ret - .then((val) => { - value = val; - pt.end(val); - }, (err) => { - pt.destroy(err); - }); - } else if (isIterable(ret, true)) { - finishCount++; - pump(ret, pt, finish); - } else { - throw new ERR_INVALID_RETURN_VALUE( - "AsyncIterable or Promise", - "destination", - ret, - ); - } - - ret = pt; - - finishCount++; - destroys.push(destroyer(ret, false, true, finish)); - } - } else if (isStream(stream)) { - if (isReadable(ret)) { - ret.pipe(stream as Readable); - - // TODO(Soremwar) - // Reimplement after stdout and stderr are implemented - // if (stream === process.stdout || stream === process.stderr) { - // ret.on("end", () => stream.end()); - // } - } else { - ret = makeAsyncIterable(ret); - - finishCount++; - pump(ret, stream as Writable, finish); - } - ret = stream; - } else { - const name = reading ? `transform[${i - 1}]` : "destination"; - throw new ERR_INVALID_ARG_TYPE( - name, - ["Stream", "Function"], - ret, - ); - } - } - - return ret as unknown as Readable; -} diff --git a/std/node/_stream/pipeline_test.ts b/std/node/_stream/pipeline_test.ts deleted file mode 100644 index aa18694160..0000000000 --- a/std/node/_stream/pipeline_test.ts +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { Buffer } from "../buffer.ts"; -import PassThrough from "./passthrough.ts"; -import pipeline from "./pipeline.ts"; -import Readable from "./readable.ts"; -import Transform from "./transform.ts"; -import Writable from "./writable.ts"; -import { mustCall } from "../_utils.ts"; -import { - assert, - assertEquals, - assertStrictEquals, -} from "../../testing/asserts.ts"; -import type { NodeErrorAbstraction } from "../_errors.ts"; - -Deno.test("Pipeline ends on stream finished", async () => { - let finished = false; - - // deno-lint-ignore no-explicit-any - const processed: any[] = []; - const expected = [ - Buffer.from("a"), - Buffer.from("b"), - Buffer.from("c"), - ]; - - const read = new Readable({ - read() {}, - }); - - const write = new Writable({ - write(data, _enc, cb) { - processed.push(data); - cb(); - }, - }); - - write.on("finish", () => { - finished = true; - }); - - for (let i = 0; i < expected.length; i++) { - read.push(expected[i]); - } - read.push(null); - - const [finishedCompleted, finishedCb] = mustCall( - (err?: NodeErrorAbstraction | null) => { - assert(!err); - assert(finished); - assertEquals(processed, expected); - }, - 1, - ); - - pipeline(read, write, finishedCb); - - await finishedCompleted; -}); - -Deno.test("Pipeline fails on stream destroyed", async () => { - const read = new Readable({ - read() {}, - }); - - const write = new Writable({ - write(_data, _enc, cb) { - cb(); - }, - }); - - read.push("data"); - queueMicrotask(() => read.destroy()); - - const [pipelineExecuted, pipelineCb] = mustCall( - (err?: NodeErrorAbstraction | null) => { - assert(err); - }, - 1, - ); - pipeline(read, write, pipelineCb); - - await pipelineExecuted; -}); - -Deno.test("Pipeline exits on stream error", async () => { - const read = new Readable({ - read() {}, - }); - - const transform = new Transform({ - transform(_data, _enc, cb) { - cb(new Error("kaboom")); - }, - }); - - const write = new Writable({ - write(_data, _enc, cb) { - cb(); - }, - }); - - const [readExecution, readCb] = mustCall(); - read.on("close", readCb); - const [closeExecution, closeCb] = mustCall(); - transform.on("close", closeCb); - const [writeExecution, writeCb] = mustCall(); - write.on("close", writeCb); - - const errorExecutions = [read, transform, write] - .map((stream) => { - const [execution, cb] = mustCall((err?: NodeErrorAbstraction | null) => { - assertEquals(err, new Error("kaboom")); - }); - - stream.on("error", cb); - return execution; - }); - - const [pipelineExecution, pipelineCb] = mustCall( - (err?: NodeErrorAbstraction | null) => { - assertEquals(err, new Error("kaboom")); - }, - ); - const dst = pipeline(read, transform, write, pipelineCb); - - assertStrictEquals(dst, write); - - read.push("hello"); - - await readExecution; - await closeExecution; - await writeExecution; - await Promise.all(errorExecutions); - await pipelineExecution; -}); - -Deno.test("Pipeline processes iterators correctly", async () => { - let res = ""; - const w = new Writable({ - write(chunk, _encoding, callback) { - res += chunk; - callback(); - }, - }); - - const [pipelineExecution, pipelineCb] = mustCall( - (err?: NodeErrorAbstraction | null) => { - assert(!err); - assertEquals(res, "helloworld"); - }, - ); - pipeline( - function* () { - yield "hello"; - yield "world"; - }(), - w, - pipelineCb, - ); - - await pipelineExecution; -}); - -Deno.test("Pipeline processes async iterators correctly", async () => { - let res = ""; - const w = new Writable({ - write(chunk, _encoding, callback) { - res += chunk; - callback(); - }, - }); - - const [pipelineExecution, pipelineCb] = mustCall( - (err?: NodeErrorAbstraction | null) => { - assert(!err); - assertEquals(res, "helloworld"); - }, - ); - pipeline( - async function* () { - await Promise.resolve(); - yield "hello"; - yield "world"; - }(), - w, - pipelineCb, - ); - - await pipelineExecution; -}); - -Deno.test("Pipeline processes generators correctly", async () => { - let res = ""; - const w = new Writable({ - write(chunk, _encoding, callback) { - res += chunk; - callback(); - }, - }); - - const [pipelineExecution, pipelineCb] = mustCall( - (err?: NodeErrorAbstraction | null) => { - assert(!err); - assertEquals(res, "helloworld"); - }, - ); - pipeline( - function* () { - yield "hello"; - yield "world"; - }, - w, - pipelineCb, - ); - - await pipelineExecution; -}); - -Deno.test("Pipeline processes async generators correctly", async () => { - let res = ""; - const w = new Writable({ - write(chunk, _encoding, callback) { - res += chunk; - callback(); - }, - }); - - const [pipelineExecution, pipelineCb] = mustCall( - (err?: NodeErrorAbstraction | null) => { - assert(!err); - assertEquals(res, "helloworld"); - }, - ); - pipeline( - async function* () { - await Promise.resolve(); - yield "hello"; - yield "world"; - }, - w, - pipelineCb, - ); - - await pipelineExecution; -}); - -Deno.test("Pipeline handles generator transforms", async () => { - let res = ""; - - const [pipelineExecuted, pipelineCb] = mustCall( - (err?: NodeErrorAbstraction | null) => { - assert(!err); - assertEquals(res, "HELLOWORLD"); - }, - ); - pipeline( - async function* () { - await Promise.resolve(); - yield "hello"; - yield "world"; - }, - async function* (source: string[]) { - for await (const chunk of source) { - yield chunk.toUpperCase(); - } - }, - async function (source: string[]) { - for await (const chunk of source) { - res += chunk; - } - }, - pipelineCb, - ); - - await pipelineExecuted; -}); - -Deno.test("Pipeline passes result to final callback", async () => { - const [pipelineExecuted, pipelineCb] = mustCall( - (err?: NodeErrorAbstraction | null, val?: unknown) => { - assert(!err); - assertEquals(val, "HELLOWORLD"); - }, - ); - pipeline( - async function* () { - await Promise.resolve(); - yield "hello"; - yield "world"; - }, - async function* (source: string[]) { - for await (const chunk of source) { - yield chunk.toUpperCase(); - } - }, - async function (source: string[]) { - let ret = ""; - for await (const chunk of source) { - ret += chunk; - } - return ret; - }, - pipelineCb, - ); - - await pipelineExecuted; -}); - -Deno.test("Pipeline returns a stream after ending", async () => { - const [pipelineExecuted, pipelineCb] = mustCall( - (err?: NodeErrorAbstraction | null) => { - assertEquals(err, undefined); - }, - ); - const ret = pipeline( - async function* () { - await Promise.resolve(); - yield "hello"; - }, - // deno-lint-ignore require-yield - async function* (source: string[]) { - for await (const chunk of source) { - chunk; - } - }, - pipelineCb, - ); - - ret.resume(); - - assertEquals(typeof ret.pipe, "function"); - - await pipelineExecuted; -}); - -Deno.test("Pipeline returns a stream after erroring", async () => { - const errorText = "kaboom"; - - const [pipelineExecuted, pipelineCb] = mustCall( - (err?: NodeErrorAbstraction | null) => { - assertEquals(err?.message, errorText); - }, - ); - const ret = pipeline( - // deno-lint-ignore require-yield - async function* () { - await Promise.resolve(); - throw new Error(errorText); - }, - // deno-lint-ignore require-yield - async function* (source: string[]) { - for await (const chunk of source) { - chunk; - } - }, - pipelineCb, - ); - - ret.resume(); - - assertEquals(typeof ret.pipe, "function"); - - await pipelineExecuted; -}); - -Deno.test("Pipeline destination gets destroyed on error", async () => { - const errorText = "kaboom"; - const s = new PassThrough(); - - const [pipelineExecution, pipelineCb] = mustCall( - (err?: NodeErrorAbstraction | null) => { - assertEquals(err?.message, errorText); - assertEquals(s.destroyed, true); - }, - ); - pipeline( - // deno-lint-ignore require-yield - async function* () { - throw new Error(errorText); - }, - s, - pipelineCb, - ); - - await pipelineExecution; -}); diff --git a/std/node/_stream/promises.ts b/std/node/_stream/promises.ts deleted file mode 100644 index 1adf4ea3f5..0000000000 --- a/std/node/_stream/promises.ts +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import pl from "./pipeline.ts"; -import type { PipelineArguments } from "./pipeline.ts"; -import eos from "./end_of_stream.ts"; -import type { - FinishedOptions, - StreamImplementations as FinishedStreams, -} from "./end_of_stream.ts"; - -export function pipeline(...streams: PipelineArguments) { - return new Promise((resolve, reject) => { - pl( - ...streams, - (err, value) => { - if (err) { - reject(err); - } else { - resolve(value); - } - }, - ); - }); -} - -export function finished( - stream: FinishedStreams, - opts?: FinishedOptions, -) { - return new Promise((resolve, reject) => { - eos( - stream, - opts || null, - (err) => { - if (err) { - reject(err); - } else { - resolve(); - } - }, - ); - }); -} diff --git a/std/node/_stream/promises_test.ts b/std/node/_stream/promises_test.ts deleted file mode 100644 index 90803b4af4..0000000000 --- a/std/node/_stream/promises_test.ts +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { Buffer } from "../buffer.ts"; -import Readable from "./readable.ts"; -import Writable from "./writable.ts"; -import { pipeline } from "./promises.ts"; -import { deferred } from "../../async/mod.ts"; -import { - assert, - assertEquals, - assertThrowsAsync, -} from "../../testing/asserts.ts"; - -Deno.test("Promise pipeline works correctly", async () => { - let pipelineExecuted = 0; - const pipelineExecutedExpected = 1; - const pipelineExpectedExecutions = deferred(); - - let finished = false; - // deno-lint-ignore no-explicit-any - const processed: any[] = []; - const expected = [ - Buffer.from("a"), - Buffer.from("b"), - Buffer.from("c"), - ]; - - const read = new Readable({ - read() {}, - }); - - const write = new Writable({ - write(data, _enc, cb) { - processed.push(data); - cb(); - }, - }); - - write.on("finish", () => { - finished = true; - }); - - for (let i = 0; i < expected.length; i++) { - read.push(expected[i]); - } - read.push(null); - - pipeline(read, write).then(() => { - pipelineExecuted++; - if (pipelineExecuted == pipelineExecutedExpected) { - pipelineExpectedExecutions.resolve(); - } - assert(finished); - assertEquals(processed, expected); - }); - - const pipelineTimeout = setTimeout( - () => pipelineExpectedExecutions.reject(), - 1000, - ); - await pipelineExpectedExecutions; - clearTimeout(pipelineTimeout); - assertEquals(pipelineExecuted, pipelineExecutedExpected); -}); - -Deno.test("Promise pipeline throws on readable destroyed", async () => { - const read = new Readable({ - read() {}, - }); - - const write = new Writable({ - write(_data, _enc, cb) { - cb(); - }, - }); - - read.push("data"); - read.destroy(); - - await assertThrowsAsync( - () => pipeline(read, write), - Error, - "Premature close", - ); -}); diff --git a/std/node/_stream/readable.ts b/std/node/_stream/readable.ts deleted file mode 100644 index 54e0d8ecdf..0000000000 --- a/std/node/_stream/readable.ts +++ /dev/null @@ -1,788 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { captureRejectionSymbol } from "../events.ts"; -import Stream from "./stream.ts"; -import type { Buffer } from "../buffer.ts"; -import BufferList from "./buffer_list.ts"; -import { - ERR_INVALID_OPT_VALUE, - ERR_METHOD_NOT_IMPLEMENTED, -} from "../_errors.ts"; -import type { Encodings } from "../_utils.ts"; -import { StringDecoder } from "../string_decoder.ts"; -import createReadableStreamAsyncIterator from "./async_iterator.ts"; -import streamFrom from "./from.ts"; -import { kDestroy, kPaused } from "./symbols.ts"; -import { - _destroy, - computeNewHighWaterMark, - emitReadable, - endReadable, - errorOrDestroy, - fromList, - howMuchToRead, - nReadingNextTick, - pipeOnDrain, - prependListener, - readableAddChunk, - resume, - updateReadableListening, -} from "./readable_internal.ts"; -import Writable from "./writable.ts"; -import { errorOrDestroy as errorOrDestroyWritable } from "./writable_internal.ts"; -import Duplex, { errorOrDestroy as errorOrDestroyDuplex } from "./duplex.ts"; - -export interface ReadableOptions { - autoDestroy?: boolean; - defaultEncoding?: Encodings; - destroy?( - this: Readable, - error: Error | null, - callback: (error: Error | null) => void, - ): void; - emitClose?: boolean; - encoding?: Encodings; - highWaterMark?: number; - objectMode?: boolean; - read?(this: Readable): void; -} - -export class ReadableState { - [kPaused]: boolean | null = null; - awaitDrainWriters: Duplex | Writable | Set | null = null; - buffer = new BufferList(); - closed = false; - closeEmitted = false; - constructed: boolean; - decoder: StringDecoder | null = null; - destroyed = false; - emittedReadable = false; - encoding: Encodings | null = null; - ended = false; - endEmitted = false; - errored: Error | null = null; - errorEmitted = false; - flowing: boolean | null = null; - highWaterMark: number; - length = 0; - multiAwaitDrain = false; - needReadable = false; - objectMode: boolean; - pipes: Array = []; - readable = true; - readableListening = false; - reading = false; - readingMore = false; - resumeScheduled = false; - sync = true; - emitClose: boolean; - autoDestroy: boolean; - defaultEncoding: string; - - constructor(options?: ReadableOptions) { - this.objectMode = !!options?.objectMode; - - this.highWaterMark = options?.highWaterMark ?? - (this.objectMode ? 16 : 16 * 1024); - if (Number.isInteger(this.highWaterMark) && this.highWaterMark >= 0) { - this.highWaterMark = Math.floor(this.highWaterMark); - } else { - throw new ERR_INVALID_OPT_VALUE("highWaterMark", this.highWaterMark); - } - - this.emitClose = options?.emitClose ?? true; - this.autoDestroy = options?.autoDestroy ?? true; - this.defaultEncoding = options?.defaultEncoding || "utf8"; - - if (options?.encoding) { - this.decoder = new StringDecoder(options.encoding); - this.encoding = options.encoding; - } - - this.constructed = true; - } -} - -class Readable extends Stream { - _readableState: ReadableState; - - constructor(options?: ReadableOptions) { - super(); - if (options) { - if (typeof options.read === "function") { - this._read = options.read; - } - if (typeof options.destroy === "function") { - this._destroy = options.destroy; - } - } - this._readableState = new ReadableState(options); - } - - static from( - // deno-lint-ignore no-explicit-any - iterable: Iterable | AsyncIterable, - opts?: ReadableOptions, - ): Readable { - return streamFrom(iterable, opts); - } - - static ReadableState = ReadableState; - - static _fromList = fromList; - - // You can override either this method, or the async _read(n) below. - read(n?: number) { - // Same as parseInt(undefined, 10), however V8 7.3 performance regressed - // in this scenario, so we are doing it manually. - if (n === undefined) { - n = NaN; - } - const state = this._readableState; - const nOrig = n; - - if (n > state.highWaterMark) { - state.highWaterMark = computeNewHighWaterMark(n); - } - - if (n !== 0) { - state.emittedReadable = false; - } - - if ( - n === 0 && - state.needReadable && - ((state.highWaterMark !== 0 - ? state.length >= state.highWaterMark - : state.length > 0) || - state.ended) - ) { - if (state.length === 0 && state.ended) { - endReadable(this); - } else { - emitReadable(this); - } - return null; - } - - n = howMuchToRead(n, state); - - if (n === 0 && state.ended) { - if (state.length === 0) { - endReadable(this); - } - return null; - } - - let doRead = state.needReadable; - if ( - state.length === 0 || state.length - (n as number) < state.highWaterMark - ) { - doRead = true; - } - - if ( - state.ended || state.reading || state.destroyed || state.errored || - !state.constructed - ) { - doRead = false; - } else if (doRead) { - state.reading = true; - state.sync = true; - if (state.length === 0) { - state.needReadable = true; - } - this._read(); - state.sync = false; - if (!state.reading) { - n = howMuchToRead(nOrig, state); - } - } - - let ret; - if ((n as number) > 0) { - ret = fromList((n as number), state); - } else { - ret = null; - } - - if (ret === null) { - state.needReadable = state.length <= state.highWaterMark; - n = 0; - } else { - state.length -= n as number; - if (state.multiAwaitDrain) { - (state.awaitDrainWriters as Set).clear(); - } else { - state.awaitDrainWriters = null; - } - } - - if (state.length === 0) { - if (!state.ended) { - state.needReadable = true; - } - - if (nOrig !== n && state.ended) { - endReadable(this); - } - } - - if (ret !== null) { - this.emit("data", ret); - } - - return ret; - } - - _read(_size?: number) { - throw new ERR_METHOD_NOT_IMPLEMENTED("_read()"); - } - - pipe(dest: T, pipeOpts?: { end?: boolean }): T { - // deno-lint-ignore no-this-alias - const src = this; - const state = this._readableState; - - if (state.pipes.length === 1) { - if (!state.multiAwaitDrain) { - state.multiAwaitDrain = true; - state.awaitDrainWriters = new Set( - state.awaitDrainWriters ? [state.awaitDrainWriters as Writable] : [], - ); - } - } - - state.pipes.push(dest); - - const doEnd = (!pipeOpts || pipeOpts.end !== false); - - //TODO(Soremwar) - //Part of doEnd condition - //In node, output/input are a duplex Stream - // && - // dest !== stdout && - // dest !== stderr - - const endFn = doEnd ? onend : unpipe; - if (state.endEmitted) { - queueMicrotask(endFn); - } else { - this.once("end", endFn); - } - - dest.on("unpipe", onunpipe); - function onunpipe(readable: Readable, unpipeInfo: { hasUnpiped: boolean }) { - if (readable === src) { - if (unpipeInfo && unpipeInfo.hasUnpiped === false) { - unpipeInfo.hasUnpiped = true; - cleanup(); - } - } - } - - function onend() { - dest.end(); - } - - let ondrain: () => void; - - let cleanedUp = false; - function cleanup() { - dest.removeListener("close", onclose); - dest.removeListener("finish", onfinish); - if (ondrain) { - dest.removeListener("drain", ondrain); - } - dest.removeListener("error", onerror); - dest.removeListener("unpipe", onunpipe); - src.removeListener("end", onend); - src.removeListener("end", unpipe); - src.removeListener("data", ondata); - - cleanedUp = true; - if ( - ondrain && state.awaitDrainWriters && - (!dest._writableState || dest._writableState.needDrain) - ) { - ondrain(); - } - } - - this.on("data", ondata); - // deno-lint-ignore no-explicit-any - function ondata(chunk: any) { - const ret = dest.write(chunk); - if (ret === false) { - if (!cleanedUp) { - if (state.pipes.length === 1 && state.pipes[0] === dest) { - state.awaitDrainWriters = dest; - state.multiAwaitDrain = false; - } else if (state.pipes.length > 1 && state.pipes.includes(dest)) { - (state.awaitDrainWriters as Set).add(dest); - } - src.pause(); - } - if (!ondrain) { - ondrain = pipeOnDrain(src, dest); - dest.on("drain", ondrain); - } - } - } - - function onerror(er: Error) { - unpipe(); - dest.removeListener("error", onerror); - if (dest.listenerCount("error") === 0) { - const s = dest._writableState || (dest as Duplex)._readableState; - if (s && !s.errorEmitted) { - if (dest instanceof Duplex) { - errorOrDestroyDuplex(dest as unknown as Duplex, er); - } else { - errorOrDestroyWritable(dest as Writable, er); - } - } else { - dest.emit("error", er); - } - } - } - - prependListener(dest, "error", onerror); - - function onclose() { - dest.removeListener("finish", onfinish); - unpipe(); - } - dest.once("close", onclose); - function onfinish() { - dest.removeListener("close", onclose); - unpipe(); - } - dest.once("finish", onfinish); - - function unpipe() { - src.unpipe(dest as Writable); - } - - dest.emit("pipe", this); - - if (!state.flowing) { - this.resume(); - } - - return dest; - } - - isPaused() { - return this._readableState[kPaused] === true || - this._readableState.flowing === false; - } - - setEncoding(enc: Encodings) { - const decoder = new StringDecoder(enc); - this._readableState.decoder = decoder; - this._readableState.encoding = this._readableState.decoder - .encoding as Encodings; - - const buffer = this._readableState.buffer; - let content = ""; - for (const data of buffer) { - content += decoder.write(data as Buffer); - } - buffer.clear(); - if (content !== "") { - buffer.push(content); - } - this._readableState.length = content.length; - return this; - } - - on( - event: "close" | "end" | "pause" | "readable" | "resume", - listener: () => void, - ): this; - // deno-lint-ignore no-explicit-any - on(event: "data", listener: (chunk: any) => void): this; - on(event: "error", listener: (err: Error) => void): this; - // deno-lint-ignore no-explicit-any - on(event: string | symbol, listener: (...args: any[]) => void): this; - on( - ev: string | symbol, - fn: - | (() => void) - // deno-lint-ignore no-explicit-any - | ((chunk: any) => void) - | ((err: Error) => void) - // deno-lint-ignore no-explicit-any - | ((...args: any[]) => void), - ) { - const res = super.on.call(this, ev, fn); - const state = this._readableState; - - if (ev === "data") { - state.readableListening = this.listenerCount("readable") > 0; - - if (state.flowing !== false) { - this.resume(); - } - } else if (ev === "readable") { - if (!state.endEmitted && !state.readableListening) { - state.readableListening = state.needReadable = true; - state.flowing = false; - state.emittedReadable = false; - if (state.length) { - emitReadable(this); - } else if (!state.reading) { - queueMicrotask(() => nReadingNextTick(this)); - } - } - } - - return res; - } - - removeListener( - event: "close" | "end" | "pause" | "readable" | "resume", - listener: () => void, - ): this; - // deno-lint-ignore no-explicit-any - removeListener(event: "data", listener: (chunk: any) => void): this; - removeListener(event: "error", listener: (err: Error) => void): this; - removeListener( - event: string | symbol, - // deno-lint-ignore no-explicit-any - listener: (...args: any[]) => void, - ): this; - removeListener( - ev: string | symbol, - fn: - | (() => void) - // deno-lint-ignore no-explicit-any - | ((chunk: any) => void) - | ((err: Error) => void) - // deno-lint-ignore no-explicit-any - | ((...args: any[]) => void), - ) { - const res = super.removeListener.call(this, ev, fn); - - if (ev === "readable") { - queueMicrotask(() => updateReadableListening(this)); - } - - return res; - } - - off = this.removeListener; - - destroy(err?: Error | null, cb?: () => void) { - const r = this._readableState; - - if (r.destroyed) { - if (typeof cb === "function") { - cb(); - } - - return this; - } - - if (err) { - // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 - err.stack; - - if (!r.errored) { - r.errored = err; - } - } - - r.destroyed = true; - - // If still constructing then defer calling _destroy. - if (!r.constructed) { - this.once(kDestroy, (er: Error) => { - _destroy(this, err || er, cb); - }); - } else { - _destroy(this, err, cb); - } - - return this; - } - - _undestroy() { - const r = this._readableState; - r.constructed = true; - r.closed = false; - r.closeEmitted = false; - r.destroyed = false; - r.errored = null; - r.errorEmitted = false; - r.reading = false; - r.ended = false; - r.endEmitted = false; - } - - _destroy( - error: Error | null, - callback: (error?: Error | null) => void, - ): void { - callback(error); - } - - [captureRejectionSymbol](err: Error) { - this.destroy(err); - } - - // deno-lint-ignore no-explicit-any - push(chunk: any, encoding?: Encodings): boolean { - return readableAddChunk(this, chunk, encoding, false); - } - - // deno-lint-ignore no-explicit-any - unshift(chunk: any, encoding?: string): boolean { - return readableAddChunk(this, chunk, encoding, true); - } - - unpipe(dest?: Writable): this { - const state = this._readableState; - const unpipeInfo = { hasUnpiped: false }; - - if (state.pipes.length === 0) { - return this; - } - - if (!dest) { - // remove all. - const dests = state.pipes; - state.pipes = []; - this.pause(); - - for (const dest of dests) { - dest.emit("unpipe", this, { hasUnpiped: false }); - } - return this; - } - - const index = state.pipes.indexOf(dest); - if (index === -1) { - return this; - } - - state.pipes.splice(index, 1); - if (state.pipes.length === 0) { - this.pause(); - } - - dest.emit("unpipe", this, unpipeInfo); - - return this; - } - - removeAllListeners( - ev: - | "close" - | "data" - | "end" - | "error" - | "pause" - | "readable" - | "resume" - | symbol - | undefined, - ) { - const res = super.removeAllListeners(ev); - - if (ev === "readable" || ev === undefined) { - queueMicrotask(() => updateReadableListening(this)); - } - - return res; - } - - resume() { - const state = this._readableState; - if (!state.flowing) { - // We flow only if there is no one listening - // for readable, but we still have to call - // resume(). - state.flowing = !state.readableListening; - resume(this, state); - } - state[kPaused] = false; - return this; - } - - pause() { - if (this._readableState.flowing !== false) { - this._readableState.flowing = false; - this.emit("pause"); - } - this._readableState[kPaused] = true; - return this; - } - - /** Wrap an old-style stream as the async data source. */ - wrap(stream: Stream): this { - const state = this._readableState; - let paused = false; - - stream.on("end", () => { - if (state.decoder && !state.ended) { - const chunk = state.decoder.end(); - if (chunk && chunk.length) { - this.push(chunk); - } - } - - this.push(null); - }); - - stream.on("data", (chunk) => { - if (state.decoder) { - chunk = state.decoder.write(chunk); - } - - if (state.objectMode && (chunk === null || chunk === undefined)) { - return; - } else if (!state.objectMode && (!chunk || !chunk.length)) { - return; - } - - const ret = this.push(chunk); - if (!ret) { - paused = true; - // By the time this is triggered, stream will be a readable stream - // deno-lint-ignore ban-ts-comment - // @ts-ignore - stream.pause(); - } - }); - - // TODO(Soremwar) - // There must be a clean way to implement this on TypeScript - // Proxy all the other methods. Important when wrapping filters and duplexes. - for (const i in stream) { - // deno-lint-ignore ban-ts-comment - //@ts-ignore - if (this[i] === undefined && typeof stream[i] === "function") { - // deno-lint-ignore ban-ts-comment - //@ts-ignore - this[i] = function methodWrap(method) { - return function methodWrapReturnFunction() { - // deno-lint-ignore ban-ts-comment - //@ts-ignore - return stream[method].apply(stream); - }; - }(i); - } - } - - stream.on("error", (err) => { - errorOrDestroy(this, err); - }); - - stream.on("close", () => { - this.emit("close"); - }); - - stream.on("destroy", () => { - this.emit("destroy"); - }); - - stream.on("pause", () => { - this.emit("pause"); - }); - - stream.on("resume", () => { - this.emit("resume"); - }); - - this._read = () => { - if (paused) { - paused = false; - // By the time this is triggered, stream will be a readable stream - // deno-lint-ignore ban-ts-comment - //@ts-ignore - stream.resume(); - } - }; - - return this; - } - - [Symbol.asyncIterator]() { - return createReadableStreamAsyncIterator(this); - } - - get readable(): boolean { - return this._readableState?.readable && - !this._readableState?.destroyed && - !this._readableState?.errorEmitted && - !this._readableState?.endEmitted; - } - set readable(val: boolean) { - if (this._readableState) { - this._readableState.readable = val; - } - } - - get readableHighWaterMark(): number { - return this._readableState.highWaterMark; - } - - get readableBuffer() { - return this._readableState && this._readableState.buffer; - } - - get readableFlowing(): boolean | null { - return this._readableState.flowing; - } - - set readableFlowing(state: boolean | null) { - if (this._readableState) { - this._readableState.flowing = state; - } - } - - get readableLength() { - return this._readableState.length; - } - - get readableObjectMode() { - return this._readableState ? this._readableState.objectMode : false; - } - - get readableEncoding() { - return this._readableState ? this._readableState.encoding : null; - } - - get destroyed() { - if (this._readableState === undefined) { - return false; - } - return this._readableState.destroyed; - } - - set destroyed(value: boolean) { - if (!this._readableState) { - return; - } - this._readableState.destroyed = value; - } - - get readableEnded() { - return this._readableState ? this._readableState.endEmitted : false; - } -} - -Object.defineProperties(Readable, { - _readableState: { enumerable: false }, - destroyed: { enumerable: false }, - readableBuffer: { enumerable: false }, - readableEncoding: { enumerable: false }, - readableEnded: { enumerable: false }, - readableFlowing: { enumerable: false }, - readableHighWaterMark: { enumerable: false }, - readableLength: { enumerable: false }, - readableObjectMode: { enumerable: false }, -}); - -export default Readable; diff --git a/std/node/_stream/readable_internal.ts b/std/node/_stream/readable_internal.ts deleted file mode 100644 index 0ef261d4d7..0000000000 --- a/std/node/_stream/readable_internal.ts +++ /dev/null @@ -1,438 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { Buffer } from "../buffer.ts"; -import type Duplex from "./duplex.ts"; -import type EventEmitter from "../events.ts"; -import type Readable from "./readable.ts"; -import type Writable from "./writable.ts"; -import type { ReadableState } from "./readable.ts"; -import { kPaused } from "./symbols.ts"; -import { - ERR_STREAM_PUSH_AFTER_EOF, - ERR_STREAM_UNSHIFT_AFTER_END_EVENT, -} from "../_errors.ts"; - -export function _destroy( - self: Readable, - err?: Error | null, - cb?: (error?: Error | null) => void, -) { - self._destroy(err || null, (err) => { - const r = (self as Readable)._readableState; - - if (err) { - // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 - err.stack; - - if (!r.errored) { - r.errored = err; - } - } - - r.closed = true; - - if (typeof cb === "function") { - cb(err); - } - - if (err) { - queueMicrotask(() => { - if (!r.errorEmitted) { - r.errorEmitted = true; - self.emit("error", err); - } - r.closeEmitted = true; - if (r.emitClose) { - self.emit("close"); - } - }); - } else { - queueMicrotask(() => { - r.closeEmitted = true; - if (r.emitClose) { - self.emit("close"); - } - }); - } - }); -} - -export function addChunk( - stream: Duplex | Readable, - state: ReadableState, - chunk: string | Buffer | Uint8Array, - addToFront: boolean, -) { - if (state.flowing && state.length === 0 && !state.sync) { - if (state.multiAwaitDrain) { - (state.awaitDrainWriters as Set).clear(); - } else { - state.awaitDrainWriters = null; - } - stream.emit("data", chunk); - } else { - // Update the buffer info. - state.length += state.objectMode ? 1 : chunk.length; - if (addToFront) { - state.buffer.unshift(chunk); - } else { - state.buffer.push(chunk); - } - - if (state.needReadable) { - emitReadable(stream); - } - } - maybeReadMore(stream, state); -} - -// Don't raise the hwm > 1GB. -const MAX_HWM = 0x40000000; -export function computeNewHighWaterMark(n: number) { - if (n >= MAX_HWM) { - n = MAX_HWM; - } else { - n--; - n |= n >>> 1; - n |= n >>> 2; - n |= n >>> 4; - n |= n >>> 8; - n |= n >>> 16; - n++; - } - return n; -} - -export function emitReadable(stream: Duplex | Readable) { - const state = stream._readableState; - state.needReadable = false; - if (!state.emittedReadable) { - state.emittedReadable = true; - queueMicrotask(() => emitReadable_(stream)); - } -} - -function emitReadable_(stream: Duplex | Readable) { - const state = stream._readableState; - if (!state.destroyed && !state.errored && (state.length || state.ended)) { - stream.emit("readable"); - state.emittedReadable = false; - } - - state.needReadable = !state.flowing && - !state.ended && - state.length <= state.highWaterMark; - flow(stream); -} - -export function endReadable(stream: Readable) { - const state = stream._readableState; - - if (!state.endEmitted) { - state.ended = true; - queueMicrotask(() => endReadableNT(state, stream)); - } -} - -function endReadableNT(state: ReadableState, stream: Readable) { - if ( - !state.errorEmitted && !state.closeEmitted && - !state.endEmitted && state.length === 0 - ) { - state.endEmitted = true; - stream.emit("end"); - - if (state.autoDestroy) { - stream.destroy(); - } - } -} - -export function errorOrDestroy( - stream: Duplex | Readable, - err: Error, - sync = false, -) { - const r = stream._readableState; - - if (r.destroyed) { - return stream; - } - - if (r.autoDestroy) { - stream.destroy(err); - } else if (err) { - // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 - err.stack; - - if (!r.errored) { - r.errored = err; - } - if (sync) { - queueMicrotask(() => { - if (!r.errorEmitted) { - r.errorEmitted = true; - stream.emit("error", err); - } - }); - } else if (!r.errorEmitted) { - r.errorEmitted = true; - stream.emit("error", err); - } - } -} - -function flow(stream: Duplex | Readable) { - const state = stream._readableState; - while (state.flowing && stream.read() !== null); -} - -/** Pluck off n bytes from an array of buffers. -* Length is the combined lengths of all the buffers in the list. -* This function is designed to be inlinable, so please take care when making -* changes to the function body. -*/ -export function fromList(n: number, state: ReadableState) { - // nothing buffered. - if (state.length === 0) { - return null; - } - - let ret; - if (state.objectMode) { - ret = state.buffer.shift(); - } else if (!n || n >= state.length) { - if (state.decoder) { - ret = state.buffer.join(""); - } else if (state.buffer.length === 1) { - ret = state.buffer.first(); - } else { - ret = state.buffer.concat(state.length); - } - state.buffer.clear(); - } else { - ret = state.buffer.consume(n, !!state.decoder); - } - - return ret; -} - -export function howMuchToRead(n: number, state: ReadableState) { - if (n <= 0 || (state.length === 0 && state.ended)) { - return 0; - } - if (state.objectMode) { - return 1; - } - if (Number.isNaN(n)) { - // Only flow one buffer at a time. - if (state.flowing && state.length) { - return state.buffer.first().length; - } - return state.length; - } - if (n <= state.length) { - return n; - } - return state.ended ? state.length : 0; -} - -export function maybeReadMore(stream: Readable, state: ReadableState) { - if (!state.readingMore && state.constructed) { - state.readingMore = true; - queueMicrotask(() => maybeReadMore_(stream, state)); - } -} - -function maybeReadMore_(stream: Readable, state: ReadableState) { - while ( - !state.reading && !state.ended && - (state.length < state.highWaterMark || - (state.flowing && state.length === 0)) - ) { - const len = state.length; - stream.read(0); - if (len === state.length) { - // Didn't get any data, stop spinning. - break; - } - } - state.readingMore = false; -} - -export function nReadingNextTick(self: Duplex | Readable) { - self.read(0); -} - -export function onEofChunk(stream: Duplex | Readable, state: ReadableState) { - if (state.ended) return; - if (state.decoder) { - const chunk = state.decoder.end(); - if (chunk && chunk.length) { - state.buffer.push(chunk); - state.length += state.objectMode ? 1 : chunk.length; - } - } - state.ended = true; - - if (state.sync) { - emitReadable(stream); - } else { - state.needReadable = false; - state.emittedReadable = true; - emitReadable_(stream); - } -} - -export function pipeOnDrain(src: Duplex | Readable, dest: Duplex | Writable) { - return function pipeOnDrainFunctionResult() { - const state = src._readableState; - - if (state.awaitDrainWriters === dest) { - state.awaitDrainWriters = null; - } else if (state.multiAwaitDrain) { - (state.awaitDrainWriters as Set).delete(dest); - } - - if ( - (!state.awaitDrainWriters || - (state.awaitDrainWriters as Set).size === 0) && - src.listenerCount("data") - ) { - state.flowing = true; - flow(src); - } - }; -} - -export function prependListener( - emitter: EventEmitter, - event: string, - // deno-lint-ignore no-explicit-any - fn: (...args: any[]) => any, -) { - if (typeof emitter.prependListener === "function") { - return emitter.prependListener(event, fn); - } - - // This is a hack to make sure that our error handler is attached before any - // userland ones. NEVER DO THIS. This is here only because this code needs - // to continue to work with older versions of Node.js that do not include - //the prependListener() method. The goal is to eventually remove this hack. - // TODO(Soremwar) - // Burn it with fire - // deno-lint-ignore ban-ts-comment - //@ts-ignore - if (emitter._events.get(event)?.length) { - // deno-lint-ignore ban-ts-comment - //@ts-ignore - const listeners = [fn, ...emitter._events.get(event)]; - // deno-lint-ignore ban-ts-comment - //@ts-ignore - emitter._events.set(event, listeners); - } else { - emitter.on(event, fn); - } -} - -export function readableAddChunk( - stream: Duplex | Readable, - chunk: string | Buffer | Uint8Array | null, - encoding: undefined | string = undefined, - addToFront: boolean, -) { - const state = stream._readableState; - let usedEncoding = encoding; - - let err; - if (!state.objectMode) { - if (typeof chunk === "string") { - usedEncoding = encoding || state.defaultEncoding; - if (state.encoding !== usedEncoding) { - if (addToFront && state.encoding) { - chunk = Buffer.from(chunk, usedEncoding).toString(state.encoding); - } else { - chunk = Buffer.from(chunk, usedEncoding); - usedEncoding = ""; - } - } - } else if (chunk instanceof Uint8Array) { - chunk = Buffer.from(chunk); - } - } - - if (err) { - errorOrDestroy(stream, err); - } else if (chunk === null) { - state.reading = false; - onEofChunk(stream, state); - } else if (state.objectMode || (chunk.length > 0)) { - if (addToFront) { - if (state.endEmitted) { - errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT()); - } else { - addChunk(stream, state, chunk, true); - } - } else if (state.ended) { - errorOrDestroy(stream, new ERR_STREAM_PUSH_AFTER_EOF()); - } else if (state.destroyed || state.errored) { - return false; - } else { - state.reading = false; - if (state.decoder && !usedEncoding) { - //TODO(Soremwar) - //I don't think this cast is right - chunk = state.decoder.write(Buffer.from(chunk as Uint8Array)); - if (state.objectMode || chunk.length !== 0) { - addChunk(stream, state, chunk, false); - } else { - maybeReadMore(stream, state); - } - } else { - addChunk(stream, state, chunk, false); - } - } - } else if (!addToFront) { - state.reading = false; - maybeReadMore(stream, state); - } - - return !state.ended && - (state.length < state.highWaterMark || state.length === 0); -} - -export function resume(stream: Duplex | Readable, state: ReadableState) { - if (!state.resumeScheduled) { - state.resumeScheduled = true; - queueMicrotask(() => resume_(stream, state)); - } -} - -function resume_(stream: Duplex | Readable, state: ReadableState) { - if (!state.reading) { - stream.read(0); - } - - state.resumeScheduled = false; - stream.emit("resume"); - flow(stream); - if (state.flowing && !state.reading) { - stream.read(0); - } -} - -export function updateReadableListening(self: Duplex | Readable) { - const state = self._readableState; - state.readableListening = self.listenerCount("readable") > 0; - - if (state.resumeScheduled && state[kPaused] === false) { - // Flowing needs to be set to true now, otherwise - // the upcoming resume will not flow. - state.flowing = true; - - // Crude way to check if we should resume. - } else if (self.listenerCount("data") > 0) { - self.resume(); - } else if (!state.readableListening) { - state.flowing = null; - } -} diff --git a/std/node/_stream/readable_test.ts b/std/node/_stream/readable_test.ts deleted file mode 100644 index 72767e28fd..0000000000 --- a/std/node/_stream/readable_test.ts +++ /dev/null @@ -1,489 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { Buffer } from "../buffer.ts"; -import Readable from "../_stream/readable.ts"; -import { once } from "../events.ts"; -import { deferred } from "../../async/mod.ts"; -import { - assert, - assertEquals, - assertStrictEquals, -} from "../../testing/asserts.ts"; - -Deno.test("Readable stream from iterator", async () => { - function* generate() { - yield "a"; - yield "b"; - yield "c"; - } - - const stream = Readable.from(generate()); - - const expected = ["a", "b", "c"]; - - for await (const chunk of stream) { - assertStrictEquals(chunk, expected.shift()); - } -}); - -Deno.test("Readable stream from async iterator", async () => { - async function* generate() { - yield "a"; - yield "b"; - yield "c"; - } - - const stream = Readable.from(generate()); - - const expected = ["a", "b", "c"]; - - for await (const chunk of stream) { - assertStrictEquals(chunk, expected.shift()); - } -}); - -Deno.test("Readable stream from promise", async () => { - const promises = [ - Promise.resolve("a"), - Promise.resolve("b"), - Promise.resolve("c"), - ]; - - const stream = Readable.from(promises); - - const expected = ["a", "b", "c"]; - - for await (const chunk of stream) { - assertStrictEquals(chunk, expected.shift()); - } -}); - -Deno.test("Readable stream from string", async () => { - const string = "abc"; - const stream = Readable.from(string); - - for await (const chunk of stream) { - assertStrictEquals(chunk, string); - } -}); - -Deno.test("Readable stream from Buffer", async () => { - const string = "abc"; - const stream = Readable.from(Buffer.from(string)); - - for await (const chunk of stream) { - assertStrictEquals((chunk as Buffer).toString(), string); - } -}); - -Deno.test("Readable stream gets destroyed on error", async () => { - // deno-lint-ignore require-yield - async function* generate() { - throw new Error("kaboom"); - } - - const stream = Readable.from(generate()); - - stream.read(); - - const [err] = await once(stream, "error"); - assertStrictEquals(err.message, "kaboom"); - assertStrictEquals(stream.destroyed, true); -}); - -Deno.test("Readable stream works as Transform stream", async () => { - async function* generate(stream: Readable) { - for await (const chunk of stream) { - yield (chunk as string).toUpperCase(); - } - } - - const source = new Readable({ - objectMode: true, - read() { - this.push("a"); - this.push("b"); - this.push("c"); - this.push(null); - }, - }); - - const stream = Readable.from(generate(source)); - - const expected = ["A", "B", "C"]; - - for await (const chunk of stream) { - assertStrictEquals(chunk, expected.shift()); - } -}); - -Deno.test("Readable stream can be paused", () => { - const readable = new Readable(); - - // _read is a noop, here. - readable._read = () => {}; - - // Default state of a stream is not "paused" - assert(!readable.isPaused()); - - // Make the stream start flowing... - readable.on("data", () => {}); - - // still not paused. - assert(!readable.isPaused()); - - readable.pause(); - assert(readable.isPaused()); - readable.resume(); - assert(!readable.isPaused()); -}); - -Deno.test("Readable stream sets enconding correctly", () => { - const readable = new Readable({ - read() {}, - }); - - readable.setEncoding("utf8"); - - readable.push(new TextEncoder().encode("DEF")); - readable.unshift(new TextEncoder().encode("ABC")); - - assertStrictEquals(readable.read(), "ABCDEF"); -}); - -Deno.test("Readable stream sets encoding correctly", () => { - const readable = new Readable({ - read() {}, - }); - - readable.setEncoding("utf8"); - - readable.push(new TextEncoder().encode("DEF")); - readable.unshift(new TextEncoder().encode("ABC")); - - assertStrictEquals(readable.read(), "ABCDEF"); -}); - -Deno.test("Readable stream holds up a big push", async () => { - let readExecuted = 0; - const readExecutedExpected = 3; - const readExpectedExecutions = deferred(); - - let endExecuted = 0; - const endExecutedExpected = 1; - const endExpectedExecutions = deferred(); - - const str = "asdfasdfasdfasdfasdf"; - - const r = new Readable({ - highWaterMark: 5, - encoding: "utf8", - }); - - let reads = 0; - - function _read() { - if (reads === 0) { - setTimeout(() => { - r.push(str); - }, 1); - reads++; - } else if (reads === 1) { - const ret = r.push(str); - assertEquals(ret, false); - reads++; - } else { - r.push(null); - } - } - - r._read = () => { - readExecuted++; - if (readExecuted == readExecutedExpected) { - readExpectedExecutions.resolve(); - } - _read(); - }; - - r.on("end", () => { - endExecuted++; - if (endExecuted == endExecutedExpected) { - endExpectedExecutions.resolve(); - } - }); - - // Push some data in to start. - // We've never gotten any read event at this point. - const ret = r.push(str); - assert(!ret); - let chunk = r.read(); - assertEquals(chunk, str); - chunk = r.read(); - assertEquals(chunk, null); - - r.once("readable", () => { - // This time, we'll get *all* the remaining data, because - // it's been added synchronously, as the read WOULD take - // us below the hwm, and so it triggered a _read() again, - // which synchronously added more, which we then return. - chunk = r.read(); - assertEquals(chunk, str + str); - - chunk = r.read(); - assertEquals(chunk, null); - }); - - const readTimeout = setTimeout( - () => readExpectedExecutions.reject(), - 1000, - ); - const endTimeout = setTimeout( - () => endExpectedExecutions.reject(), - 1000, - ); - await readExpectedExecutions; - await endExpectedExecutions; - clearTimeout(readTimeout); - clearTimeout(endTimeout); - assertEquals(readExecuted, readExecutedExpected); - assertEquals(endExecuted, endExecutedExpected); -}); - -Deno.test("Readable stream: 'on' event", async () => { - async function* generate() { - yield "a"; - yield "b"; - yield "c"; - } - - const stream = Readable.from(generate()); - - let iterations = 0; - const expected = ["a", "b", "c"]; - - stream.on("data", (chunk) => { - iterations++; - assertStrictEquals(chunk, expected.shift()); - }); - - await once(stream, "end"); - - assertStrictEquals(iterations, 3); -}); - -Deno.test("Readable stream: 'data' event", async () => { - async function* generate() { - yield "a"; - yield "b"; - yield "c"; - } - - const stream = Readable.from(generate(), { objectMode: false }); - - let iterations = 0; - const expected = ["a", "b", "c"]; - - stream.on("data", (chunk) => { - iterations++; - assertStrictEquals(chunk instanceof Buffer, true); - assertStrictEquals(chunk.toString(), expected.shift()); - }); - - await once(stream, "end"); - - assertStrictEquals(iterations, 3); -}); - -Deno.test("Readable stream: 'data' event on non-object", async () => { - async function* generate() { - yield "a"; - yield "b"; - yield "c"; - } - - const stream = Readable.from(generate(), { objectMode: false }); - - let iterations = 0; - const expected = ["a", "b", "c"]; - - stream.on("data", (chunk) => { - iterations++; - assertStrictEquals(chunk instanceof Buffer, true); - assertStrictEquals(chunk.toString(), expected.shift()); - }); - - await once(stream, "end"); - - assertStrictEquals(iterations, 3); -}); - -Deno.test("Readable stream: 'readable' event is emitted but 'read' is not on highWaterMark length exceeded", async () => { - let readableExecuted = 0; - const readableExecutedExpected = 1; - const readableExpectedExecutions = deferred(); - - const r = new Readable({ - highWaterMark: 3, - }); - - r._read = () => { - throw new Error("_read must not be called"); - }; - r.push(Buffer.from("blerg")); - - setTimeout(function () { - assert(!r._readableState.reading); - r.on("readable", () => { - readableExecuted++; - if (readableExecuted == readableExecutedExpected) { - readableExpectedExecutions.resolve(); - } - }); - }, 1); - - const readableTimeout = setTimeout( - () => readableExpectedExecutions.reject(), - 1000, - ); - await readableExpectedExecutions; - clearTimeout(readableTimeout); - assertEquals(readableExecuted, readableExecutedExpected); -}); - -Deno.test("Readable stream: 'readable' and 'read' events are emitted on highWaterMark length not reached", async () => { - let readableExecuted = 0; - const readableExecutedExpected = 1; - const readableExpectedExecutions = deferred(); - - let readExecuted = 0; - const readExecutedExpected = 1; - const readExpectedExecutions = deferred(); - - const r = new Readable({ - highWaterMark: 3, - }); - - r._read = () => { - readExecuted++; - if (readExecuted == readExecutedExpected) { - readExpectedExecutions.resolve(); - } - }; - - r.push(Buffer.from("bl")); - - setTimeout(function () { - assert(r._readableState.reading); - r.on("readable", () => { - readableExecuted++; - if (readableExecuted == readableExecutedExpected) { - readableExpectedExecutions.resolve(); - } - }); - }, 1); - - const readableTimeout = setTimeout( - () => readableExpectedExecutions.reject(), - 1000, - ); - const readTimeout = setTimeout( - () => readExpectedExecutions.reject(), - 1000, - ); - await readableExpectedExecutions; - await readExpectedExecutions; - clearTimeout(readableTimeout); - clearTimeout(readTimeout); - assertEquals(readableExecuted, readableExecutedExpected); - assertEquals(readExecuted, readExecutedExpected); -}); - -Deno.test("Readable stream: 'readable' event is emitted but 'read' is not on highWaterMark length not reached and stream ended", async () => { - let readableExecuted = 0; - const readableExecutedExpected = 1; - const readableExpectedExecutions = deferred(); - - const r = new Readable({ - highWaterMark: 30, - }); - - r._read = () => { - throw new Error("Must not be executed"); - }; - - r.push(Buffer.from("blerg")); - //This ends the stream and triggers end - r.push(null); - - setTimeout(function () { - // Assert we're testing what we think we are - assert(!r._readableState.reading); - r.on("readable", () => { - readableExecuted++; - if (readableExecuted == readableExecutedExpected) { - readableExpectedExecutions.resolve(); - } - }); - }, 1); - - const readableTimeout = setTimeout( - () => readableExpectedExecutions.reject(), - 1000, - ); - await readableExpectedExecutions; - clearTimeout(readableTimeout); - assertEquals(readableExecuted, readableExecutedExpected); -}); - -Deno.test("Readable stream: 'read' is emitted on empty string pushed in non-object mode", async () => { - let endExecuted = 0; - const endExecutedExpected = 1; - const endExpectedExecutions = deferred(); - - const underlyingData = ["", "x", "y", "", "z"]; - const expected = underlyingData.filter((data) => data); - const result: unknown[] = []; - - const r = new Readable({ - encoding: "utf8", - }); - r._read = function () { - queueMicrotask(() => { - if (!underlyingData.length) { - this.push(null); - } else { - this.push(underlyingData.shift()); - } - }); - }; - - r.on("readable", () => { - const data = r.read(); - if (data !== null) result.push(data); - }); - - r.on("end", () => { - endExecuted++; - if (endExecuted == endExecutedExpected) { - endExpectedExecutions.resolve(); - } - assertEquals(result, expected); - }); - - const endTimeout = setTimeout( - () => endExpectedExecutions.reject(), - 1000, - ); - await endExpectedExecutions; - clearTimeout(endTimeout); - assertEquals(endExecuted, endExecutedExpected); -}); - -Deno.test("Readable stream: listeners can be removed", () => { - const r = new Readable(); - r._read = () => {}; - r.on("data", () => {}); - - r.removeAllListeners("data"); - - assertEquals(r.eventNames().length, 0); -}); diff --git a/std/node/_stream/stream.ts b/std/node/_stream/stream.ts deleted file mode 100644 index 4daafc77bc..0000000000 --- a/std/node/_stream/stream.ts +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { Buffer } from "../buffer.ts"; -import EventEmitter from "../events.ts"; -import type Readable from "./readable.ts"; -import type Writable from "./writable.ts"; -import { types } from "../util.ts"; - -class Stream extends EventEmitter { - constructor() { - super(); - } - - static _isUint8Array = types.isUint8Array; - static _uint8ArrayToBuffer = (chunk: Uint8Array) => Buffer.from(chunk); - - pipe(dest: Readable | Writable, options?: { end?: boolean }) { - // deno-lint-ignore no-this-alias - const source = this; - - //TODO(Soremwar) - //isStdio exist on stdin || stdout only, which extend from Duplex - //if (!dest._isStdio && (options?.end ?? true)) { - //Find an alternative to be able to pipe streams to stdin & stdout - //Port them as well? - if (options?.end ?? true) { - source.on("end", onend); - source.on("close", onclose); - } - - let didOnEnd = false; - function onend() { - if (didOnEnd) return; - didOnEnd = true; - - // 'end' is only called on Writable streams - (dest as Writable).end(); - } - - function onclose() { - if (didOnEnd) return; - didOnEnd = true; - - if (typeof dest.destroy === "function") dest.destroy(); - } - - // Don't leave dangling pipes when there are errors. - function onerror(this: Stream, er: Error) { - cleanup(); - if (this.listenerCount("error") === 0) { - throw er; // Unhandled stream error in pipe. - } - } - - source.on("error", onerror); - dest.on("error", onerror); - - // Remove all the event listeners that were added. - function cleanup() { - source.removeListener("end", onend); - source.removeListener("close", onclose); - - source.removeListener("error", onerror); - dest.removeListener("error", onerror); - - source.removeListener("end", cleanup); - source.removeListener("close", cleanup); - - dest.removeListener("close", cleanup); - } - - source.on("end", cleanup); - source.on("close", cleanup); - - dest.on("close", cleanup); - dest.emit("pipe", source); - - return dest; - } -} - -export default Stream; diff --git a/std/node/_stream/symbols.ts b/std/node/_stream/symbols.ts deleted file mode 100644 index addb969d3a..0000000000 --- a/std/node/_stream/symbols.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -export const kConstruct = Symbol("kConstruct"); -export const kDestroy = Symbol("kDestroy"); -export const kPaused = Symbol("kPaused"); diff --git a/std/node/_stream/transform.ts b/std/node/_stream/transform.ts deleted file mode 100644 index a4246e81a0..0000000000 --- a/std/node/_stream/transform.ts +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { Encodings } from "../_utils.ts"; -import Duplex from "./duplex.ts"; -import type { DuplexOptions } from "./duplex.ts"; -import type { writeV } from "./writable_internal.ts"; -import { ERR_METHOD_NOT_IMPLEMENTED } from "../_errors.ts"; - -const kCallback = Symbol("kCallback"); - -type TransformFlush = ( - this: Transform, - // deno-lint-ignore no-explicit-any - callback: (error?: Error | null, data?: any) => void, -) => void; - -export interface TransformOptions extends DuplexOptions { - read?(this: Transform, size: number): void; - write?( - this: Transform, - // deno-lint-ignore no-explicit-any - chunk: any, - encoding: Encodings, - callback: (error?: Error | null) => void, - ): void; - writev?: writeV; - final?(this: Transform, callback: (error?: Error | null) => void): void; - destroy?( - this: Transform, - error: Error | null, - callback: (error: Error | null) => void, - ): void; - transform?( - this: Transform, - // deno-lint-ignore no-explicit-any - chunk: any, - encoding: Encodings, - // deno-lint-ignore no-explicit-any - callback: (error?: Error | null, data?: any) => void, - ): void; - flush?: TransformFlush; -} - -export default class Transform extends Duplex { - [kCallback]: null | ((error?: Error | null) => void); - _flush?: TransformFlush; - - constructor(options?: TransformOptions) { - super(options); - this._readableState.sync = false; - - this[kCallback] = null; - - if (options) { - if (typeof options.transform === "function") { - this._transform = options.transform; - } - - if (typeof options.flush === "function") { - this._flush = options.flush; - } - } - - this.on("prefinish", function (this: Transform) { - if (typeof this._flush === "function" && !this.destroyed) { - this._flush((er, data) => { - if (er) { - this.destroy(er); - return; - } - - if (data != null) { - this.push(data); - } - this.push(null); - }); - } else { - this.push(null); - } - }); - } - - _read = () => { - if (this[kCallback]) { - const callback = this[kCallback] as (error?: Error | null) => void; - this[kCallback] = null; - callback(); - } - }; - - _transform( - // deno-lint-ignore no-explicit-any - _chunk: any, - _encoding: string, - // deno-lint-ignore no-explicit-any - _callback: (error?: Error | null, data?: any) => void, - ) { - throw new ERR_METHOD_NOT_IMPLEMENTED("_transform()"); - } - - _write = ( - // deno-lint-ignore no-explicit-any - chunk: any, - encoding: string, - callback: (error?: Error | null) => void, - ) => { - const rState = this._readableState; - const wState = this._writableState; - const length = rState.length; - - this._transform(chunk, encoding, (err, val) => { - if (err) { - callback(err); - return; - } - - if (val != null) { - this.push(val); - } - - if ( - wState.ended || // Backwards compat. - length === rState.length || // Backwards compat. - rState.length < rState.highWaterMark || - rState.length === 0 - ) { - callback(); - } else { - this[kCallback] = callback; - } - }); - }; -} diff --git a/std/node/_stream/transform_test.ts b/std/node/_stream/transform_test.ts deleted file mode 100644 index d3b90ff01b..0000000000 --- a/std/node/_stream/transform_test.ts +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { Buffer } from "../buffer.ts"; -import Transform from "./transform.ts"; -import finished from "./end_of_stream.ts"; -import { deferred } from "../../async/mod.ts"; -import { assert, assertEquals } from "../../testing/asserts.ts"; - -Deno.test("Transform stream finishes correctly", async () => { - let finishedExecuted = 0; - const finishedExecutedExpected = 1; - const finishedExecution = deferred(); - - const tr = new Transform({ - transform(_data, _enc, cb) { - cb(); - }, - }); - - let finish = false; - let ended = false; - - tr.on("end", () => { - ended = true; - }); - - tr.on("finish", () => { - finish = true; - }); - - finished(tr, (err) => { - finishedExecuted++; - if (finishedExecuted === finishedExecutedExpected) { - finishedExecution.resolve(); - } - assert(!err, "no error"); - assert(finish); - assert(ended); - }); - - tr.end(); - tr.resume(); - - const finishedTimeout = setTimeout( - () => finishedExecution.reject(), - 1000, - ); - await finishedExecution; - clearTimeout(finishedTimeout); - assertEquals(finishedExecuted, finishedExecutedExpected); -}); - -Deno.test("Transform stream flushes data correctly", () => { - const expected = "asdf"; - - const t = new Transform({ - transform: (_d, _e, n) => { - n(); - }, - flush: (n) => { - n(null, expected); - }, - }); - - t.end(Buffer.from("blerg")); - t.on("data", (data) => { - assertEquals(data.toString(), expected); - }); -}); diff --git a/std/node/_stream/writable.ts b/std/node/_stream/writable.ts deleted file mode 100644 index 534fc22fb0..0000000000 --- a/std/node/_stream/writable.ts +++ /dev/null @@ -1,443 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { Buffer } from "../buffer.ts"; -import Stream from "./stream.ts"; -import { captureRejectionSymbol } from "../events.ts"; -import { - ERR_INVALID_ARG_TYPE, - ERR_INVALID_OPT_VALUE, - ERR_METHOD_NOT_IMPLEMENTED, - ERR_STREAM_ALREADY_FINISHED, - ERR_STREAM_CANNOT_PIPE, - ERR_STREAM_DESTROYED, - ERR_STREAM_NULL_VALUES, - ERR_STREAM_WRITE_AFTER_END, - ERR_UNKNOWN_ENCODING, -} from "../_errors.ts"; -import type { AfterWriteTick, writeV } from "./writable_internal.ts"; -import { - clearBuffer, - destroy, - errorBuffer, - errorOrDestroy, - finishMaybe, - kOnFinished, - nop, - onwrite, - resetBuffer, - writeOrBuffer, -} from "./writable_internal.ts"; -import type { Encodings } from "../_utils.ts"; - -type WritableEncodings = Encodings | "buffer"; - -export interface WritableOptions { - autoDestroy?: boolean; - decodeStrings?: boolean; - defaultEncoding?: WritableEncodings; - destroy?( - this: Writable, - error: Error | null, - callback: (error: Error | null) => void, - ): void; - emitClose?: boolean; - final?(this: Writable, callback: (error?: Error | null) => void): void; - highWaterMark?: number; - objectMode?: boolean; - write?( - this: Writable, - // deno-lint-ignore no-explicit-any - chunk: any, - encoding: WritableEncodings, - callback: (error?: Error | null) => void, - ): void; - writev?( - this: Writable, - // deno-lint-ignore no-explicit-any - chunks: Array<{ chunk: any; encoding: string }>, - callback: (error?: Error | null) => void, - ): void; -} - -export class WritableState { - [kOnFinished]: Array<(error?: Error) => void> = []; - afterWriteTickInfo: null | AfterWriteTick = null; - allBuffers = true; - allNoop = true; - autoDestroy: boolean; - buffered: Array<{ - allBuffers?: boolean; - // deno-lint-ignore no-explicit-any - chunk: any; - encoding: string; - callback: (error: Error) => void; - }> = []; - bufferedIndex = 0; - bufferProcessing = false; - closed = false; - closeEmitted = false; - constructed: boolean; - corked = 0; - decodeStrings: boolean; - defaultEncoding: WritableEncodings; - destroyed = false; - emitClose: boolean; - ended = false; - ending = false; - errored: Error | null = null; - errorEmitted = false; - finalCalled = false; - finished = false; - highWaterMark: number; - length = 0; - needDrain = false; - objectMode: boolean; - onwrite: (error?: Error | null) => void; - pendingcb = 0; - prefinished = false; - sync = true; - writecb: null | ((error: Error) => void) = null; - writable = true; - writelen = 0; - writing = false; - - constructor(options: WritableOptions | undefined, stream: Writable) { - this.objectMode = !!options?.objectMode; - - this.highWaterMark = options?.highWaterMark ?? - (this.objectMode ? 16 : 16 * 1024); - - if (Number.isInteger(this.highWaterMark) && this.highWaterMark >= 0) { - this.highWaterMark = Math.floor(this.highWaterMark); - } else { - throw new ERR_INVALID_OPT_VALUE("highWaterMark", this.highWaterMark); - } - - this.decodeStrings = !options?.decodeStrings === false; - - this.defaultEncoding = options?.defaultEncoding || "utf8"; - - this.onwrite = onwrite.bind(undefined, stream); - - resetBuffer(this); - - this.emitClose = options?.emitClose ?? true; - this.autoDestroy = options?.autoDestroy ?? true; - this.constructed = true; - } - - getBuffer() { - return this.buffered.slice(this.bufferedIndex); - } - - get bufferedRequestCount() { - return this.buffered.length - this.bufferedIndex; - } -} - -/** A bit simpler than readable streams. -* Implement an async `._write(chunk, encoding, cb)`, and it'll handle all -* the drain event emission and buffering. -*/ -class Writable extends Stream { - _final?: ( - this: Writable, - callback: (error?: Error | null | undefined) => void, - ) => void; - _writableState: WritableState; - _writev?: writeV | null = null; - - constructor(options?: WritableOptions) { - super(); - this._writableState = new WritableState(options, this); - - if (options) { - if (typeof options.write === "function") { - this._write = options.write; - } - - if (typeof options.writev === "function") { - this._writev = options.writev; - } - - if (typeof options.destroy === "function") { - this._destroy = options.destroy; - } - - if (typeof options.final === "function") { - this._final = options.final; - } - } - } - - [captureRejectionSymbol](err?: Error) { - this.destroy(err); - } - - static WritableState = WritableState; - - get destroyed() { - return this._writableState ? this._writableState.destroyed : false; - } - - set destroyed(value) { - if (this._writableState) { - this._writableState.destroyed = value; - } - } - - get writable() { - const w = this._writableState; - return !w.destroyed && !w.errored && !w.ending && !w.ended; - } - - set writable(val) { - if (this._writableState) { - this._writableState.writable = !!val; - } - } - - get writableFinished() { - return this._writableState ? this._writableState.finished : false; - } - - get writableObjectMode() { - return this._writableState ? this._writableState.objectMode : false; - } - - get writableBuffer() { - return this._writableState && this._writableState.getBuffer(); - } - - get writableEnded() { - return this._writableState ? this._writableState.ending : false; - } - - get writableHighWaterMark() { - return this._writableState && this._writableState.highWaterMark; - } - - get writableCorked() { - return this._writableState ? this._writableState.corked : 0; - } - - get writableLength() { - return this._writableState && this._writableState.length; - } - - _undestroy() { - const w = this._writableState; - w.constructed = true; - w.destroyed = false; - w.closed = false; - w.closeEmitted = false; - w.errored = null; - w.errorEmitted = false; - w.ended = false; - w.ending = false; - w.finalCalled = false; - w.prefinished = false; - w.finished = false; - } - - _destroy(err: Error | null, cb: (error?: Error | null) => void) { - cb(err); - } - - destroy(err?: Error | null, cb?: () => void) { - const state = this._writableState; - if (!state.destroyed) { - queueMicrotask(() => errorBuffer(state)); - } - destroy.call(this, err, cb); - return this; - } - - end(cb?: () => void): void; - // deno-lint-ignore no-explicit-any - end(chunk: any, cb?: () => void): void; - // deno-lint-ignore no-explicit-any - end(chunk: any, encoding: WritableEncodings, cb?: () => void): void; - - end( - // deno-lint-ignore no-explicit-any - x?: any | (() => void), - y?: WritableEncodings | (() => void), - z?: () => void, - ) { - const state = this._writableState; - // deno-lint-ignore no-explicit-any - let chunk: any | null; - let encoding: WritableEncodings | null; - let cb: undefined | ((error?: Error) => void); - - if (typeof x === "function") { - chunk = null; - encoding = null; - cb = x; - } else if (typeof y === "function") { - chunk = x; - encoding = null; - cb = y; - } else { - chunk = x; - encoding = y as WritableEncodings; - cb = z; - } - - if (chunk !== null && chunk !== undefined) { - this.write(chunk, encoding); - } - - if (state.corked) { - state.corked = 1; - this.uncork(); - } - - let err: Error | undefined; - if (!state.errored && !state.ending) { - state.ending = true; - finishMaybe(this, state, true); - state.ended = true; - } else if (state.finished) { - err = new ERR_STREAM_ALREADY_FINISHED("end"); - } else if (state.destroyed) { - err = new ERR_STREAM_DESTROYED("end"); - } - - if (typeof cb === "function") { - if (err || state.finished) { - queueMicrotask(() => { - (cb as (error?: Error | undefined) => void)(err); - }); - } else { - state[kOnFinished].push(cb); - } - } - - return this; - } - - _write( - // deno-lint-ignore no-explicit-any - chunk: any, - encoding: string, - cb: (error?: Error | null) => void, - ): void { - if (this._writev) { - this._writev([{ chunk, encoding }], cb); - } else { - throw new ERR_METHOD_NOT_IMPLEMENTED("_write()"); - } - } - - //This signature was changed to keep inheritance coherent - pipe(dest: Writable): Writable { - errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE()); - return dest; - } - - // deno-lint-ignore no-explicit-any - write(chunk: any, cb?: (error: Error | null | undefined) => void): boolean; - write( - // deno-lint-ignore no-explicit-any - chunk: any, - encoding: WritableEncodings | null, - cb?: (error: Error | null | undefined) => void, - ): boolean; - - write( - // deno-lint-ignore no-explicit-any - chunk: any, - x?: WritableEncodings | null | ((error: Error | null | undefined) => void), - y?: ((error: Error | null | undefined) => void), - ) { - const state = this._writableState; - let encoding: WritableEncodings; - let cb: (error?: Error | null) => void; - - if (typeof x === "function") { - cb = x; - encoding = state.defaultEncoding; - } else { - if (!x) { - encoding = state.defaultEncoding; - } else if (x !== "buffer" && !Buffer.isEncoding(x)) { - throw new ERR_UNKNOWN_ENCODING(x); - } else { - encoding = x; - } - if (typeof y !== "function") { - cb = nop; - } else { - cb = y; - } - } - - if (chunk === null) { - throw new ERR_STREAM_NULL_VALUES(); - } else if (!state.objectMode) { - if (typeof chunk === "string") { - if (state.decodeStrings !== false) { - chunk = Buffer.from(chunk, encoding); - encoding = "buffer"; - } - } else if (chunk instanceof Buffer) { - encoding = "buffer"; - } else if (Stream._isUint8Array(chunk)) { - chunk = Stream._uint8ArrayToBuffer(chunk); - encoding = "buffer"; - } else { - throw new ERR_INVALID_ARG_TYPE( - "chunk", - ["string", "Buffer", "Uint8Array"], - chunk, - ); - } - } - - let err: Error | undefined; - if (state.ending) { - err = new ERR_STREAM_WRITE_AFTER_END(); - } else if (state.destroyed) { - err = new ERR_STREAM_DESTROYED("write"); - } - - if (err) { - queueMicrotask(() => cb(err)); - errorOrDestroy(this, err, true); - return false; - } - state.pendingcb++; - return writeOrBuffer(this, state, chunk, encoding, cb); - } - - cork() { - this._writableState.corked++; - } - - uncork() { - const state = this._writableState; - - if (state.corked) { - state.corked--; - - if (!state.writing) { - clearBuffer(this, state); - } - } - } - - setDefaultEncoding(encoding: string) { - // node::ParseEncoding() requires lower case. - if (typeof encoding === "string") { - encoding = encoding.toLowerCase(); - } - if (!Buffer.isEncoding(encoding)) { - throw new ERR_UNKNOWN_ENCODING(encoding); - } - this._writableState.defaultEncoding = encoding as WritableEncodings; - return this; - } -} - -export default Writable; diff --git a/std/node/_stream/writable_internal.ts b/std/node/_stream/writable_internal.ts deleted file mode 100644 index e8c001af0d..0000000000 --- a/std/node/_stream/writable_internal.ts +++ /dev/null @@ -1,457 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import type Duplex from "./duplex.ts"; -import type Writable from "./writable.ts"; -import type { WritableState } from "./writable.ts"; -import { kDestroy } from "./symbols.ts"; -import { ERR_MULTIPLE_CALLBACK, ERR_STREAM_DESTROYED } from "../_errors.ts"; - -export type writeV = ( - // deno-lint-ignore no-explicit-any - chunks: Array<{ chunk: any; encoding: string }>, - callback: (error?: Error | null) => void, -) => void; - -export type AfterWriteTick = { - cb: (error?: Error) => void; - count: number; - state: WritableState; - stream: Writable; -}; - -export const kOnFinished = Symbol("kOnFinished"); - -function _destroy( - self: Writable, - err?: Error | null, - cb?: (error?: Error | null) => void, -) { - self._destroy(err || null, (err) => { - const w = self._writableState; - - if (err) { - // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 - err.stack; - - if (!w.errored) { - w.errored = err; - } - } - - w.closed = true; - - if (typeof cb === "function") { - cb(err); - } - - if (err) { - queueMicrotask(() => { - if (!w.errorEmitted) { - w.errorEmitted = true; - self.emit("error", err); - } - w.closeEmitted = true; - if (w.emitClose) { - self.emit("close"); - } - }); - } else { - queueMicrotask(() => { - w.closeEmitted = true; - if (w.emitClose) { - self.emit("close"); - } - }); - } - }); -} - -export function afterWrite( - stream: Writable, - state: WritableState, - count: number, - cb: (error?: Error) => void, -) { - const needDrain = !state.ending && !stream.destroyed && state.length === 0 && - state.needDrain; - if (needDrain) { - state.needDrain = false; - stream.emit("drain"); - } - - while (count-- > 0) { - state.pendingcb--; - cb(); - } - - if (state.destroyed) { - errorBuffer(state); - } - - finishMaybe(stream, state); -} - -export function afterWriteTick({ - cb, - count, - state, - stream, -}: AfterWriteTick) { - state.afterWriteTickInfo = null; - return afterWrite(stream, state, count, cb); -} - -/** If there's something in the buffer waiting, then process it.*/ -export function clearBuffer(stream: Duplex | Writable, state: WritableState) { - if ( - state.corked || - state.bufferProcessing || - state.destroyed || - !state.constructed - ) { - return; - } - - const { buffered, bufferedIndex, objectMode } = state; - const bufferedLength = buffered.length - bufferedIndex; - - if (!bufferedLength) { - return; - } - - const i = bufferedIndex; - - state.bufferProcessing = true; - if (bufferedLength > 1 && stream._writev) { - state.pendingcb -= bufferedLength - 1; - - const callback = state.allNoop ? nop : (err: Error) => { - for (let n = i; n < buffered.length; ++n) { - buffered[n].callback(err); - } - }; - const chunks = state.allNoop && i === 0 ? buffered : buffered.slice(i); - - doWrite(stream, state, true, state.length, chunks, "", callback); - - resetBuffer(state); - } else { - do { - const { chunk, encoding, callback } = buffered[i]; - const len = objectMode ? 1 : chunk.length; - doWrite(stream, state, false, len, chunk, encoding, callback); - } while (i < buffered.length && !state.writing); - - if (i === buffered.length) { - resetBuffer(state); - } else if (i > 256) { - buffered.splice(0, i); - state.bufferedIndex = 0; - } else { - state.bufferedIndex = i; - } - } - state.bufferProcessing = false; -} - -export function destroy(this: Writable, err?: Error | null, cb?: () => void) { - const w = this._writableState; - - if (w.destroyed) { - if (typeof cb === "function") { - cb(); - } - - return this; - } - - if (err) { - // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 - err.stack; - - if (!w.errored) { - w.errored = err; - } - } - - w.destroyed = true; - - if (!w.constructed) { - this.once(kDestroy, (er) => { - _destroy(this, err || er, cb); - }); - } else { - _destroy(this, err, cb); - } - - return this; -} - -function doWrite( - stream: Duplex | Writable, - state: WritableState, - writev: boolean, - len: number, - // deno-lint-ignore no-explicit-any - chunk: any, - encoding: string, - cb: (error: Error) => void, -) { - state.writelen = len; - state.writecb = cb; - state.writing = true; - state.sync = true; - if (state.destroyed) { - state.onwrite(new ERR_STREAM_DESTROYED("write")); - } else if (writev) { - (stream._writev as unknown as writeV)(chunk, state.onwrite); - } else { - stream._write(chunk, encoding, state.onwrite); - } - state.sync = false; -} - -/** If there's something in the buffer waiting, then invoke callbacks.*/ -export function errorBuffer(state: WritableState) { - if (state.writing) { - return; - } - - for (let n = state.bufferedIndex; n < state.buffered.length; ++n) { - const { chunk, callback } = state.buffered[n]; - const len = state.objectMode ? 1 : chunk.length; - state.length -= len; - callback(new ERR_STREAM_DESTROYED("write")); - } - - for (const callback of state[kOnFinished].splice(0)) { - callback(new ERR_STREAM_DESTROYED("end")); - } - - resetBuffer(state); -} - -export function errorOrDestroy(stream: Writable, err: Error, sync = false) { - const w = stream._writableState; - - if (w.destroyed) { - return stream; - } - - if (w.autoDestroy) { - stream.destroy(err); - } else if (err) { - // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 - err.stack; - - if (!w.errored) { - w.errored = err; - } - if (sync) { - queueMicrotask(() => { - if (w.errorEmitted) { - return; - } - w.errorEmitted = true; - stream.emit("error", err); - }); - } else { - if (w.errorEmitted) { - return; - } - w.errorEmitted = true; - stream.emit("error", err); - } - } -} - -function finish(stream: Writable, state: WritableState) { - state.pendingcb--; - if (state.errorEmitted || state.closeEmitted) { - return; - } - - state.finished = true; - - for (const callback of state[kOnFinished].splice(0)) { - callback(); - } - - stream.emit("finish"); - - if (state.autoDestroy) { - stream.destroy(); - } -} - -export function finishMaybe( - stream: Writable, - state: WritableState, - sync?: boolean, -) { - if (needFinish(state)) { - prefinish(stream, state); - if (state.pendingcb === 0 && needFinish(state)) { - state.pendingcb++; - if (sync) { - queueMicrotask(() => finish(stream, state)); - } else { - finish(stream, state); - } - } - } -} - -export function needFinish(state: WritableState) { - return (state.ending && - state.constructed && - state.length === 0 && - !state.errored && - state.buffered.length === 0 && - !state.finished && - !state.writing); -} - -export function nop() {} - -export function resetBuffer(state: WritableState) { - state.buffered = []; - state.bufferedIndex = 0; - state.allBuffers = true; - state.allNoop = true; -} - -function onwriteError( - stream: Writable, - state: WritableState, - er: Error, - cb: (error: Error) => void, -) { - --state.pendingcb; - - cb(er); - errorBuffer(state); - errorOrDestroy(stream, er); -} - -export function onwrite(stream: Writable, er?: Error | null) { - const state = stream._writableState; - const sync = state.sync; - const cb = state.writecb; - - if (typeof cb !== "function") { - errorOrDestroy(stream, new ERR_MULTIPLE_CALLBACK()); - return; - } - - state.writing = false; - state.writecb = null; - state.length -= state.writelen; - state.writelen = 0; - - if (er) { - // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 - er.stack; - - if (!state.errored) { - state.errored = er; - } - - if (sync) { - queueMicrotask(() => onwriteError(stream, state, er, cb)); - } else { - onwriteError(stream, state, er, cb); - } - } else { - if (state.buffered.length > state.bufferedIndex) { - clearBuffer(stream, state); - } - - if (sync) { - if ( - state.afterWriteTickInfo !== null && - state.afterWriteTickInfo.cb === cb - ) { - state.afterWriteTickInfo.count++; - } else { - state.afterWriteTickInfo = { - count: 1, - cb: (cb as (error?: Error) => void), - stream, - state, - }; - queueMicrotask(() => - afterWriteTick(state.afterWriteTickInfo as AfterWriteTick) - ); - } - } else { - afterWrite(stream, state, 1, cb as (error?: Error) => void); - } - } -} - -export function prefinish(stream: Writable, state: WritableState) { - if (!state.prefinished && !state.finalCalled) { - if (typeof stream._final === "function" && !state.destroyed) { - state.finalCalled = true; - - state.sync = true; - state.pendingcb++; - stream._final((err) => { - state.pendingcb--; - if (err) { - for (const callback of state[kOnFinished].splice(0)) { - callback(err); - } - errorOrDestroy(stream, err, state.sync); - } else if (needFinish(state)) { - state.prefinished = true; - stream.emit("prefinish"); - state.pendingcb++; - queueMicrotask(() => finish(stream, state)); - } - }); - state.sync = false; - } else { - state.prefinished = true; - stream.emit("prefinish"); - } - } -} - -export function writeOrBuffer( - stream: Duplex | Writable, - state: WritableState, - // deno-lint-ignore no-explicit-any - chunk: any, - encoding: string, - callback: (error: Error) => void, -) { - const len = state.objectMode ? 1 : chunk.length; - - state.length += len; - - if (state.writing || state.corked || state.errored || !state.constructed) { - state.buffered.push({ chunk, encoding, callback }); - if (state.allBuffers && encoding !== "buffer") { - state.allBuffers = false; - } - if (state.allNoop && callback !== nop) { - state.allNoop = false; - } - } else { - state.writelen = len; - state.writecb = callback; - state.writing = true; - state.sync = true; - stream._write(chunk, encoding, state.onwrite); - state.sync = false; - } - - const ret = state.length < state.highWaterMark; - - if (!ret) { - state.needDrain = true; - } - - return ret && !state.errored && !state.destroyed; -} diff --git a/std/node/_stream/writable_test.ts b/std/node/_stream/writable_test.ts deleted file mode 100644 index d6133b65f0..0000000000 --- a/std/node/_stream/writable_test.ts +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. MIT License. -import { Buffer } from "../buffer.ts"; -import finished from "./end_of_stream.ts"; -import Writable from "../_stream/writable.ts"; -import { deferred } from "../../async/mod.ts"; -import { - assert, - assertEquals, - assertStrictEquals, - assertThrows, -} from "../../testing/asserts.ts"; - -Deno.test("Writable stream writes correctly", async () => { - let callback: undefined | ((error?: Error | null | undefined) => void); - - let writeExecuted = 0; - const writeExecutedExpected = 1; - const writeExpectedExecutions = deferred(); - - let writevExecuted = 0; - const writevExecutedExpected = 1; - const writevExpectedExecutions = deferred(); - - const writable = new Writable({ - write: (chunk, encoding, cb) => { - writeExecuted++; - if (writeExecuted == writeExecutedExpected) { - writeExpectedExecutions.resolve(); - } - assert(chunk instanceof Buffer); - assertStrictEquals(encoding, "buffer"); - assertStrictEquals(String(chunk), "ABC"); - callback = cb; - }, - writev: (chunks) => { - writevExecuted++; - if (writevExecuted == writevExecutedExpected) { - writevExpectedExecutions.resolve(); - } - assertStrictEquals(chunks.length, 2); - assertStrictEquals(chunks[0].encoding, "buffer"); - assertStrictEquals(chunks[1].encoding, "buffer"); - assertStrictEquals(chunks[0].chunk + chunks[1].chunk, "DEFGHI"); - }, - }); - - writable.write(new TextEncoder().encode("ABC")); - writable.write(new TextEncoder().encode("DEF")); - writable.end(new TextEncoder().encode("GHI")); - callback?.(); - - const writeTimeout = setTimeout( - () => writeExpectedExecutions.reject(), - 1000, - ); - const writevTimeout = setTimeout( - () => writevExpectedExecutions.reject(), - 1000, - ); - await writeExpectedExecutions; - await writevExpectedExecutions; - clearTimeout(writeTimeout); - clearTimeout(writevTimeout); - assertEquals(writeExecuted, writeExecutedExpected); - assertEquals(writevExecuted, writevExecutedExpected); -}); - -Deno.test("Writable stream writes Uint8Array in object mode", async () => { - let writeExecuted = 0; - const writeExecutedExpected = 1; - const writeExpectedExecutions = deferred(); - - const ABC = new TextEncoder().encode("ABC"); - - const writable = new Writable({ - objectMode: true, - write: (chunk, encoding, cb) => { - writeExecuted++; - if (writeExecuted == writeExecutedExpected) { - writeExpectedExecutions.resolve(); - } - assert(!(chunk instanceof Buffer)); - assert(chunk instanceof Uint8Array); - assertEquals(chunk, ABC); - assertEquals(encoding, "utf8"); - cb(); - }, - }); - - writable.end(ABC); - - const writeTimeout = setTimeout( - () => writeExpectedExecutions.reject(), - 1000, - ); - await writeExpectedExecutions; - clearTimeout(writeTimeout); - assertEquals(writeExecuted, writeExecutedExpected); -}); - -Deno.test("Writable stream throws on unexpected close", async () => { - let finishedExecuted = 0; - const finishedExecutedExpected = 1; - const finishedExpectedExecutions = deferred(); - - const writable = new Writable({ - write: () => {}, - }); - writable.writable = false; - writable.destroy(); - - finished(writable, (err) => { - finishedExecuted++; - if (finishedExecuted == finishedExecutedExpected) { - finishedExpectedExecutions.resolve(); - } - assertEquals(err?.code, "ERR_STREAM_PREMATURE_CLOSE"); - }); - - const finishedTimeout = setTimeout( - () => finishedExpectedExecutions.reject(), - 1000, - ); - await finishedExpectedExecutions; - clearTimeout(finishedTimeout); - assertEquals(finishedExecuted, finishedExecutedExpected); -}); - -Deno.test("Writable stream finishes correctly", async () => { - let finishedExecuted = 0; - const finishedExecutedExpected = 1; - const finishedExpectedExecutions = deferred(); - - const w = new Writable({ - write(_chunk, _encoding, cb) { - cb(); - }, - autoDestroy: false, - }); - - w.end("asd"); - - queueMicrotask(() => { - finished(w, () => { - finishedExecuted++; - if (finishedExecuted == finishedExecutedExpected) { - finishedExpectedExecutions.resolve(); - } - }); - }); - - const finishedTimeout = setTimeout( - () => finishedExpectedExecutions.reject(), - 1000, - ); - await finishedExpectedExecutions; - clearTimeout(finishedTimeout); - assertEquals(finishedExecuted, finishedExecutedExpected); -}); - -Deno.test("Writable stream finishes correctly after error", async () => { - let errorExecuted = 0; - const errorExecutedExpected = 1; - const errorExpectedExecutions = deferred(); - - let finishedExecuted = 0; - const finishedExecutedExpected = 1; - const finishedExpectedExecutions = deferred(); - - const w = new Writable({ - write(_chunk, _encoding, cb) { - cb(new Error()); - }, - autoDestroy: false, - }); - w.write("asd"); - w.on("error", () => { - errorExecuted++; - if (errorExecuted == errorExecutedExpected) { - errorExpectedExecutions.resolve(); - } - finished(w, () => { - finishedExecuted++; - if (finishedExecuted == finishedExecutedExpected) { - finishedExpectedExecutions.resolve(); - } - }); - }); - - const errorTimeout = setTimeout( - () => errorExpectedExecutions.reject(), - 1000, - ); - const finishedTimeout = setTimeout( - () => finishedExpectedExecutions.reject(), - 1000, - ); - await finishedExpectedExecutions; - await errorExpectedExecutions; - clearTimeout(finishedTimeout); - clearTimeout(errorTimeout); - assertEquals(finishedExecuted, finishedExecutedExpected); - assertEquals(errorExecuted, errorExecutedExpected); -}); - -Deno.test("Writable stream fails on 'write' null value", () => { - const writable = new Writable(); - assertThrows(() => writable.write(null)); -}); diff --git a/std/node/_util/_util_callbackify.ts b/std/node/_util/_util_callbackify.ts deleted file mode 100644 index e0b862b0e9..0000000000 --- a/std/node/_util/_util_callbackify.ts +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// -// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// These are simplified versions of the "real" errors in Node. -class NodeFalsyValueRejectionError extends Error { - public reason: unknown; - public code = "ERR_FALSY_VALUE_REJECTION"; - constructor(reason: unknown) { - super("Promise was rejected with falsy value"); - this.reason = reason; - } -} -class NodeInvalidArgTypeError extends TypeError { - public code = "ERR_INVALID_ARG_TYPE"; - constructor(argumentName: string) { - super(`The ${argumentName} argument must be of type function.`); - } -} - -type Callback = - | ((err: Error) => void) - | ((err: null, result: ResultT) => void); - -function callbackify( - fn: () => PromiseLike, -): (callback: Callback) => void; -function callbackify( - fn: (arg: ArgT) => PromiseLike, -): (arg: ArgT, callback: Callback) => void; -function callbackify( - fn: (arg1: Arg1T, arg2: Arg2T) => PromiseLike, -): (arg1: Arg1T, arg2: Arg2T, callback: Callback) => void; -function callbackify( - fn: (arg1: Arg1T, arg2: Arg2T, arg3: Arg3T) => PromiseLike, -): (arg1: Arg1T, arg2: Arg2T, arg3: Arg3T, callback: Callback) => void; -function callbackify( - fn: ( - arg1: Arg1T, - arg2: Arg2T, - arg3: Arg3T, - arg4: Arg4T, - ) => PromiseLike, -): ( - arg1: Arg1T, - arg2: Arg2T, - arg3: Arg3T, - arg4: Arg4T, - callback: Callback, -) => void; -function callbackify( - fn: ( - arg1: Arg1T, - arg2: Arg2T, - arg3: Arg3T, - arg4: Arg4T, - arg5: Arg5T, - ) => PromiseLike, -): ( - arg1: Arg1T, - arg2: Arg2T, - arg3: Arg3T, - arg4: Arg4T, - arg5: Arg5T, - callback: Callback, -) => void; - -// deno-lint-ignore no-explicit-any -function callbackify(original: any): any { - if (typeof original !== "function") { - throw new NodeInvalidArgTypeError('"original"'); - } - - const callbackified = function (this: unknown, ...args: unknown[]): void { - const maybeCb = args.pop(); - if (typeof maybeCb !== "function") { - throw new NodeInvalidArgTypeError("last"); - } - const cb = (...args: unknown[]): void => { - maybeCb.apply(this, args); - }; - original.apply(this, args).then( - (ret: unknown) => { - queueMicrotask(cb.bind(this, null, ret)); - }, - (rej: unknown) => { - rej = rej || new NodeFalsyValueRejectionError(rej); - queueMicrotask(cb.bind(this, rej)); - }, - ); - }; - - const descriptors = Object.getOwnPropertyDescriptors(original); - // It is possible to manipulate a functions `length` or `name` property. This - // guards against the manipulation. - if (typeof descriptors.length.value === "number") { - descriptors.length.value++; - } - if (typeof descriptors.name.value === "string") { - descriptors.name.value += "Callbackified"; - } - Object.defineProperties(callbackified, descriptors); - return callbackified; -} - -export { callbackify }; diff --git a/std/node/_util/_util_callbackify_test.ts b/std/node/_util/_util_callbackify_test.ts deleted file mode 100644 index 9e5281409f..0000000000 --- a/std/node/_util/_util_callbackify_test.ts +++ /dev/null @@ -1,390 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// -// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -import { assert, assertStrictEquals } from "../../testing/asserts.ts"; -import { callbackify } from "./_util_callbackify.ts"; - -const values = [ - "hello world", - null, - undefined, - false, - 0, - {}, - { key: "value" }, - Symbol("I am a symbol"), - function ok(): void {}, - ["array", "with", 4, "values"], - new Error("boo"), -]; - -class TestQueue { - #waitingPromise: Promise; - #resolve?: () => void; - #reject?: (err: unknown) => void; - #queueSize = 0; - - constructor() { - this.#waitingPromise = new Promise((resolve, reject) => { - this.#resolve = resolve; - this.#reject = reject; - }); - } - - enqueue(fn: (done: () => void) => void): void { - this.#queueSize++; - try { - fn(() => { - this.#queueSize--; - if (this.#queueSize === 0) { - assert( - this.#resolve, - "Test setup error; async queue is missing #resolve", - ); - this.#resolve(); - } - }); - } catch (err) { - assert(this.#reject, "Test setup error; async queue is missing #reject"); - this.#reject(err); - } - } - - waitForCompletion(): Promise { - return this.#waitingPromise; - } -} - -Deno.test( - "callbackify passes the resolution value as the second argument to the callback", - async () => { - const testQueue = new TestQueue(); - - for (const value of values) { - // deno-lint-ignore require-await - const asyncFn = async (): Promise => { - return value; - }; - const cbAsyncFn = callbackify(asyncFn); - testQueue.enqueue((done) => { - cbAsyncFn((err: unknown, ret: unknown) => { - assertStrictEquals(err, null); - assertStrictEquals(ret, value); - done(); - }); - }); - - const promiseFn = (): Promise => { - return Promise.resolve(value); - }; - const cbPromiseFn = callbackify(promiseFn); - testQueue.enqueue((done) => { - cbPromiseFn((err: unknown, ret: unknown) => { - assertStrictEquals(err, null); - assertStrictEquals(ret, value); - done(); - }); - }); - - // deno-lint-ignore no-explicit-any - const thenableFn = (): PromiseLike => { - return { - // deno-lint-ignore no-explicit-any - then(onfulfilled): PromiseLike { - assert(onfulfilled); - onfulfilled(value); - return this; - }, - }; - }; - const cbThenableFn = callbackify(thenableFn); - testQueue.enqueue((done) => { - cbThenableFn((err: unknown, ret: unknown) => { - assertStrictEquals(err, null); - assertStrictEquals(ret, value); - done(); - }); - }); - } - - await testQueue.waitForCompletion(); - }, -); - -Deno.test( - "callbackify passes the rejection value as the first argument to the callback", - async () => { - const testQueue = new TestQueue(); - - for (const value of values) { - // deno-lint-ignore require-await - const asyncFn = async (): Promise => { - return Promise.reject(value); - }; - const cbAsyncFn = callbackify(asyncFn); - assertStrictEquals(cbAsyncFn.length, 1); - assertStrictEquals(cbAsyncFn.name, "asyncFnCallbackified"); - testQueue.enqueue((done) => { - cbAsyncFn((err: unknown, ret: unknown) => { - assertStrictEquals(ret, undefined); - if (err instanceof Error) { - if ("reason" in err) { - assert(!value); - assertStrictEquals( - // deno-lint-ignore no-explicit-any - (err as any).code, - "ERR_FALSY_VALUE_REJECTION", - ); - // deno-lint-ignore no-explicit-any - assertStrictEquals((err as any).reason, value); - } else { - assertStrictEquals(String(value).endsWith(err.message), true); - } - } else { - assertStrictEquals(err, value); - } - done(); - }); - }); - - const promiseFn = (): Promise => { - return Promise.reject(value); - }; - const obj = {}; - Object.defineProperty(promiseFn, "name", { - value: obj, - writable: false, - enumerable: false, - configurable: true, - }); - - const cbPromiseFn = callbackify(promiseFn); - assertStrictEquals(promiseFn.name, obj); - testQueue.enqueue((done) => { - cbPromiseFn((err: unknown, ret: unknown) => { - assertStrictEquals(ret, undefined); - if (err instanceof Error) { - if ("reason" in err) { - assert(!value); - assertStrictEquals( - // deno-lint-ignore no-explicit-any - (err as any).code, - "ERR_FALSY_VALUE_REJECTION", - ); - // deno-lint-ignore no-explicit-any - assertStrictEquals((err as any).reason, value); - } else { - assertStrictEquals(String(value).endsWith(err.message), true); - } - } else { - assertStrictEquals(err, value); - } - done(); - }); - }); - - const thenableFn = (): PromiseLike => { - return { - then(onfulfilled, onrejected): PromiseLike { - assert(onrejected); - onrejected(value); - return this; - }, - }; - }; - - const cbThenableFn = callbackify(thenableFn); - testQueue.enqueue((done) => { - cbThenableFn((err: unknown, ret: unknown) => { - assertStrictEquals(ret, undefined); - if (err instanceof Error) { - if ("reason" in err) { - assert(!value); - assertStrictEquals( - // deno-lint-ignore no-explicit-any - (err as any).code, - "ERR_FALSY_VALUE_REJECTION", - ); - // deno-lint-ignore no-explicit-any - assertStrictEquals((err as any).reason, value); - } else { - assertStrictEquals(String(value).endsWith(err.message), true); - } - } else { - assertStrictEquals(err, value); - } - done(); - }); - }); - } - - await testQueue.waitForCompletion(); - }, -); - -Deno.test("callbackify passes arguments to the original", async () => { - const testQueue = new TestQueue(); - - for (const value of values) { - // deno-lint-ignore require-await - const asyncFn = async (arg: typeof value): Promise => { - assertStrictEquals(arg, value); - return arg; - }; - - const cbAsyncFn = callbackify(asyncFn); - assertStrictEquals(cbAsyncFn.length, 2); - assert(Object.getPrototypeOf(cbAsyncFn) !== Object.getPrototypeOf(asyncFn)); - assertStrictEquals(Object.getPrototypeOf(cbAsyncFn), Function.prototype); - testQueue.enqueue((done) => { - cbAsyncFn(value, (err: unknown, ret: unknown) => { - assertStrictEquals(err, null); - assertStrictEquals(ret, value); - done(); - }); - }); - - const promiseFn = (arg: typeof value): Promise => { - assertStrictEquals(arg, value); - return Promise.resolve(arg); - }; - const obj = {}; - Object.defineProperty(promiseFn, "length", { - value: obj, - writable: false, - enumerable: false, - configurable: true, - }); - - const cbPromiseFn = callbackify(promiseFn); - assertStrictEquals(promiseFn.length, obj); - testQueue.enqueue((done) => { - cbPromiseFn(value, (err: unknown, ret: unknown) => { - assertStrictEquals(err, null); - assertStrictEquals(ret, value); - done(); - }); - }); - } - - await testQueue.waitForCompletion(); -}); - -Deno.test("callbackify preserves the `this` binding", async () => { - const testQueue = new TestQueue(); - - for (const value of values) { - const objectWithSyncFunction = { - fn(this: unknown, arg: typeof value): Promise { - assertStrictEquals(this, objectWithSyncFunction); - return Promise.resolve(arg); - }, - }; - const cbSyncFunction = callbackify(objectWithSyncFunction.fn); - testQueue.enqueue((done) => { - cbSyncFunction.call(objectWithSyncFunction, value, function ( - this: unknown, - err: unknown, - ret: unknown, - ) { - assertStrictEquals(err, null); - assertStrictEquals(ret, value); - assertStrictEquals(this, objectWithSyncFunction); - done(); - }); - }); - - const objectWithAsyncFunction = { - // deno-lint-ignore require-await - async fn(this: unknown, arg: typeof value): Promise { - assertStrictEquals(this, objectWithAsyncFunction); - return arg; - }, - }; - const cbAsyncFunction = callbackify(objectWithAsyncFunction.fn); - testQueue.enqueue((done) => { - cbAsyncFunction.call(objectWithAsyncFunction, value, function ( - this: unknown, - err: unknown, - ret: unknown, - ) { - assertStrictEquals(err, null); - assertStrictEquals(ret, value); - assertStrictEquals(this, objectWithAsyncFunction); - done(); - }); - }); - } - - await testQueue.waitForCompletion(); -}); - -Deno.test("callbackify throws with non-function inputs", () => { - ["foo", null, undefined, false, 0, {}, Symbol(), []].forEach((value) => { - try { - // deno-lint-ignore no-explicit-any - callbackify(value as any); - throw Error("We should never reach this error"); - } catch (err) { - assert(err instanceof TypeError); - // deno-lint-ignore no-explicit-any - assertStrictEquals((err as any).code, "ERR_INVALID_ARG_TYPE"); - assertStrictEquals(err.name, "TypeError"); - assertStrictEquals( - err.message, - 'The "original" argument must be of type function.', - ); - } - }); -}); - -Deno.test( - "callbackify returns a function that throws if the last argument is not a function", - () => { - // deno-lint-ignore require-await - async function asyncFn(): Promise { - return 42; - } - - // deno-lint-ignore no-explicit-any - const cb = callbackify(asyncFn) as any; - const args: unknown[] = []; - - ["foo", null, undefined, false, 0, {}, Symbol(), []].forEach((value) => { - args.push(value); - - try { - cb(...args); - throw Error("We should never reach this error"); - } catch (err) { - assert(err instanceof TypeError); - // deno-lint-ignore no-explicit-any - assertStrictEquals((err as any).code, "ERR_INVALID_ARG_TYPE"); - assertStrictEquals(err.name, "TypeError"); - assertStrictEquals( - err.message, - "The last argument must be of type function.", - ); - } - }); - }, -); diff --git a/std/node/_util/_util_promisify.ts b/std/node/_util/_util_promisify.ts deleted file mode 100644 index 6692677ecc..0000000000 --- a/std/node/_util/_util_promisify.ts +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// -// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Hack: work around the following TypeScript error: -// error: TS2345 [ERROR]: Argument of type 'typeof kCustomPromisifiedSymbol' -// is not assignable to parameter of type 'typeof kCustomPromisifiedSymbol'. -// assertStrictEquals(kCustomPromisifiedSymbol, promisify.custom); -// ~~~~~~~~~~~~~~~~ -declare const _CustomPromisifiedSymbol: unique symbol; -declare const _CustomPromisifyArgsSymbol: unique symbol; -declare let Symbol: SymbolConstructor; -interface SymbolConstructor { - for(key: "nodejs.util.promisify.custom"): typeof _CustomPromisifiedSymbol; - for( - key: "nodejs.util.promisify.customArgs", - ): typeof _CustomPromisifyArgsSymbol; -} -// End hack. - -// In addition to being accessible through util.promisify.custom, -// this symbol is registered globally and can be accessed in any environment as -// Symbol.for('nodejs.util.promisify.custom'). -const kCustomPromisifiedSymbol = Symbol.for("nodejs.util.promisify.custom"); -// This is an internal Node symbol used by functions returning multiple -// arguments, e.g. ['bytesRead', 'buffer'] for fs.read(). -const kCustomPromisifyArgsSymbol = Symbol.for( - "nodejs.util.promisify.customArgs", -); - -class NodeInvalidArgTypeError extends TypeError { - public code = "ERR_INVALID_ARG_TYPE"; - constructor(argumentName: string, type: string, received: unknown) { - super( - `The "${argumentName}" argument must be of type ${type}. Received ${typeof received}`, - ); - } -} - -export function promisify( - // deno-lint-ignore no-explicit-any - original: (...args: any[]) => void, - // deno-lint-ignore no-explicit-any -): (...args: any[]) => Promise { - if (typeof original !== "function") { - throw new NodeInvalidArgTypeError("original", "Function", original); - } - // deno-lint-ignore no-explicit-any - if ((original as any)[kCustomPromisifiedSymbol]) { - // deno-lint-ignore no-explicit-any - const fn = (original as any)[kCustomPromisifiedSymbol]; - if (typeof fn !== "function") { - throw new NodeInvalidArgTypeError( - "util.promisify.custom", - "Function", - fn, - ); - } - return Object.defineProperty(fn, kCustomPromisifiedSymbol, { - value: fn, - enumerable: false, - writable: false, - configurable: true, - }); - } - - // Names to create an object from in case the callback receives multiple - // arguments, e.g. ['bytesRead', 'buffer'] for fs.read. - // deno-lint-ignore no-explicit-any - const argumentNames = (original as any)[kCustomPromisifyArgsSymbol]; - // deno-lint-ignore no-explicit-any - function fn(this: any, ...args: unknown[]): Promise { - return new Promise((resolve, reject) => { - original.call(this, ...args, (err: Error, ...values: unknown[]) => { - if (err) { - return reject(err); - } - if (argumentNames !== undefined && values.length > 1) { - const obj = {}; - for (let i = 0; i < argumentNames.length; i++) { - // deno-lint-ignore no-explicit-any - (obj as any)[argumentNames[i]] = values[i]; - } - resolve(obj); - } else { - resolve(values[0]); - } - }); - }); - } - - Object.setPrototypeOf(fn, Object.getPrototypeOf(original)); - - Object.defineProperty(fn, kCustomPromisifiedSymbol, { - value: fn, - enumerable: false, - writable: false, - configurable: true, - }); - return Object.defineProperties( - fn, - Object.getOwnPropertyDescriptors(original), - ); -} - -promisify.custom = kCustomPromisifiedSymbol; diff --git a/std/node/_util/_util_promisify_test.ts b/std/node/_util/_util_promisify_test.ts deleted file mode 100644 index 39f7de75c5..0000000000 --- a/std/node/_util/_util_promisify_test.ts +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// -// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -import { - assert, - assertEquals, - assertStrictEquals, - assertThrowsAsync, -} from "../../testing/asserts.ts"; -import { promisify } from "./_util_promisify.ts"; -import * as fs from "../fs.ts"; - -// deno-lint-ignore no-explicit-any -type VoidFunction = (...args: any[]) => void; - -const readFile = promisify(fs.readFile); -const customPromisifyArgs = Symbol.for("nodejs.util.promisify.customArgs"); - -Deno.test( - "Errors should reject the promise", - async function testPromiseRejection() { - await assertThrowsAsync(() => readFile("/dontexist"), Deno.errors.NotFound); - }, -); - -Deno.test("Promisify.custom", async function testPromisifyCustom() { - function fn(): void {} - - function promisifedFn(): void {} - // @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol - fn[promisify.custom] = promisifedFn; - - const promisifiedFnA = promisify(fn); - const promisifiedFnB = promisify(promisifiedFnA); - assertStrictEquals(promisifiedFnA, promisifedFn); - assertStrictEquals(promisifiedFnB, promisifedFn); - - await promisifiedFnA; - await promisifiedFnB; -}); - -Deno.test("promiisfy.custom symbol", function testPromisifyCustomSymbol() { - function fn(): void {} - - function promisifiedFn(): void {} - - // util.promisify.custom is a shared symbol which can be accessed - // as `Symbol.for("nodejs.util.promisify.custom")`. - const kCustomPromisifiedSymbol = Symbol.for("nodejs.util.promisify.custom"); - // @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol - fn[kCustomPromisifiedSymbol] = promisifiedFn; - - assertStrictEquals(kCustomPromisifiedSymbol, promisify.custom); - assertStrictEquals(promisify(fn), promisifiedFn); - assertStrictEquals(promisify(promisify(fn)), promisifiedFn); -}); - -Deno.test("Invalid argument should throw", function testThrowInvalidArgument() { - function fn(): void {} - // @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol - fn[promisify.custom] = 42; - try { - promisify(fn); - } catch (e) { - assertStrictEquals(e.code, "ERR_INVALID_ARG_TYPE"); - assert(e instanceof TypeError); - } -}); - -Deno.test("Custom promisify args", async function testPromisifyCustomArgs() { - const firstValue = 5; - const secondValue = 17; - - function fn(callback: VoidFunction): void { - callback(null, firstValue, secondValue); - } - - // @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol - fn[customPromisifyArgs] = ["first", "second"]; - - const obj = await promisify(fn)(); - assertEquals(obj, { first: firstValue, second: secondValue }); -}); - -Deno.test( - "Multiple callback args without custom promisify args", - async function testPromisifyWithoutCustomArgs() { - function fn(callback: VoidFunction): void { - callback(null, "foo", "bar"); - } - const value = await promisify(fn)(); - assertStrictEquals(value, "foo"); - }, -); - -Deno.test( - "Undefined resolved value", - async function testPromisifyWithUndefinedResolvedValue() { - function fn(callback: VoidFunction): void { - callback(null); - } - const value = await promisify(fn)(); - assertStrictEquals(value, undefined); - }, -); - -Deno.test( - "Undefined resolved value II", - async function testPromisifyWithUndefinedResolvedValueII() { - function fn(callback: VoidFunction): void { - callback(); - } - const value = await promisify(fn)(); - assertStrictEquals(value, undefined); - }, -); - -Deno.test( - "Resolved value: number", - async function testPromisifyWithNumberResolvedValue() { - function fn(err: Error | null, val: number, callback: VoidFunction): void { - callback(err, val); - } - const value = await promisify(fn)(null, 42); - assertStrictEquals(value, 42); - }, -); - -Deno.test( - "Rejected value", - async function testPromisifyWithNumberRejectedValue() { - function fn(err: Error | null, val: null, callback: VoidFunction): void { - callback(err, val); - } - await assertThrowsAsync( - () => promisify(fn)(new Error("oops"), null), - Error, - "oops", - ); - }, -); - -Deno.test("Rejected value", async function testPromisifyWithAsObjectMethod() { - const o: { fn?: VoidFunction } = {}; - const fn = promisify(function (this: unknown, cb: VoidFunction): void { - cb(null, this === o); - }); - - o.fn = fn; - - const val = await o.fn(); - assert(val); -}); - -Deno.test( - "Multiple callback", - async function testPromisifyWithMultipleCallback() { - const err = new Error( - "Should not have called the callback with the error.", - ); - const stack = err.stack; - - const fn = promisify(function (cb: VoidFunction): void { - cb(null); - cb(err); - }); - - await fn(); - await Promise.resolve(); - return assertStrictEquals(stack, err.stack); - }, -); - -Deno.test("Promisify a promise", function testPromisifyPromise() { - function c(): void {} - const a = promisify(function (): void {}); - const b = promisify(a); - assert(c !== a); - assertStrictEquals(a, b); -}); - -Deno.test("Test error", async function testInvalidArguments() { - let errToThrow; - - const thrower = promisify(function ( - a: number, - b: number, - c: number, - cb: VoidFunction, - ): void { - errToThrow = new Error(`${a}-${b}-${c}-${cb}`); - throw errToThrow; - }); - - try { - await thrower(1, 2, 3); - throw new Error(`should've failed`); - } catch (e) { - assertStrictEquals(e, errToThrow); - } -}); - -Deno.test("Test invalid arguments", function testInvalidArguments() { - [undefined, null, true, 0, "str", {}, [], Symbol()].forEach((input) => { - try { - // @ts-expect-error TypeScript - promisify(input); - } catch (e) { - assertStrictEquals(e.code, "ERR_INVALID_ARG_TYPE"); - assert(e instanceof TypeError); - assertEquals( - e.message, - `The "original" argument must be of type Function. Received ${typeof input}`, - ); - } - }); -}); diff --git a/std/node/_util/_util_types.ts b/std/node/_util/_util_types.ts deleted file mode 100644 index f64f5377bf..0000000000 --- a/std/node/_util/_util_types.ts +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// -// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -const _toString = Object.prototype.toString; - -const _isObjectLike = (value: unknown): boolean => - value !== null && typeof value === "object"; - -const _isFunctionLike = (value: unknown): boolean => - value !== null && typeof value === "function"; - -export function isAnyArrayBuffer(value: unknown): boolean { - return ( - _isObjectLike(value) && - (_toString.call(value) === "[object ArrayBuffer]" || - _toString.call(value) === "[object SharedArrayBuffer]") - ); -} - -export function isArrayBufferView(value: unknown): boolean { - return ArrayBuffer.isView(value); -} - -export function isArgumentsObject(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object Arguments]"; -} - -export function isArrayBuffer(value: unknown): boolean { - return ( - _isObjectLike(value) && _toString.call(value) === "[object ArrayBuffer]" - ); -} - -export function isAsyncFunction(value: unknown): boolean { - return ( - _isFunctionLike(value) && _toString.call(value) === "[object AsyncFunction]" - ); -} - -export function isBigInt64Array(value: unknown): boolean { - return ( - _isObjectLike(value) && _toString.call(value) === "[object BigInt64Array]" - ); -} - -export function isBigUint64Array(value: unknown): boolean { - return ( - _isObjectLike(value) && _toString.call(value) === "[object BigUint64Array]" - ); -} - -export function isBooleanObject(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object Boolean]"; -} - -export function isBoxedPrimitive(value: unknown): boolean { - return ( - isBooleanObject(value) || - isStringObject(value) || - isNumberObject(value) || - isSymbolObject(value) || - isBigIntObject(value) - ); -} - -export function isDataView(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object DataView]"; -} - -export function isDate(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object Date]"; -} - -// isExternal: Not implemented - -export function isFloat32Array(value: unknown): boolean { - return ( - _isObjectLike(value) && _toString.call(value) === "[object Float32Array]" - ); -} - -export function isFloat64Array(value: unknown): boolean { - return ( - _isObjectLike(value) && _toString.call(value) === "[object Float64Array]" - ); -} - -export function isGeneratorFunction(value: unknown): boolean { - return ( - _isFunctionLike(value) && - _toString.call(value) === "[object GeneratorFunction]" - ); -} - -export function isGeneratorObject(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object Generator]"; -} - -export function isInt8Array(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object Int8Array]"; -} - -export function isInt16Array(value: unknown): boolean { - return ( - _isObjectLike(value) && _toString.call(value) === "[object Int16Array]" - ); -} - -export function isInt32Array(value: unknown): boolean { - return ( - _isObjectLike(value) && _toString.call(value) === "[object Int32Array]" - ); -} - -export function isMap(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object Map]"; -} - -export function isMapIterator(value: unknown): boolean { - return ( - _isObjectLike(value) && _toString.call(value) === "[object Map Iterator]" - ); -} - -export function isModuleNamespaceObject(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object Module]"; -} - -export function isNativeError(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object Error]"; -} - -export function isNumberObject(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object Number]"; -} - -export function isBigIntObject(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object BigInt]"; -} - -export function isPromise(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object Promise]"; -} - -export function isRegExp(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object RegExp]"; -} - -export function isSet(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object Set]"; -} - -export function isSetIterator(value: unknown): boolean { - return ( - _isObjectLike(value) && _toString.call(value) === "[object Set Iterator]" - ); -} - -export function isSharedArrayBuffer(value: unknown): boolean { - return ( - _isObjectLike(value) && - _toString.call(value) === "[object SharedArrayBuffer]" - ); -} - -export function isStringObject(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object String]"; -} - -export function isSymbolObject(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object Symbol]"; -} - -// Adapted from Lodash -export function isTypedArray(value: unknown): boolean { - /** Used to match `toStringTag` values of typed arrays. */ - const reTypedTag = - /^\[object (?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)Array\]$/; - return _isObjectLike(value) && reTypedTag.test(_toString.call(value)); -} - -export function isUint8Array(value: unknown): boolean { - return ( - _isObjectLike(value) && _toString.call(value) === "[object Uint8Array]" - ); -} - -export function isUint8ClampedArray(value: unknown): boolean { - return ( - _isObjectLike(value) && - _toString.call(value) === "[object Uint8ClampedArray]" - ); -} - -export function isUint16Array(value: unknown): boolean { - return ( - _isObjectLike(value) && _toString.call(value) === "[object Uint16Array]" - ); -} - -export function isUint32Array(value: unknown): boolean { - return ( - _isObjectLike(value) && _toString.call(value) === "[object Uint32Array]" - ); -} - -export function isWeakMap(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object WeakMap]"; -} - -export function isWeakSet(value: unknown): boolean { - return _isObjectLike(value) && _toString.call(value) === "[object WeakSet]"; -} diff --git a/std/node/_util/_util_types_test.ts b/std/node/_util/_util_types_test.ts deleted file mode 100644 index 763969964a..0000000000 --- a/std/node/_util/_util_types_test.ts +++ /dev/null @@ -1,509 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// -// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -import { assertStrictEquals } from "../../testing/asserts.ts"; -import { - isAnyArrayBuffer, - isArgumentsObject, - isArrayBuffer, - isArrayBufferView, - isAsyncFunction, - isBigInt64Array, - isBigIntObject, - isBigUint64Array, - isBooleanObject, - isBoxedPrimitive, - isDataView, - isDate, - isFloat32Array, - isFloat64Array, - isGeneratorFunction, - isGeneratorObject, - isInt16Array, - isInt32Array, - isInt8Array, - isMap, - isMapIterator, - isModuleNamespaceObject, - isNativeError, - isNumberObject, - isPromise, - isRegExp, - isSet, - isSetIterator, - isSharedArrayBuffer, - isStringObject, - isSymbolObject, - isTypedArray, - isUint16Array, - isUint32Array, - isUint8Array, - isUint8ClampedArray, - isWeakMap, - isWeakSet, -} from "./_util_types.ts"; - -// Used to test isModuleNamespaceObject -import * as testModuleNamespaceOpbject from "./_util_types.ts"; - -// isAnyArrayBuffer -Deno.test("Should return true for valid ArrayBuffer types", () => { - assertStrictEquals(isAnyArrayBuffer(new ArrayBuffer(0)), true); - assertStrictEquals(isAnyArrayBuffer(new SharedArrayBuffer(0)), true); -}); - -Deno.test("Should return false for invalid ArrayBuffer types", () => { - assertStrictEquals(isAnyArrayBuffer({}), false); - assertStrictEquals(isAnyArrayBuffer([]), false); - assertStrictEquals(isAnyArrayBuffer(new Error()), false); -}); - -// isArrayBufferView -Deno.test("Should return true for valid ArrayBufferView types", () => { - assertStrictEquals(isArrayBufferView(new Int8Array(0)), true); - assertStrictEquals(isArrayBufferView(new Uint8Array(0)), true); - assertStrictEquals(isArrayBufferView(new Uint8ClampedArray(0)), true); - assertStrictEquals(isArrayBufferView(new Int16Array(0)), true); - assertStrictEquals(isArrayBufferView(new Uint16Array(0)), true); - assertStrictEquals(isArrayBufferView(new Int32Array(0)), true); - assertStrictEquals(isArrayBufferView(new Uint32Array(0)), true); - assertStrictEquals(isArrayBufferView(new Float32Array(0)), true); - assertStrictEquals(isArrayBufferView(new Float64Array(0)), true); - assertStrictEquals(isArrayBufferView(new DataView(new ArrayBuffer(0))), true); -}); - -Deno.test("Should return false for invalid ArrayBufferView types", () => { - assertStrictEquals(isArrayBufferView({}), false); - assertStrictEquals(isArrayBufferView([]), false); - assertStrictEquals(isArrayBufferView(new Error()), false); - assertStrictEquals(isArrayBufferView(new ArrayBuffer(0)), false); -}); - -// isArgumentsObject -// Note: not testable in TS - -Deno.test("Should return false for invalid Argument types", () => { - assertStrictEquals(isArgumentsObject({}), false); - assertStrictEquals(isArgumentsObject([]), false); - assertStrictEquals(isArgumentsObject(new Error()), false); -}); - -// isArrayBuffer -Deno.test("Should return true for valid ArrayBuffer types", () => { - assertStrictEquals(isArrayBuffer(new ArrayBuffer(0)), true); -}); - -Deno.test("Should return false for invalid ArrayBuffer types", () => { - assertStrictEquals(isArrayBuffer(new SharedArrayBuffer(0)), false); - assertStrictEquals(isArrayBuffer({}), false); - assertStrictEquals(isArrayBuffer([]), false); - assertStrictEquals(isArrayBuffer(new Error()), false); -}); - -// isAsyncFunction -Deno.test("Should return true for valid async function types", () => { - const asyncFunction = async (): Promise => {}; - assertStrictEquals(isAsyncFunction(asyncFunction), true); -}); - -Deno.test("Should return false for invalid async function types", () => { - const syncFunction = (): void => {}; - assertStrictEquals(isAsyncFunction(syncFunction), false); - assertStrictEquals(isAsyncFunction({}), false); - assertStrictEquals(isAsyncFunction([]), false); - assertStrictEquals(isAsyncFunction(new Error()), false); -}); - -// isBigInt64Array -Deno.test("Should return true for valid BigInt64Array types", () => { - assertStrictEquals(isBigInt64Array(new BigInt64Array()), true); -}); - -Deno.test("Should return false for invalid BigInt64Array types", () => { - assertStrictEquals(isBigInt64Array(new BigUint64Array()), false); - assertStrictEquals(isBigInt64Array(new Float32Array()), false); - assertStrictEquals(isBigInt64Array(new Int32Array()), false); -}); - -// isBigUint64Array -Deno.test("Should return true for valid isBigUint64Array types", () => { - assertStrictEquals(isBigUint64Array(new BigUint64Array()), true); -}); - -Deno.test("Should return false for invalid isBigUint64Array types", () => { - assertStrictEquals(isBigUint64Array(new BigInt64Array()), false); - assertStrictEquals(isBigUint64Array(new Float32Array()), false); - assertStrictEquals(isBigUint64Array(new Int32Array()), false); -}); - -// isBooleanObject -Deno.test("Should return true for valid Boolean object types", () => { - assertStrictEquals(isBooleanObject(new Boolean(false)), true); - assertStrictEquals(isBooleanObject(new Boolean(true)), true); -}); - -Deno.test("Should return false for invalid isBigUint64Array types", () => { - assertStrictEquals(isBooleanObject(false), false); - assertStrictEquals(isBooleanObject(true), false); - assertStrictEquals(isBooleanObject(Boolean(false)), false); - assertStrictEquals(isBooleanObject(Boolean(true)), false); -}); - -// isBoxedPrimitive -Deno.test("Should return true for valid boxed primitive values", () => { - assertStrictEquals(isBoxedPrimitive(new Boolean(false)), true); - assertStrictEquals(isBoxedPrimitive(Object(Symbol("foo"))), true); - assertStrictEquals(isBoxedPrimitive(Object(BigInt(5))), true); - assertStrictEquals(isBoxedPrimitive(new String("foo")), true); -}); - -Deno.test("Should return false for invalid boxed primitive values", () => { - assertStrictEquals(isBoxedPrimitive(false), false); - assertStrictEquals(isBoxedPrimitive(Symbol("foo")), false); -}); - -// isDateView -Deno.test("Should return true for valid DataView types", () => { - assertStrictEquals(isDataView(new DataView(new ArrayBuffer(0))), true); -}); - -Deno.test("Should return false for invalid DataView types", () => { - assertStrictEquals(isDataView(new Float64Array(0)), false); -}); - -// isDate -Deno.test("Should return true for valid date types", () => { - assertStrictEquals(isDate(new Date()), true); - assertStrictEquals(isDate(new Date(0)), true); - assertStrictEquals(isDate(new (eval("Date"))()), true); -}); - -Deno.test("Should return false for invalid date types", () => { - assertStrictEquals(isDate(Date()), false); - assertStrictEquals(isDate({}), false); - assertStrictEquals(isDate([]), false); - assertStrictEquals(isDate(new Error()), false); - assertStrictEquals(isDate(Object.create(Date.prototype)), false); -}); - -// isFloat32Array -Deno.test("Should return true for valid Float32Array types", () => { - assertStrictEquals(isFloat32Array(new Float32Array(0)), true); -}); - -Deno.test("Should return false for invalid Float32Array types", () => { - assertStrictEquals(isFloat32Array(new ArrayBuffer(0)), false); - assertStrictEquals(isFloat32Array(new Float64Array(0)), false); -}); - -// isFloat64Array -Deno.test("Should return true for valid Float64Array types", () => { - assertStrictEquals(isFloat64Array(new Float64Array(0)), true); -}); - -Deno.test("Should return false for invalid Float64Array types", () => { - assertStrictEquals(isFloat64Array(new ArrayBuffer(0)), false); - assertStrictEquals(isFloat64Array(new Uint8Array(0)), false); -}); - -// isGeneratorFunction -Deno.test("Should return true for valid generator functions", () => { - assertStrictEquals( - isGeneratorFunction(function* foo() {}), - true, - ); -}); - -Deno.test("Should return false for invalid generator functions", () => { - assertStrictEquals( - isGeneratorFunction(function foo() {}), - false, - ); -}); - -// isGeneratorObject -Deno.test("Should return true for valid generator object types", () => { - function* foo(): Iterator {} - assertStrictEquals(isGeneratorObject(foo()), true); -}); - -Deno.test("Should return false for invalid generation object types", () => { - assertStrictEquals( - isGeneratorObject(function* foo() {}), - false, - ); -}); - -// isInt8Array -Deno.test("Should return true for valid Int8Array types", () => { - assertStrictEquals(isInt8Array(new Int8Array(0)), true); -}); - -Deno.test("Should return false for invalid Int8Array types", () => { - assertStrictEquals(isInt8Array(new ArrayBuffer(0)), false); - assertStrictEquals(isInt8Array(new Float64Array(0)), false); -}); - -// isInt16Array -Deno.test("Should return true for valid Int16Array types", () => { - assertStrictEquals(isInt16Array(new Int16Array(0)), true); -}); - -Deno.test("Should return false for invalid Int16Array type", () => { - assertStrictEquals(isInt16Array(new ArrayBuffer(0)), false); - assertStrictEquals(isInt16Array(new Float64Array(0)), false); -}); - -// isInt32Array -Deno.test("Should return true for valid isInt32Array types", () => { - assertStrictEquals(isInt32Array(new Int32Array(0)), true); -}); - -Deno.test("Should return false for invalid isInt32Array type", () => { - assertStrictEquals(isInt32Array(new ArrayBuffer(0)), false); - assertStrictEquals(isInt32Array(new Float64Array(0)), false); -}); - -// isStringObject -Deno.test("Should return true for valid String types", () => { - assertStrictEquals(isStringObject(new String("")), true); - assertStrictEquals(isStringObject(new String("Foo")), true); -}); - -Deno.test("Should return false for invalid String types", () => { - assertStrictEquals(isStringObject(""), false); - assertStrictEquals(isStringObject("Foo"), false); -}); - -// isMap -Deno.test("Should return true for valid Map types", () => { - assertStrictEquals(isMap(new Map()), true); -}); - -Deno.test("Should return false for invalid Map types", () => { - assertStrictEquals(isMap({}), false); - assertStrictEquals(isMap([]), false); - assertStrictEquals(isMap(new Date()), false); - assertStrictEquals(isMap(new Error()), false); -}); - -// isMapIterator -Deno.test("Should return true for valid Map Iterator types", () => { - const map = new Map(); - assertStrictEquals(isMapIterator(map.keys()), true); - assertStrictEquals(isMapIterator(map.values()), true); - assertStrictEquals(isMapIterator(map.entries()), true); - assertStrictEquals(isMapIterator(map[Symbol.iterator]()), true); -}); - -Deno.test("Should return false for invalid Map iterator types", () => { - assertStrictEquals(isMapIterator(new Map()), false); - assertStrictEquals(isMapIterator([]), false); - assertStrictEquals(isMapIterator(new Date()), false); - assertStrictEquals(isMapIterator(new Error()), false); -}); - -// isModuleNamespaceObject -Deno.test("Should return true for valid module namespace objects", () => { - assertStrictEquals(isModuleNamespaceObject(testModuleNamespaceOpbject), true); -}); - -Deno.test("Should return false for invalid module namespace objects", () => { - assertStrictEquals(isModuleNamespaceObject(assertStrictEquals), false); -}); - -// isNativeError -Deno.test("Should return true for valid Error types", () => { - assertStrictEquals(isNativeError(new Error()), true); - assertStrictEquals(isNativeError(new TypeError()), true); - assertStrictEquals(isNativeError(new RangeError()), true); -}); - -Deno.test("Should return false for invalid Error types", () => { - assertStrictEquals(isNativeError(null), false); - assertStrictEquals(isNativeError(NaN), false); -}); - -// isNumberObject -Deno.test("Should return true for valid number objects", () => { - assertStrictEquals(isNumberObject(new Number(0)), true); -}); - -Deno.test("Should return false for invalid number types", () => { - assertStrictEquals(isNumberObject(0), false); -}); - -// isBigIntObject -Deno.test("Should return true for valid number objects", () => { - assertStrictEquals(isBigIntObject(new Object(BigInt(42))), true); -}); - -Deno.test("Should return false for invalid number types", () => { - assertStrictEquals(isBigIntObject(BigInt(42)), false); -}); - -// isPromise -Deno.test("Should return true for valid Promise types", () => { - assertStrictEquals(isPromise(Promise.resolve(42)), true); -}); - -Deno.test("Should return false for invalid Promise types", () => { - assertStrictEquals(isPromise(new Object()), false); -}); - -// isRegExp -Deno.test("Should return true for valid RegExp", () => { - assertStrictEquals(isRegExp(/abc/), true); - assertStrictEquals(isRegExp(new RegExp("abc")), true); -}); - -Deno.test("Should return false for invalid RegExp types", () => { - assertStrictEquals(isRegExp({}), false); - assertStrictEquals(isRegExp("/abc/"), false); -}); - -// isSet -Deno.test("Should return true for valid Set types", () => { - assertStrictEquals(isSet(new Set()), true); -}); - -Deno.test("Should return false for invalid Set types", () => { - assertStrictEquals(isSet({}), false); - assertStrictEquals(isSet([]), false); - assertStrictEquals(isSet(new Map()), false); - assertStrictEquals(isSet(new Error()), false); -}); - -// isSetIterator -Deno.test("Should return true for valid Set Iterator types", () => { - const set = new Set(); - assertStrictEquals(isSetIterator(set.keys()), true); - assertStrictEquals(isSetIterator(set.values()), true); - assertStrictEquals(isSetIterator(set.entries()), true); - assertStrictEquals(isSetIterator(set[Symbol.iterator]()), true); -}); - -Deno.test("Should return false for invalid Set Iterator types", () => { - assertStrictEquals(isSetIterator(new Set()), false); - assertStrictEquals(isSetIterator([]), false); - assertStrictEquals(isSetIterator(new Map()), false); - assertStrictEquals(isSetIterator(new Error()), false); -}); - -// isSharedArrayBuffer -Deno.test("Should return true for valid SharedArrayBuffer types", () => { - assertStrictEquals(isSharedArrayBuffer(new SharedArrayBuffer(0)), true); -}); - -Deno.test("Should return false for invalid SharedArrayBuffer types", () => { - assertStrictEquals(isSharedArrayBuffer(new ArrayBuffer(0)), false); -}); - -// isStringObject -Deno.test("Should return true for valid String Object types", () => { - assertStrictEquals(isStringObject(new String("")), true); - assertStrictEquals(isStringObject(new String("Foo")), true); -}); - -Deno.test("Should return false for invalid String Object types", () => { - assertStrictEquals(isStringObject(""), false); - assertStrictEquals(isStringObject("Foo"), false); -}); - -// isSymbolObject -Deno.test("Should return true for valid Symbol types", () => { - assertStrictEquals(isSymbolObject(Object(Symbol("foo"))), true); -}); - -Deno.test("Should return false for invalid Symbol types", () => { - assertStrictEquals(isSymbolObject(Symbol("foo")), false); -}); - -// isTypedArray -Deno.test("Should return true for valid TypedArray types", () => { - assertStrictEquals(isTypedArray(new Uint8Array(0)), true); - assertStrictEquals(isTypedArray(new Float64Array(0)), true); -}); - -Deno.test("Should return false for invalid TypedArray types", () => { - assertStrictEquals(isTypedArray(new ArrayBuffer(0)), false); -}); - -// isUint8Array -Deno.test("Should return true for valid Uint8Array types", () => { - assertStrictEquals(isUint8Array(new Uint8Array(0)), true); -}); - -Deno.test("Should return false for invalid Uint8Array types", () => { - assertStrictEquals(isUint8Array(new ArrayBuffer(0)), false); - assertStrictEquals(isUint8Array(new Float64Array(0)), false); -}); - -// isUint8ClampedArray -Deno.test("Should return true for valid Uint8ClampedArray types", () => { - assertStrictEquals(isUint8ClampedArray(new Uint8ClampedArray(0)), true); -}); - -Deno.test("Should return false for invalid Uint8Array types", () => { - assertStrictEquals(isUint8ClampedArray(new ArrayBuffer(0)), false); - assertStrictEquals(isUint8ClampedArray(new Float64Array(0)), false); -}); - -// isUint16Array -Deno.test("Should return true for valid isUint16Array types", () => { - assertStrictEquals(isUint16Array(new Uint16Array(0)), true); -}); - -Deno.test("Should return false for invalid Uint16Array types", () => { - assertStrictEquals(isUint16Array(new ArrayBuffer(0)), false); - assertStrictEquals(isUint16Array(new Float64Array(0)), false); -}); - -// isUint32Array -Deno.test("Should return true for valid Uint32Array types", () => { - assertStrictEquals(isUint32Array(new Uint32Array(0)), true); -}); - -Deno.test("Should return false for invalid isUint16Array types", () => { - assertStrictEquals(isUint32Array(new ArrayBuffer(0)), false); - assertStrictEquals(isUint32Array(new Float64Array(0)), false); -}); - -// isWeakMap -Deno.test("Should return true for valid WeakMap types", () => { - assertStrictEquals(isWeakMap(new WeakMap()), true); -}); - -Deno.test("Should return false for invalid WeakMap types", () => { - assertStrictEquals(isWeakMap(new Set()), false); - assertStrictEquals(isWeakMap(new Map()), false); -}); - -// isWeakSet -Deno.test("Should return true for valid WeakSet types", () => { - assertStrictEquals(isWeakSet(new WeakSet()), true); -}); - -Deno.test("Should return false for invalid WeakSet types", () => { - assertStrictEquals(isWeakSet(new Set()), false); - assertStrictEquals(isWeakSet(new Map()), false); -}); diff --git a/std/node/_utils.ts b/std/node/_utils.ts deleted file mode 100644 index 62a911843f..0000000000 --- a/std/node/_utils.ts +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { deferred } from "../async/mod.ts"; -import { assert, assertStringIncludes, fail } from "../testing/asserts.ts"; - -export type BinaryEncodings = "binary"; - -export type TextEncodings = - | "ascii" - | "utf8" - | "utf-8" - | "utf16le" - | "ucs2" - | "ucs-2" - | "base64" - | "latin1" - | "hex"; - -export type Encodings = BinaryEncodings | TextEncodings; - -export function notImplemented(msg?: string): never { - const message = msg ? `Not implemented: ${msg}` : "Not implemented"; - throw new Error(message); -} - -export type _TextDecoder = typeof TextDecoder.prototype; -export const _TextDecoder = TextDecoder; - -export type _TextEncoder = typeof TextEncoder.prototype; -export const _TextEncoder = TextEncoder; - -// API helpers - -export type MaybeNull = T | null; -export type MaybeDefined = T | undefined; -export type MaybeEmpty = T | null | undefined; - -export function intoCallbackAPI( - // deno-lint-ignore no-explicit-any - func: (...args: any[]) => Promise, - cb: MaybeEmpty<(err: MaybeNull, value?: MaybeEmpty) => void>, - // deno-lint-ignore no-explicit-any - ...args: any[] -): void { - func(...args).then( - (value) => cb && cb(null, value), - (err) => cb && cb(err), - ); -} - -export function intoCallbackAPIWithIntercept( - // deno-lint-ignore no-explicit-any - func: (...args: any[]) => Promise, - interceptor: (v: T1) => T2, - cb: MaybeEmpty<(err: MaybeNull, value?: MaybeEmpty) => void>, - // deno-lint-ignore no-explicit-any - ...args: any[] -): void { - func(...args).then( - (value) => cb && cb(null, interceptor(value)), - (err) => cb && cb(err), - ); -} - -export function spliceOne(list: string[], index: number): void { - for (; index + 1 < list.length; index++) list[index] = list[index + 1]; - list.pop(); -} - -// Taken from: https://github.com/nodejs/node/blob/ba684805b6c0eded76e5cd89ee00328ac7a59365/lib/internal/util.js#L125 -// Return undefined if there is no match. -// Move the "slow cases" to a separate function to make sure this function gets -// inlined properly. That prioritizes the common case. -export function normalizeEncoding( - enc: string | null, -): TextEncodings | undefined { - if (enc == null || enc === "utf8" || enc === "utf-8") return "utf8"; - return slowCases(enc); -} - -// https://github.com/nodejs/node/blob/ba684805b6c0eded76e5cd89ee00328ac7a59365/lib/internal/util.js#L130 -function slowCases(enc: string): TextEncodings | undefined { - switch (enc.length) { - case 4: - if (enc === "UTF8") return "utf8"; - if (enc === "ucs2" || enc === "UCS2") return "utf16le"; - enc = `${enc}`.toLowerCase(); - if (enc === "utf8") return "utf8"; - if (enc === "ucs2") return "utf16le"; - break; - case 3: - if (enc === "hex" || enc === "HEX" || `${enc}`.toLowerCase() === "hex") { - return "hex"; - } - break; - case 5: - if (enc === "ascii") return "ascii"; - if (enc === "ucs-2") return "utf16le"; - if (enc === "UTF-8") return "utf8"; - if (enc === "ASCII") return "ascii"; - if (enc === "UCS-2") return "utf16le"; - enc = `${enc}`.toLowerCase(); - if (enc === "utf-8") return "utf8"; - if (enc === "ascii") return "ascii"; - if (enc === "ucs-2") return "utf16le"; - break; - case 6: - if (enc === "base64") return "base64"; - if (enc === "latin1" || enc === "binary") return "latin1"; - if (enc === "BASE64") return "base64"; - if (enc === "LATIN1" || enc === "BINARY") return "latin1"; - enc = `${enc}`.toLowerCase(); - if (enc === "base64") return "base64"; - if (enc === "latin1" || enc === "binary") return "latin1"; - break; - case 7: - if ( - enc === "utf16le" || - enc === "UTF16LE" || - `${enc}`.toLowerCase() === "utf16le" - ) { - return "utf16le"; - } - break; - case 8: - if ( - enc === "utf-16le" || - enc === "UTF-16LE" || - `${enc}`.toLowerCase() === "utf-16le" - ) { - return "utf16le"; - } - break; - default: - if (enc === "") return "utf8"; - } -} - -export function validateIntegerRange( - value: number, - name: string, - min = -2147483648, - max = 2147483647, -): void { - // The defaults for min and max correspond to the limits of 32-bit integers. - if (!Number.isInteger(value)) { - throw new Error(`${name} must be 'an integer' but was ${value}`); - } - - if (value < min || value > max) { - throw new Error( - `${name} must be >= ${min} && <= ${max}. Value was ${value}`, - ); - } -} - -type OptionalSpread = T extends undefined ? [] - : [T]; - -export function once( - callback: (...args: OptionalSpread) => void, -) { - let called = false; - return function (this: unknown, ...args: OptionalSpread) { - if (called) return; - called = true; - callback.apply(this, args); - }; -} - -/** - * @param {number} [expectedExecutions = 1] - * @param {number} [timeout = 1000] Milliseconds to wait before the promise is forcefully exited -*/ -export function mustCall( - fn: ((...args: T) => void) = () => {}, - expectedExecutions = 1, - timeout = 1000, -): [Promise, (...args: T) => void] { - if (expectedExecutions < 1) { - throw new Error("Expected executions can't be lower than 1"); - } - let timesExecuted = 0; - const completed = deferred(); - - const abort = setTimeout(() => completed.reject(), timeout); - - function callback(this: unknown, ...args: T) { - timesExecuted++; - if (timesExecuted === expectedExecutions) { - completed.resolve(); - } - fn.apply(this, args); - } - - const result = completed - .then(() => clearTimeout(abort)) - .catch(() => - fail( - `Async operation not completed: Expected ${expectedExecutions}, executed ${timesExecuted}`, - ) - ); - - return [ - result, - callback, - ]; -} -/** Asserts that an error thrown in a callback will not be wrongly caught. */ -export async function assertCallbackErrorUncaught( - { prelude, invocation, cleanup }: { - /** Any code which needs to run before the actual invocation (notably, any import statements). */ - prelude?: string; - /** - * The start of the invocation of the function, e.g. `open("foo.txt", `. - * The callback will be added after it. - */ - invocation: string; - /** Called after the subprocess is finished but before running the assertions, e.g. to clean up created files. */ - cleanup?: () => Promise | void; - }, -) { - // Since the error has to be uncaught, and that will kill the Deno process, - // the only way to test this is to spawn a subprocess. - const p = Deno.run({ - cmd: [ - Deno.execPath(), - "eval", - "--no-check", // Running TSC for every one of these tests would take way too long - "--unstable", - `${prelude ?? ""} - - ${invocation}(err) => { - // If the bug is present and the callback is called again with an error, - // don't throw another error, so if the subprocess fails we know it had the correct behaviour. - if (!err) throw new Error("success"); - });`, - ], - stderr: "piped", - }); - const status = await p.status(); - const stderr = new TextDecoder().decode(await Deno.readAll(p.stderr)); - p.close(); - p.stderr.close(); - await cleanup?.(); - assert(!status.success); - assertStringIncludes(stderr, "Error: success"); -} diff --git a/std/node/assert.ts b/std/node/assert.ts deleted file mode 100644 index 7b144b6909..0000000000 --- a/std/node/assert.ts +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -export { AssertionError } from "./assertion_error.ts"; -import { - assertEquals as deepStrictEqual, - AssertionError, - assertMatch as match, - assertNotEquals as notDeepStrictEqual, - assertNotStrictEquals as notStrictEqual, - assertStrictEquals as strictEqual, - assertThrows as throws, - fail, -} from "../testing/asserts.ts"; - -function assert(expr: unknown, msg = ""): asserts expr { - if (!expr) { - throw new AssertionError(msg); - } -} -const ok = assert; -export default assert; - -Object.assign(assert, { - deepStrictEqual, - fail, - match, - notDeepStrictEqual, - notStrictEqual, - ok, - strictEqual, - throws, -}); - -export { - deepStrictEqual, - fail, - match, - notDeepStrictEqual, - notStrictEqual, - ok, - strictEqual, - throws, -}; diff --git a/std/node/assert_test.ts b/std/node/assert_test.ts deleted file mode 100644 index ab4bec79bc..0000000000 --- a/std/node/assert_test.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assert as denoAssert, - assertEquals, - assertMatch, - assertNotEquals, - assertNotStrictEquals, - assertStrictEquals, - assertThrows, - fail as denoFail, -} from "../testing/asserts.ts"; - -import AssertionError from "./assertion_error.ts"; - -import assert, { - AssertionError as AssertionError_, - deepStrictEqual, - fail, - match, - notDeepStrictEqual, - notStrictEqual, - ok, - strictEqual, - throws, -} from "./assert.ts"; - -Deno.test("API should be exposed", () => { - assertStrictEquals(assert, ok, "`assert()` should be an alias of `ok()`"); - assertStrictEquals( - assertEquals, - deepStrictEqual, - "`assertEquals()` should be exposed as `deepStrictEqual()`", - ); - assertStrictEquals( - assertNotEquals, - notDeepStrictEqual, - "`assertNotEquals()` should be exposed as `notDeepStrictEqual()`", - ); - assertStrictEquals( - assertStrictEquals, - strictEqual, - "`assertStrictEquals()` should be exposed as `strictEqual()`", - ); - assertStrictEquals( - assertNotStrictEquals, - notStrictEqual, - "`assertNotStrictEquals()` should be exposed as `notStrictEqual()`", - ); - assertStrictEquals( - assertMatch, - match, - "`assertMatch()` should be exposed as `match()`", - ); - assertStrictEquals( - assertThrows, - throws, - "`assertThrows()` should be exposed as `throws()`", - ); - assertStrictEquals(fail, denoFail, "`fail()` should be exposed"); - assertStrictEquals( - AssertionError, - AssertionError_, - "`AssertionError()` constructor should be exposed", - ); -}); diff --git a/std/node/assertion_error.ts b/std/node/assertion_error.ts deleted file mode 100644 index dcce1478b1..0000000000 --- a/std/node/assertion_error.ts +++ /dev/null @@ -1,576 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors. - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: - -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// TODO(schwarzkopfb): change this when `Deno.consoleSize()` will be stable -interface DenoUnstable { - consoleSize?(rid: number): { columns: number }; -} -function getConsoleWidth(): number { - return (Deno as DenoUnstable).consoleSize?.(Deno.stderr.rid).columns ?? 80; -} - -import { inspect } from "./util.ts"; -import { stripColor as removeColors } from "../fmt/colors.ts"; - -// TODO(schwarzkopfb): we should implement Node's concept of "primordials" -// Ref: https://github.com/denoland/deno/issues/6040#issuecomment-637305828 -const MathMax = Math.max; -const { Error } = globalThis; -const { - create: ObjectCreate, - defineProperty: ObjectDefineProperty, - getPrototypeOf: ObjectGetPrototypeOf, - getOwnPropertyDescriptor: ObjectGetOwnPropertyDescriptor, - keys: ObjectKeys, -} = Object; - -import { ERR_INVALID_ARG_TYPE } from "./_errors.ts"; - -let blue = ""; -let green = ""; -let red = ""; -let defaultColor = ""; - -const kReadableOperator: { [key: string]: string } = { - deepStrictEqual: "Expected values to be strictly deep-equal:", - strictEqual: "Expected values to be strictly equal:", - strictEqualObject: 'Expected "actual" to be reference-equal to "expected":', - deepEqual: "Expected values to be loosely deep-equal:", - notDeepStrictEqual: 'Expected "actual" not to be strictly deep-equal to:', - notStrictEqual: 'Expected "actual" to be strictly unequal to:', - notStrictEqualObject: - 'Expected "actual" not to be reference-equal to "expected":', - notDeepEqual: 'Expected "actual" not to be loosely deep-equal to:', - notIdentical: "Values have same structure but are not reference-equal:", - notDeepEqualUnequal: "Expected values not to be loosely deep-equal:", -}; - -// Comparing short primitives should just show === / !== instead of using the -// diff. -const kMaxShortLength = 12; - -export function copyError(source: Error): Error { - const keys = ObjectKeys(source); - const target = ObjectCreate(ObjectGetPrototypeOf(source)); - for (const key of keys) { - const desc = ObjectGetOwnPropertyDescriptor(source, key); - - if (desc !== undefined) { - ObjectDefineProperty(target, key, desc); - } - } - ObjectDefineProperty(target, "message", { value: source.message }); - return target; -} - -export function inspectValue(val: unknown): string { - // The util.inspect default values could be changed. This makes sure the - // error messages contain the necessary information nevertheless. - return inspect( - val, - { - compact: false, - customInspect: false, - depth: 1000, - maxArrayLength: Infinity, - // Assert compares only enumerable properties (with a few exceptions). - showHidden: false, - // Assert does not detect proxies currently. - showProxy: false, - sorted: true, - // Inspect getters as we also check them when comparing entries. - getters: true, - }, - ); -} - -export function createErrDiff( - actual: unknown, - expected: unknown, - operator: string, -): string { - let other = ""; - let res = ""; - let end = ""; - let skipped = false; - const actualInspected = inspectValue(actual); - const actualLines = actualInspected.split("\n"); - const expectedLines = inspectValue(expected).split("\n"); - - let i = 0; - let indicator = ""; - - // In case both values are objects or functions explicitly mark them as not - // reference equal for the `strictEqual` operator. - if ( - operator === "strictEqual" && - ((typeof actual === "object" && actual !== null && - typeof expected === "object" && expected !== null) || - (typeof actual === "function" && typeof expected === "function")) - ) { - operator = "strictEqualObject"; - } - - // If "actual" and "expected" fit on a single line and they are not strictly - // equal, check further special handling. - if ( - actualLines.length === 1 && expectedLines.length === 1 && - actualLines[0] !== expectedLines[0] - ) { - // Check for the visible length using the `removeColors()` function, if - // appropriate. - const c = inspect.defaultOptions.colors; - const actualRaw = c ? removeColors(actualLines[0]) : actualLines[0]; - const expectedRaw = c ? removeColors(expectedLines[0]) : expectedLines[0]; - const inputLength = actualRaw.length + expectedRaw.length; - // If the character length of "actual" and "expected" together is less than - // kMaxShortLength and if neither is an object and at least one of them is - // not `zero`, use the strict equal comparison to visualize the output. - if (inputLength <= kMaxShortLength) { - if ( - (typeof actual !== "object" || actual === null) && - (typeof expected !== "object" || expected === null) && - (actual !== 0 || expected !== 0) - ) { // -0 === +0 - return `${kReadableOperator[operator]}\n\n` + - `${actualLines[0]} !== ${expectedLines[0]}\n`; - } - } else if (operator !== "strictEqualObject") { - // If the stderr is a tty and the input length is lower than the current - // columns per line, add a mismatch indicator below the output. If it is - // not a tty, use a default value of 80 characters. - const maxLength = Deno.isatty(Deno.stderr.rid) ? getConsoleWidth() : 80; - if (inputLength < maxLength) { - while (actualRaw[i] === expectedRaw[i]) { - i++; - } - // Ignore the first characters. - if (i > 2) { - // Add position indicator for the first mismatch in case it is a - // single line and the input length is less than the column length. - indicator = `\n ${" ".repeat(i)}^`; - i = 0; - } - } - } - } - - // Remove all ending lines that match (this optimizes the output for - // readability by reducing the number of total changed lines). - let a = actualLines[actualLines.length - 1]; - let b = expectedLines[expectedLines.length - 1]; - while (a === b) { - if (i++ < 3) { - end = `\n ${a}${end}`; - } else { - other = a; - } - actualLines.pop(); - expectedLines.pop(); - if (actualLines.length === 0 || expectedLines.length === 0) { - break; - } - a = actualLines[actualLines.length - 1]; - b = expectedLines[expectedLines.length - 1]; - } - - const maxLines = MathMax(actualLines.length, expectedLines.length); - // Strict equal with identical objects that are not identical by reference. - // E.g., assert.deepStrictEqual({ a: Symbol() }, { a: Symbol() }) - if (maxLines === 0) { - // We have to get the result again. The lines were all removed before. - const actualLines = actualInspected.split("\n"); - - // Only remove lines in case it makes sense to collapse those. - if (actualLines.length > 50) { - actualLines[46] = `${blue}...${defaultColor}`; - while (actualLines.length > 47) { - actualLines.pop(); - } - } - - return `${kReadableOperator.notIdentical}\n\n${actualLines.join("\n")}\n`; - } - - // There were at least five identical lines at the end. Mark a couple of - // skipped. - if (i >= 5) { - end = `\n${blue}...${defaultColor}${end}`; - skipped = true; - } - if (other !== "") { - end = `\n ${other}${end}`; - other = ""; - } - - let printedLines = 0; - let identical = 0; - const msg = kReadableOperator[operator] + - `\n${green}+ actual${defaultColor} ${red}- expected${defaultColor}`; - const skippedMsg = ` ${blue}...${defaultColor} Lines skipped`; - - let lines = actualLines; - let plusMinus = `${green}+${defaultColor}`; - let maxLength = expectedLines.length; - if (actualLines.length < maxLines) { - lines = expectedLines; - plusMinus = `${red}-${defaultColor}`; - maxLength = actualLines.length; - } - - for (i = 0; i < maxLines; i++) { - if (maxLength < i + 1) { - // If more than two former lines are identical, print them. Collapse them - // in case more than five lines were identical. - if (identical > 2) { - if (identical > 3) { - if (identical > 4) { - if (identical === 5) { - res += `\n ${lines[i - 3]}`; - printedLines++; - } else { - res += `\n${blue}...${defaultColor}`; - skipped = true; - } - } - res += `\n ${lines[i - 2]}`; - printedLines++; - } - res += `\n ${lines[i - 1]}`; - printedLines++; - } - // No identical lines before. - identical = 0; - // Add the expected line to the cache. - if (lines === actualLines) { - res += `\n${plusMinus} ${lines[i]}`; - } else { - other += `\n${plusMinus} ${lines[i]}`; - } - printedLines++; - // Only extra actual lines exist - // Lines diverge - } else { - const expectedLine = expectedLines[i]; - let actualLine = actualLines[i]; - // If the lines diverge, specifically check for lines that only diverge by - // a trailing comma. In that case it is actually identical and we should - // mark it as such. - let divergingLines = actualLine !== expectedLine && - (!actualLine.endsWith(",") || - actualLine.slice(0, -1) !== expectedLine); - // If the expected line has a trailing comma but is otherwise identical, - // add a comma at the end of the actual line. Otherwise the output could - // look weird as in: - // - // [ - // 1 // No comma at the end! - // + 2 - // ] - // - if ( - divergingLines && - expectedLine.endsWith(",") && - expectedLine.slice(0, -1) === actualLine - ) { - divergingLines = false; - actualLine += ","; - } - if (divergingLines) { - // If more than two former lines are identical, print them. Collapse - // them in case more than five lines were identical. - if (identical > 2) { - if (identical > 3) { - if (identical > 4) { - if (identical === 5) { - res += `\n ${actualLines[i - 3]}`; - printedLines++; - } else { - res += `\n${blue}...${defaultColor}`; - skipped = true; - } - } - res += `\n ${actualLines[i - 2]}`; - printedLines++; - } - res += `\n ${actualLines[i - 1]}`; - printedLines++; - } - // No identical lines before. - identical = 0; - // Add the actual line to the result and cache the expected diverging - // line so consecutive diverging lines show up as +++--- and not +-+-+-. - res += `\n${green}+${defaultColor} ${actualLine}`; - other += `\n${red}-${defaultColor} ${expectedLine}`; - printedLines += 2; - // Lines are identical - } else { - // Add all cached information to the result before adding other things - // and reset the cache. - res += other; - other = ""; - identical++; - // The very first identical line since the last diverging line is be - // added to the result. - if (identical <= 2) { - res += `\n ${actualLine}`; - printedLines++; - } - } - } - // Inspected object to big (Show ~50 rows max) - if (printedLines > 50 && i < maxLines - 2) { - return `${msg}${skippedMsg}\n${res}\n${blue}...${defaultColor}${other}\n` + - `${blue}...${defaultColor}`; - } - } - - return `${msg}${skipped ? skippedMsg : ""}\n${res}${other}${end}${indicator}`; -} - -export interface AssertionErrorDetailsDescriptor { - message: string; - actual: unknown; - expected: unknown; - operator: string; - stack: Error; -} - -export interface AssertionErrorConstructorOptions { - message?: string; - actual?: unknown; - expected?: unknown; - operator?: string; - details?: AssertionErrorDetailsDescriptor[]; - // deno-lint-ignore ban-types - stackStartFn?: Function; - // Compatibility with older versions. - // deno-lint-ignore ban-types - stackStartFunction?: Function; -} - -interface ErrorWithStackTraceLimit extends ErrorConstructor { - stackTraceLimit: number; -} - -export class AssertionError extends Error { - [key: string]: unknown - - // deno-lint-ignore constructor-super - constructor(options: AssertionErrorConstructorOptions) { - if (typeof options !== "object" || options === null) { - throw new ERR_INVALID_ARG_TYPE("options", "Object", options); - } - const { - message, - operator, - stackStartFn, - details, - // Compatibility with older versions. - stackStartFunction, - } = options; - let { - actual, - expected, - } = options; - - // TODO(schwarzkopfb): `stackTraceLimit` should be added to `ErrorConstructor` in - // cli/dts/lib.deno.shared_globals.d.ts - const limit = (Error as ErrorWithStackTraceLimit).stackTraceLimit; - (Error as ErrorWithStackTraceLimit).stackTraceLimit = 0; - - if (message != null) { - super(String(message)); - } else { - if (Deno.isatty(Deno.stderr.rid)) { - // Reset on each call to make sure we handle dynamically set environment - // variables correct. - if (Deno.noColor) { - blue = ""; - green = ""; - defaultColor = ""; - red = ""; - } else { - blue = "\u001b[34m"; - green = "\u001b[32m"; - defaultColor = "\u001b[39m"; - red = "\u001b[31m"; - } - } - // Prevent the error stack from being visible by duplicating the error - // in a very close way to the original in case both sides are actually - // instances of Error. - if ( - typeof actual === "object" && actual !== null && - typeof expected === "object" && expected !== null && - "stack" in actual && actual instanceof Error && - "stack" in expected && expected instanceof Error - ) { - actual = copyError(actual); - expected = copyError(expected); - } - - if (operator === "deepStrictEqual" || operator === "strictEqual") { - super(createErrDiff(actual, expected, operator)); - } else if ( - operator === "notDeepStrictEqual" || - operator === "notStrictEqual" - ) { - // In case the objects are equal but the operator requires unequal, show - // the first object and say A equals B - let base = kReadableOperator[operator]; - const res = inspectValue(actual).split("\n"); - - // In case "actual" is an object or a function, it should not be - // reference equal. - if ( - operator === "notStrictEqual" && - ((typeof actual === "object" && actual !== null) || - typeof actual === "function") - ) { - base = kReadableOperator.notStrictEqualObject; - } - - // Only remove lines in case it makes sense to collapse those. - if (res.length > 50) { - res[46] = `${blue}...${defaultColor}`; - while (res.length > 47) { - res.pop(); - } - } - - // Only print a single input. - if (res.length === 1) { - super(`${base}${res[0].length > 5 ? "\n\n" : " "}${res[0]}`); - } else { - super(`${base}\n\n${res.join("\n")}\n`); - } - } else { - let res = inspectValue(actual); - let other = inspectValue(expected); - const knownOperator = kReadableOperator[operator ?? ""]; - if (operator === "notDeepEqual" && res === other) { - res = `${knownOperator}\n\n${res}`; - if (res.length > 1024) { - res = `${res.slice(0, 1021)}...`; - } - super(res); - } else { - if (res.length > 512) { - res = `${res.slice(0, 509)}...`; - } - if (other.length > 512) { - other = `${other.slice(0, 509)}...`; - } - if (operator === "deepEqual") { - res = `${knownOperator}\n\n${res}\n\nshould loosely deep-equal\n\n`; - } else { - const newOp = kReadableOperator[`${operator}Unequal`]; - if (newOp) { - res = `${newOp}\n\n${res}\n\nshould not loosely deep-equal\n\n`; - } else { - other = ` ${operator} ${other}`; - } - } - super(`${res}${other}`); - } - } - } - - (Error as ErrorWithStackTraceLimit).stackTraceLimit = limit; - - this.generatedMessage = !message; - ObjectDefineProperty(this, "name", { - value: "AssertionError [ERR_ASSERTION]", - enumerable: false, - writable: true, - configurable: true, - }); - this.code = "ERR_ASSERTION"; - - if (details) { - this.actual = undefined; - this.expected = undefined; - this.operator = undefined; - - for (let i = 0; i < details.length; i++) { - this["message " + i] = details[i].message; - this["actual " + i] = details[i].actual; - this["expected " + i] = details[i].expected; - this["operator " + i] = details[i].operator; - this["stack trace " + i] = details[i].stack; - } - } else { - this.actual = actual; - this.expected = expected; - this.operator = operator; - } - - Error.captureStackTrace(this, stackStartFn || stackStartFunction); - // Create error message including the error code in the name. - this.stack; - // Reset the name. - this.name = "AssertionError"; - } - - toString() { - return `${this.name} [${this.code}]: ${this.message}`; - } - - [inspect.custom](recurseTimes: number, ctx: Record) { - // Long strings should not be fully inspected. - const tmpActual = this.actual; - const tmpExpected = this.expected; - - for (const name of ["actual", "expected"]) { - if (typeof this[name] === "string") { - const value = (this[name] as string); - const lines = value.split("\n"); - if (lines.length > 10) { - lines.length = 10; - this[name] = `${lines.join("\n")}\n...`; - } else if (value.length > 512) { - this[name] = `${value.slice(512)}...`; - } - } - } - - // This limits the `actual` and `expected` property default inspection to - // the minimum depth. Otherwise those values would be too verbose compared - // to the actual error message which contains a combined view of these two - // input values. - const result = inspect(this, { - ...ctx, - customInspect: false, - depth: 0, - }); - - // Reset the properties after inspection. - this.actual = tmpActual; - this.expected = tmpExpected; - - return result; - } -} - -export default AssertionError; diff --git a/std/node/assertion_error_test.ts b/std/node/assertion_error_test.ts deleted file mode 100644 index f4c9f2b190..0000000000 --- a/std/node/assertion_error_test.ts +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { stripColor } from "../fmt/colors.ts"; -import { - assert, - assertEquals, - assertNotStrictEquals, - assertStrictEquals, -} from "../testing/asserts.ts"; -import { - AssertionError, - copyError, - createErrDiff, - inspectValue, -} from "./assertion_error.ts"; - -Deno.test({ - name: "copyError()", - fn() { - class TestError extends Error {} - const err = new TestError("this is a test"); - const copy = copyError(err); - - assert(copy instanceof Error, "Copy should inherit from Error."); - assert(copy instanceof TestError, "Copy should inherit from TestError."); - assertEquals(copy, err, "Copy should be equal to the original error."); - assertNotStrictEquals( - copy, - err, - "Copy should not be strictly equal to the original error.", - ); - }, -}); - -Deno.test({ - name: "inspectValue()", - fn() { - const obj = { a: 1, b: [2] }; - Object.defineProperty(obj, "c", { value: 3, enumerable: false }); - assertStrictEquals( - stripColor(inspectValue(obj)), - "{ a: 1, b: [ 2 ] }", - ); - }, -}); - -Deno.test({ - name: "createErrDiff()", - fn() { - assertStrictEquals( - stripColor( - createErrDiff({ a: 1, b: 2 }, { a: 2, b: 2 }, "strictEqual"), - ), - stripColor( - 'Expected "actual" to be reference-equal to "expected":' + "\n" + - "+ actual - expected" + "\n" + - "\n" + - "+ { a: 1, b: 2 }" + "\n" + - "- { a: 2, b: 2 }", - ), - ); - }, -}); - -Deno.test({ - name: "construct AssertionError() with given message", - fn() { - const err = new AssertionError( - { - message: "answer", - actual: "42", - expected: "42", - operator: "notStrictEqual", - }, - ); - assertStrictEquals(err.name, "AssertionError"); - assertStrictEquals(err.message, "answer"); - assertStrictEquals(err.generatedMessage, false); - assertStrictEquals(err.code, "ERR_ASSERTION"); - assertStrictEquals(err.actual, "42"); - assertStrictEquals(err.expected, "42"); - assertStrictEquals(err.operator, "notStrictEqual"); - }, -}); - -Deno.test({ - name: "construct AssertionError() with generated message", - fn() { - const err = new AssertionError( - { actual: 1, expected: 2, operator: "equal" }, - ); - assertStrictEquals(err.name, "AssertionError"); - assertStrictEquals(stripColor(err.message), "1 equal 2"); - assertStrictEquals(err.generatedMessage, true); - assertStrictEquals(err.code, "ERR_ASSERTION"); - assertStrictEquals(err.actual, 1); - assertStrictEquals(err.expected, 2); - assertStrictEquals(err.operator, "equal"); - }, -}); - -Deno.test({ - name: "construct AssertionError() with stackStartFn", - fn: function stackStartFn() { - const expected = /node/; - const err = new AssertionError({ - actual: "deno", - expected, - operator: "match", - stackStartFn, - }); - assertStrictEquals(err.name, "AssertionError"); - assertStrictEquals(stripColor(err.message), `"deno" match /node/`); - assertStrictEquals(err.generatedMessage, true); - assertStrictEquals(err.code, "ERR_ASSERTION"); - assertStrictEquals(err.actual, "deno"); - assertStrictEquals(err.expected, expected); - assertStrictEquals(err.operator, "match"); - assert(err.stack, "error should have a stack"); - assert( - !err.stack?.includes("stackStartFn"), - "stackStartFn() should not present in stack trace", - ); - }, -}); - -Deno.test({ - name: "error details", - fn() { - const stack0 = new Error(); - const stack1 = new Error(); - const err = new AssertionError({ - message: "Function(s) were not called the expected number of times", - details: [ - { - message: - "Expected the calls function to be executed 2 time(s) but was executed 3 time(s).", - actual: 3, - expected: 2, - operator: "calls", - stack: stack0, - }, - { - message: - "Expected the fn function to be executed 1 time(s) but was executed 0 time(s).", - actual: 0, - expected: 1, - operator: "fn", - stack: stack1, - }, - ], - }); - - assertStrictEquals( - err.message, - "Function(s) were not called the expected number of times", - ); - - assertStrictEquals( - err["message 0"], - "Expected the calls function to be executed 2 time(s) but was executed 3 time(s).", - ); - assertStrictEquals(err["actual 0"], 3); - assertStrictEquals(err["expected 0"], 2); - assertStrictEquals(err["operator 0"], "calls"); - assertStrictEquals(err["stack trace 0"], stack0); - - assertStrictEquals( - err["message 1"], - "Expected the fn function to be executed 1 time(s) but was executed 0 time(s).", - ); - assertStrictEquals(err["actual 1"], 0); - assertStrictEquals(err["expected 1"], 1); - assertStrictEquals(err["operator 1"], "fn"); - assertStrictEquals(err["stack trace 1"], stack1); - }, -}); diff --git a/std/node/buffer.ts b/std/node/buffer.ts deleted file mode 100644 index 379a2c09a8..0000000000 --- a/std/node/buffer.ts +++ /dev/null @@ -1,601 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import * as hex from "../encoding/hex.ts"; -import * as base64 from "../encoding/base64.ts"; -import { Encodings, normalizeEncoding, notImplemented } from "./_utils.ts"; - -const notImplementedEncodings = [ - "ascii", - "binary", - "latin1", - "ucs2", - "utf16le", -]; - -function checkEncoding(encoding = "utf8", strict = true): Encodings { - if (typeof encoding !== "string" || (strict && encoding === "")) { - if (!strict) return "utf8"; - throw new TypeError(`Unkown encoding: ${encoding}`); - } - - const normalized = normalizeEncoding(encoding); - - if (normalized === undefined) { - throw new TypeError(`Unkown encoding: ${encoding}`); - } - - if (notImplementedEncodings.includes(encoding)) { - notImplemented(`"${encoding}" encoding`); - } - - return normalized; -} - -interface EncodingOp { - byteLength(string: string): number; -} - -// https://github.com/nodejs/node/blob/56dbe466fdbc598baea3bfce289bf52b97b8b8f7/lib/buffer.js#L598 -const encodingOps: { [key: string]: EncodingOp } = { - utf8: { - byteLength: (string: string): number => - new TextEncoder().encode(string).byteLength, - }, - ucs2: { - byteLength: (string: string): number => string.length * 2, - }, - utf16le: { - byteLength: (string: string): number => string.length * 2, - }, - latin1: { - byteLength: (string: string): number => string.length, - }, - ascii: { - byteLength: (string: string): number => string.length, - }, - base64: { - byteLength: (string: string): number => - base64ByteLength(string, string.length), - }, - hex: { - byteLength: (string: string): number => string.length >>> 1, - }, -}; - -function base64ByteLength(str: string, bytes: number): number { - // Handle padding - if (str.charCodeAt(bytes - 1) === 0x3d) bytes--; - if (bytes > 1 && str.charCodeAt(bytes - 1) === 0x3d) bytes--; - - // Base64 ratio: 3/4 - return (bytes * 3) >>> 2; -} - -/** - * See also https://nodejs.org/api/buffer.html - */ -export class Buffer extends Uint8Array { - /** - * Allocates a new Buffer of size bytes. - */ - static alloc( - size: number, - fill?: number | string | Uint8Array | Buffer, - encoding = "utf8", - ): Buffer { - if (typeof size !== "number") { - throw new TypeError( - `The "size" argument must be of type number. Received type ${typeof size}`, - ); - } - - const buf = new Buffer(size); - if (size === 0) return buf; - - let bufFill; - if (typeof fill === "string") { - const clearEncoding = checkEncoding(encoding); - if ( - typeof fill === "string" && - fill.length === 1 && - clearEncoding === "utf8" - ) { - buf.fill(fill.charCodeAt(0)); - } else bufFill = Buffer.from(fill, clearEncoding); - } else if (typeof fill === "number") { - buf.fill(fill); - } else if (fill instanceof Uint8Array) { - if (fill.length === 0) { - throw new TypeError( - `The argument "value" is invalid. Received ${fill.constructor.name} []`, - ); - } - - bufFill = fill; - } - - if (bufFill) { - if (bufFill.length > buf.length) { - bufFill = bufFill.subarray(0, buf.length); - } - - let offset = 0; - while (offset < size) { - buf.set(bufFill, offset); - offset += bufFill.length; - if (offset + bufFill.length >= size) break; - } - if (offset !== size) { - buf.set(bufFill.subarray(0, size - offset), offset); - } - } - - return buf; - } - - static allocUnsafe(size: number): Buffer { - return new Buffer(size); - } - - /** - * Returns the byte length of a string when encoded. This is not the same as - * String.prototype.length, which does not account for the encoding that is - * used to convert the string into bytes. - */ - static byteLength( - string: string | Buffer | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, - encoding = "utf8", - ): number { - if (typeof string != "string") return string.byteLength; - - encoding = normalizeEncoding(encoding) || "utf8"; - return encodingOps[encoding].byteLength(string); - } - - /** - * Returns a new Buffer which is the result of concatenating all the Buffer - * instances in the list together. - */ - static concat(list: Buffer[] | Uint8Array[], totalLength?: number): Buffer { - if (totalLength == undefined) { - totalLength = 0; - for (const buf of list) { - totalLength += buf.length; - } - } - - const buffer = Buffer.allocUnsafe(totalLength); - let pos = 0; - for (const item of list) { - let buf: Buffer; - if (!(item instanceof Buffer)) { - buf = Buffer.from(item); - } else { - buf = item; - } - buf.copy(buffer, pos); - pos += buf.length; - } - - return buffer; - } - - /** - * Allocates a new Buffer using an array of bytes in the range 0 – 255. Array - * entries outside that range will be truncated to fit into it. - */ - static from(array: number[]): Buffer; - /** - * This creates a view of the ArrayBuffer without copying the underlying - * memory. For example, when passed a reference to the .buffer property of a - * TypedArray instance, the newly created Buffer will share the same allocated - * memory as the TypedArray. - */ - static from( - arrayBuffer: ArrayBuffer | SharedArrayBuffer, - byteOffset?: number, - length?: number, - ): Buffer; - /** - * Copies the passed buffer data onto a new Buffer instance. - */ - static from(buffer: Buffer | Uint8Array): Buffer; - /** - * Creates a new Buffer containing string. - */ - static from(string: string, encoding?: string): Buffer; - static from( - // deno-lint-ignore no-explicit-any - value: any, - offsetOrEncoding?: number | string, - length?: number, - ): Buffer { - const offset = typeof offsetOrEncoding === "string" - ? undefined - : offsetOrEncoding; - let encoding = typeof offsetOrEncoding === "string" - ? offsetOrEncoding - : undefined; - - if (typeof value == "string") { - encoding = checkEncoding(encoding, false); - if (encoding === "hex") return new Buffer(hex.decodeString(value).buffer); - if (encoding === "base64") return new Buffer(base64.decode(value).buffer); - return new Buffer(new TextEncoder().encode(value).buffer); - } - - // workaround for https://github.com/microsoft/TypeScript/issues/38446 - return new Buffer(value, offset!, length); - } - - /** - * Returns true if obj is a Buffer, false otherwise. - */ - static isBuffer(obj: unknown): obj is Buffer { - return obj instanceof Buffer; - } - - // deno-lint-ignore no-explicit-any - static isEncoding(encoding: any): boolean { - return ( - typeof encoding === "string" && - encoding.length !== 0 && - normalizeEncoding(encoding) !== undefined - ); - } - - /** - * Copies data from a region of buf to a region in target, even if the target - * memory region overlaps with buf. - */ - copy( - targetBuffer: Buffer | Uint8Array, - targetStart = 0, - sourceStart = 0, - sourceEnd = this.length, - ): number { - const sourceBuffer = this - .subarray(sourceStart, sourceEnd) - .subarray(0, Math.max(0, targetBuffer.length - targetStart)); - - if (sourceBuffer.length === 0) return 0; - - targetBuffer.set(sourceBuffer, targetStart); - return sourceBuffer.length; - } - - /* - * Returns true if both buf and otherBuffer have exactly the same bytes, false otherwise. - */ - equals(otherBuffer: Uint8Array | Buffer): boolean { - if (!(otherBuffer instanceof Uint8Array)) { - throw new TypeError( - `The "otherBuffer" argument must be an instance of Buffer or Uint8Array. Received type ${typeof otherBuffer}`, - ); - } - - if (this === otherBuffer) return true; - if (this.byteLength !== otherBuffer.byteLength) return false; - - for (let i = 0; i < this.length; i++) { - if (this[i] !== otherBuffer[i]) return false; - } - - return true; - } - - readBigInt64BE(offset = 0): bigint { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getBigInt64(offset); - } - readBigInt64LE(offset = 0): bigint { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getBigInt64(offset, true); - } - - readBigUInt64BE(offset = 0): bigint { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getBigUint64(offset); - } - readBigUInt64LE(offset = 0): bigint { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getBigUint64(offset, true); - } - - readDoubleBE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getFloat64(offset); - } - readDoubleLE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getFloat64(offset, true); - } - - readFloatBE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getFloat32(offset); - } - readFloatLE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getFloat32(offset, true); - } - - readInt8(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt8( - offset, - ); - } - - readInt16BE(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt16( - offset, - ); - } - readInt16LE(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt16( - offset, - true, - ); - } - - readInt32BE(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt32( - offset, - ); - } - readInt32LE(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getInt32( - offset, - true, - ); - } - - readUInt8(offset = 0): number { - return new DataView(this.buffer, this.byteOffset, this.byteLength).getUint8( - offset, - ); - } - - readUInt16BE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getUint16(offset); - } - readUInt16LE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getUint16(offset, true); - } - - readUInt32BE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getUint32(offset); - } - readUInt32LE(offset = 0): number { - return new DataView( - this.buffer, - this.byteOffset, - this.byteLength, - ).getUint32(offset, true); - } - - /** - * Returns a new Buffer that references the same memory as the original, but - * offset and cropped by the start and end indices. - */ - slice(begin = 0, end = this.length): Buffer { - // workaround for https://github.com/microsoft/TypeScript/issues/38665 - return this.subarray(begin, end) as Buffer; - } - - /** - * Returns a JSON representation of buf. JSON.stringify() implicitly calls - * this function when stringifying a Buffer instance. - */ - toJSON(): Record { - return { type: "Buffer", data: Array.from(this) }; - } - - /** - * Decodes buf to a string according to the specified character encoding in - * encoding. start and end may be passed to decode only a subset of buf. - */ - toString(encoding = "utf8", start = 0, end = this.length): string { - encoding = checkEncoding(encoding); - - const b = this.subarray(start, end); - if (encoding === "hex") return hex.encodeToString(b); - if (encoding === "base64") return base64.encode(b.buffer); - - return new TextDecoder(encoding).decode(b); - } - - /** - * Writes string to buf at offset according to the character encoding in - * encoding. The length parameter is the number of bytes to write. If buf did - * not contain enough space to fit the entire string, only part of string will - * be written. However, partially encoded characters will not be written. - */ - write(string: string, offset = 0, length = this.length): number { - return new TextEncoder().encodeInto( - string, - this.subarray(offset, offset + length), - ).written; - } - - writeBigInt64BE(value: bigint, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setBigInt64( - offset, - value, - ); - return offset + 4; - } - writeBigInt64LE(value: bigint, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setBigInt64( - offset, - value, - true, - ); - return offset + 4; - } - - writeBigUInt64BE(value: bigint, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setBigUint64( - offset, - value, - ); - return offset + 4; - } - writeBigUInt64LE(value: bigint, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setBigUint64( - offset, - value, - true, - ); - return offset + 4; - } - - writeDoubleBE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat64( - offset, - value, - ); - return offset + 8; - } - writeDoubleLE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat64( - offset, - value, - true, - ); - return offset + 8; - } - - writeFloatBE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat32( - offset, - value, - ); - return offset + 4; - } - writeFloatLE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setFloat32( - offset, - value, - true, - ); - return offset + 4; - } - - writeInt8(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setInt8( - offset, - value, - ); - return offset + 1; - } - - writeInt16BE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setInt16( - offset, - value, - ); - return offset + 2; - } - writeInt16LE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setInt16( - offset, - value, - true, - ); - return offset + 2; - } - - writeInt32BE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( - offset, - value, - ); - return offset + 4; - } - writeInt32LE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setInt32( - offset, - value, - true, - ); - return offset + 4; - } - - writeUInt8(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint8( - offset, - value, - ); - return offset + 1; - } - - writeUInt16BE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint16( - offset, - value, - ); - return offset + 2; - } - writeUInt16LE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint16( - offset, - value, - true, - ); - return offset + 2; - } - - writeUInt32BE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( - offset, - value, - ); - return offset + 4; - } - writeUInt32LE(value: number, offset = 0): number { - new DataView(this.buffer, this.byteOffset, this.byteLength).setUint32( - offset, - value, - true, - ); - return offset + 4; - } -} - -export default { Buffer }; diff --git a/std/node/buffer_test.ts b/std/node/buffer_test.ts deleted file mode 100644 index f3fee8e295..0000000000 --- a/std/node/buffer_test.ts +++ /dev/null @@ -1,649 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals, assertThrows } from "../testing/asserts.ts"; -import { Buffer } from "./buffer.ts"; - -Deno.test({ - name: "alloc fails on negative numbers", - fn() { - assertThrows( - () => { - Buffer.alloc(-1); - }, - RangeError, - "Invalid typed array length: -1", - "should throw on negative numbers", - ); - }, -}); - -Deno.test({ - name: "alloc fails if size is not a number", - fn() { - const invalidSizes = [{}, "1", "foo", []]; - - for (const size of invalidSizes) { - assertThrows( - () => { - // deno-lint-ignore ban-ts-comment - // @ts-expect-error - Buffer.alloc(size); - }, - TypeError, - `The "size" argument must be of type number. Received type ${typeof size}`, - "should throw on non-number size", - ); - } - }, -}); - -Deno.test({ - name: "alloc(>0) fails if value is an empty Buffer/Uint8Array", - fn() { - const invalidValues = [new Uint8Array(), Buffer.alloc(0)]; - - for (const value of invalidValues) { - assertThrows( - () => { - console.log(value.constructor.name); - Buffer.alloc(1, value); - }, - TypeError, - `The argument "value" is invalid. Received ${value.constructor.name} []`, - "should throw for empty Buffer/Uint8Array", - ); - } - }, -}); - -Deno.test({ - name: "alloc(0) doesn't fail if value is an empty Buffer/Uint8Array", - fn() { - const invalidValues = [new Uint8Array(), Buffer.alloc(0)]; - - for (const value of invalidValues) { - assertEquals(Buffer.alloc(0, value).length, 0); - } - }, -}); - -Deno.test({ - name: "alloc allocates a buffer with the expected size", - fn() { - const buffer: Buffer = Buffer.alloc(1); - assertEquals(buffer.length, 1, "Buffer size should be 1"); - assertEquals(buffer[0], 0, "Content should be filled with 0"); - }, -}); - -Deno.test({ - name: "alloc(0) creates an empty buffer", - fn() { - const buffer: Buffer = Buffer.alloc(0); - assertEquals(buffer.length, 0, "Buffer size should be 0"); - }, -}); - -Deno.test({ - name: "allocUnsafe allocates a buffer with the expected size", - fn() { - const buffer: Buffer = Buffer.allocUnsafe(1); - assertEquals(buffer.length, 1, "Buffer size should be 1"); - }, -}); - -Deno.test({ - name: "allocUnsafe(0) creates an empty buffer", - fn() { - const buffer: Buffer = Buffer.allocUnsafe(0); - assertEquals(buffer.length, 0, "Buffer size should be 0"); - }, -}); - -Deno.test({ - name: "alloc filled correctly with integer", - fn() { - const buffer: Buffer = Buffer.alloc(3, 5); - assertEquals(buffer, new Uint8Array([5, 5, 5])); - }, -}); - -Deno.test({ - name: "alloc filled correctly with single character", - fn() { - assertEquals(Buffer.alloc(5, "a"), new Uint8Array([97, 97, 97, 97, 97])); - }, -}); - -Deno.test({ - name: "alloc filled correctly with base64 string", - fn() { - assertEquals( - Buffer.alloc(11, "aGVsbG8gd29ybGQ=", "base64"), - new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]), - ); - }, -}); - -Deno.test({ - name: "alloc filled correctly with hex string", - fn() { - assertEquals( - Buffer.alloc(4, "64656e6f", "hex"), - new Uint8Array([100, 101, 110, 111]), - ); - }, -}); - -Deno.test({ - name: "alloc filled correctly with hex string smaller than alloc size", - fn() { - assertEquals( - Buffer.alloc(13, "64656e6f", "hex").toString(), - "denodenodenod", - ); - }, -}); - -Deno.test({ - name: "alloc filled correctly with Uint8Array smaller than alloc size", - fn() { - assertEquals( - Buffer.alloc(7, new Uint8Array([100, 101])), - new Uint8Array([100, 101, 100, 101, 100, 101, 100]), - ); - assertEquals( - Buffer.alloc(6, new Uint8Array([100, 101])), - new Uint8Array([100, 101, 100, 101, 100, 101]), - ); - }, -}); - -Deno.test({ - name: "alloc filled correctly with Uint8Array bigger than alloc size", - fn() { - assertEquals( - Buffer.alloc(1, new Uint8Array([100, 101])), - new Uint8Array([100]), - ); - }, -}); - -Deno.test({ - name: "alloc filled correctly with Buffer", - fn() { - assertEquals( - Buffer.alloc(6, new Buffer([100, 101])), - new Uint8Array([100, 101, 100, 101, 100, 101]), - ); - assertEquals( - Buffer.alloc(7, new Buffer([100, 101])), - new Uint8Array([100, 101, 100, 101, 100, 101, 100]), - ); - }, -}); - -// tests from: -// https://github.com/nodejs/node/blob/56dbe466fdbc598baea3bfce289bf52b97b8b8f7/test/parallel/test-buffer-bytelength.js#L70 -Deno.test({ - name: "Byte length is the expected for strings", - fn() { - // Special case: zero length string - assertEquals(Buffer.byteLength("", "ascii"), 0); - assertEquals(Buffer.byteLength("", "HeX"), 0); - - // utf8 - assertEquals(Buffer.byteLength("∑éllö wørl∂!", "utf-8"), 19); - assertEquals(Buffer.byteLength("κλμνξο", "utf8"), 12); - assertEquals(Buffer.byteLength("挵挶挷挸挹", "utf-8"), 15); - assertEquals(Buffer.byteLength("𠝹𠱓𠱸", "UTF8"), 12); - // Without an encoding, utf8 should be assumed - assertEquals(Buffer.byteLength("hey there"), 9); - assertEquals(Buffer.byteLength("𠱸挶νξ#xx :)"), 17); - assertEquals(Buffer.byteLength("hello world", ""), 11); - // It should also be assumed with unrecognized encoding - assertEquals(Buffer.byteLength("hello world", "abc"), 11); - assertEquals(Buffer.byteLength("ßœ∑≈", "unkn0wn enc0ding"), 10); - - // base64 - assertEquals(Buffer.byteLength("aGVsbG8gd29ybGQ=", "base64"), 11); - assertEquals(Buffer.byteLength("aGVsbG8gd29ybGQ=", "BASE64"), 11); - assertEquals(Buffer.byteLength("bm9kZS5qcyByb2NrcyE=", "base64"), 14); - assertEquals(Buffer.byteLength("aGkk", "base64"), 3); - assertEquals( - Buffer.byteLength("bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw==", "base64"), - 25, - ); - // special padding - assertEquals(Buffer.byteLength("aaa=", "base64"), 2); - assertEquals(Buffer.byteLength("aaaa==", "base64"), 3); - - assertEquals(Buffer.byteLength("Il était tué"), 14); - assertEquals(Buffer.byteLength("Il était tué", "utf8"), 14); - - ["ascii", "latin1", "binary"] - .reduce((es: string[], e: string) => es.concat(e, e.toUpperCase()), []) - .forEach((encoding: string) => { - assertEquals(Buffer.byteLength("Il était tué", encoding), 12); - }); - - ["ucs2", "ucs-2", "utf16le", "utf-16le"] - .reduce((es: string[], e: string) => es.concat(e, e.toUpperCase()), []) - .forEach((encoding: string) => { - assertEquals(Buffer.byteLength("Il était tué", encoding), 24); - }); - }, -}); - -Deno.test({ - name: "Byte length is the expected one for non-strings", - fn() { - assertEquals( - Buffer.byteLength(Buffer.alloc(0)), - Buffer.alloc(0).byteLength, - "Byte lenght differs on buffers", - ); - }, -}); - -Deno.test({ - name: "Two Buffers are concatenated", - fn() { - const data1 = [1, 2, 3]; - const data2 = [4, 5, 6]; - - const buffer1 = Buffer.from(data1); - const buffer2 = Buffer.from(data2); - - const resultBuffer = Buffer.concat([buffer1, buffer2]); - const expectedBuffer = Buffer.from([...data1, ...data2]); - assertEquals(resultBuffer, expectedBuffer); - }, -}); - -Deno.test({ - name: "A single buffer concatenates and return the same buffer", - fn() { - const buffer1 = Buffer.alloc(1); - const resultBuffer = Buffer.concat([buffer1]); - assertEquals(resultBuffer.length, 1, "Buffer length should be 1"); - }, -}); - -Deno.test({ - name: "No buffers concat returns an empty buffer", - fn() { - const resultBuffer = Buffer.concat([]); - assertEquals(resultBuffer.length, 0, "Buffer length should be 0"); - }, -}); - -Deno.test({ - name: "Buffer concat respects totalLenght parameter", - fn() { - const maxLength1 = 10; - const buffer1 = Buffer.alloc(2); - const buffer2 = Buffer.alloc(2); - assertEquals( - Buffer.concat([buffer1, buffer2], maxLength1).length, - maxLength1, - ); - - const maxLength2 = 3; - const buffer3 = Buffer.alloc(2); - const buffer4 = Buffer.alloc(2); - assertEquals( - Buffer.concat([buffer3, buffer4], maxLength2).length, - maxLength2, - ); - }, -}); - -Deno.test({ - name: "Buffer copy works as expected", - fn() { - const data1 = new Uint8Array([1, 2, 3]); - const data2 = new Uint8Array([4, 5, 6]); - - const buffer1 = Buffer.from(data1); - const buffer2 = Buffer.from(data2); - - //Mutates data_1 - data1.set(data2); - //Mutates buffer_1 - buffer2.copy(buffer1); - - assertEquals( - data1, - buffer1, - ); - }, -}); - -Deno.test({ - name: "Buffer copy respects the starting point for copy", - fn() { - const buffer1 = Buffer.from([1, 2, 3]); - const buffer2 = Buffer.alloc(8); - - buffer1.copy(buffer2, 5); - - const expected = Buffer.from([0, 0, 0, 0, 0, 1, 2, 3]); - - assertEquals( - buffer2, - expected, - ); - }, -}); - -Deno.test({ - name: "Buffer copy doesn't throw on offset but copies until offset reached", - fn() { - const buffer1 = Buffer.from([1, 2, 3]); - const buffer2 = Buffer.alloc(8); - - const writtenBytes1 = buffer1.copy(buffer2, 6); - - assertEquals( - writtenBytes1, - 2, - ); - - assertEquals( - buffer2, - Buffer.from([0, 0, 0, 0, 0, 0, 1, 2]), - ); - - const buffer3 = Buffer.from([1, 2, 3]); - const buffer4 = Buffer.alloc(8); - - const writtenBytes2 = buffer3.copy(buffer4, 8); - - assertEquals( - writtenBytes2, - 0, - ); - - assertEquals( - buffer4, - Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), - ); - }, -}); - -Deno.test({ - name: "Buffer from string creates a Buffer", - fn() { - const buffer: Buffer = Buffer.from("test"); - assertEquals(buffer.length, 4, "Buffer length should be 4"); - assertEquals( - buffer.toString(), - "test", - "Buffer to string should recover the string", - ); - }, -}); - -Deno.test({ - name: "Buffer from string hex", - fn() { - for (const encoding of ["hex", "HEX"]) { - const buffer: Buffer = Buffer.from( - "7468697320697320612074c3a97374", - encoding, - ); - assertEquals(buffer.length, 15, "Buffer length should be 15"); - assertEquals( - buffer.toString(), - "this is a tést", - "Buffer to string should recover the string", - ); - } - }, -}); - -Deno.test({ - name: "Buffer from string base64", - fn() { - for (const encoding of ["base64", "BASE64"]) { - const buffer: Buffer = Buffer.from("dGhpcyBpcyBhIHTDqXN0", encoding); - assertEquals(buffer.length, 15, "Buffer length should be 15"); - assertEquals( - buffer.toString(), - "this is a tést", - "Buffer to string should recover the string", - ); - } - }, -}); - -Deno.test({ - name: "Buffer to string base64", - fn() { - for (const encoding of ["base64", "BASE64"]) { - const buffer: Buffer = Buffer.from("deno land"); - assertEquals( - buffer.toString(encoding), - "ZGVubyBsYW5k", - "Buffer to string should recover the string in base64", - ); - } - const b64 = "dGhpcyBpcyBhIHTDqXN0"; - assertEquals(Buffer.from(b64, "base64").toString("base64"), b64); - }, -}); - -Deno.test({ - name: "Buffer to string hex", - fn() { - for (const encoding of ["hex", "HEX"]) { - const buffer: Buffer = Buffer.from("deno land"); - assertEquals( - buffer.toString(encoding), - "64656e6f206c616e64", - "Buffer to string should recover the string", - ); - } - const hex = "64656e6f206c616e64"; - assertEquals(Buffer.from(hex, "hex").toString("hex"), hex); - }, -}); - -Deno.test({ - name: "Buffer to string invalid encoding", - fn() { - const buffer: Buffer = Buffer.from("deno land"); - const invalidEncodings = [null, 5, {}, true, false, "foo", ""]; - - for (const encoding of invalidEncodings) { - assertThrows( - () => { - // deno-lint-ignore ban-ts-comment - // @ts-expect-error - buffer.toString(encoding); - }, - TypeError, - `Unkown encoding: ${encoding}`, - "Should throw on invalid encoding", - ); - } - }, -}); - -Deno.test({ - name: "Buffer from string invalid encoding", - fn() { - const defaultToUtf8Encodings = [null, 5, {}, true, false, ""]; - const invalidEncodings = ["deno", "base645"]; - - for (const encoding of defaultToUtf8Encodings) { - // deno-lint-ignore ban-ts-comment - // @ts-expect-error - assertEquals(Buffer.from("yes", encoding).toString(), "yes"); - } - - for (const encoding of invalidEncodings) { - assertThrows( - () => { - Buffer.from("yes", encoding); - }, - TypeError, - `Unkown encoding: ${encoding}`, - ); - } - }, -}); - -Deno.test({ - name: "Buffer to/from string not implemented encodings", - fn() { - const buffer: Buffer = Buffer.from("deno land"); - const notImplemented = ["ascii", "binary"]; - - for (const encoding of notImplemented) { - assertThrows( - () => { - buffer.toString(encoding); - }, - Error, - `"${encoding}" encoding`, - "Should throw on invalid encoding", - ); - - assertThrows( - () => { - Buffer.from("", encoding); - }, - Error, - `"${encoding}" encoding`, - "Should throw on invalid encoding", - ); - } - }, -}); - -Deno.test({ - name: "Buffer from another buffer creates a Buffer", - fn() { - const buffer: Buffer = Buffer.from(Buffer.from("test")); - assertEquals(buffer.length, 4, "Buffer length should be 4"); - assertEquals( - buffer.toString(), - "test", - "Buffer to string should recover the string", - ); - }, -}); - -Deno.test({ - name: "isBuffer returns true if the object is a buffer", - fn() { - assertEquals(Buffer.isBuffer(Buffer.from("test")), true); - }, -}); - -Deno.test({ - name: "isBuffer returns false if the object is not a buffer", - fn() { - assertEquals(Buffer.isBuffer({ test: 3 }), false); - assertEquals(Buffer.isBuffer(new Uint8Array()), false); - }, -}); - -Deno.test({ - name: "Buffer toJSON", - fn() { - assertEquals( - JSON.stringify(Buffer.from("deno")), - '{"type":"Buffer","data":[100,101,110,111]}', - ); - }, -}); - -Deno.test({ - name: "buf.slice does not create a copy", - fn() { - const buf = Buffer.from("ceno"); - // This method is not compatible with the Uint8Array.prototype.slice() - const slice = buf.slice(); - slice[0]++; - assertEquals(slice.toString(), "deno"); - }, -}); - -Deno.test({ - name: "isEncoding returns true for valid encodings", - fn() { - [ - "hex", - "HEX", - "HeX", - "utf8", - "utf-8", - "ascii", - "latin1", - "binary", - "base64", - "BASE64", - "BASe64", - "ucs2", - "ucs-2", - "utf16le", - "utf-16le", - ].forEach((enc) => { - assertEquals(Buffer.isEncoding(enc), true); - }); - }, -}); - -Deno.test({ - name: "isEncoding returns false for invalid encodings", - fn() { - [ - "utf9", - "utf-7", - "Unicode-FTW", - "new gnu gun", - false, - NaN, - {}, - Infinity, - [], - 1, - 0, - -1, - ].forEach((enc) => { - assertEquals(Buffer.isEncoding(enc), false); - }); - }, -}); - -// ported from: -// https://github.com/nodejs/node/blob/56dbe466fdbc598baea3bfce289bf52b97b8b8f7/test/parallel/test-buffer-equals.js#L6 -Deno.test({ - name: "buf.equals", - fn() { - const b = Buffer.from("abcdf"); - const c = Buffer.from("abcdf"); - const d = Buffer.from("abcde"); - const e = Buffer.from("abcdef"); - - assertEquals(b.equals(c), true); - assertEquals(d.equals(d), true); - assertEquals( - d.equals(new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65])), - true, - ); - - assertEquals(c.equals(d), false); - assertEquals(d.equals(e), false); - - assertThrows( - // deno-lint-ignore ban-ts-comment - // @ts-expect-error - () => Buffer.alloc(1).equals("abc"), - TypeError, - `The "otherBuffer" argument must be an instance of Buffer or Uint8Array. Received type string`, - ); - }, -}); diff --git a/std/node/crypto.ts b/std/node/crypto.ts deleted file mode 100644 index 8b933a99c3..0000000000 --- a/std/node/crypto.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { default as randomBytes } from "./_crypto/randomBytes.ts"; -import { pbkdf2, pbkdf2Sync } from "./_crypto/pbkdf2.ts"; - -export default { randomBytes, pbkdf2, pbkdf2Sync }; -export { pbkdf2, pbkdf2Sync, randomBytes }; diff --git a/std/node/events.ts b/std/node/events.ts deleted file mode 100644 index f737c884c6..0000000000 --- a/std/node/events.ts +++ /dev/null @@ -1,558 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// Copyright (c) 2019 Denolibs authors. All rights reserved. MIT license. -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -import { validateIntegerRange } from "./_utils.ts"; -import { assert } from "../_util/assert.ts"; - -// deno-lint-ignore no-explicit-any -export type GenericFunction = (...args: any[]) => any; - -export interface WrappedFunction extends Function { - listener: GenericFunction; -} - -// deno-lint-ignore no-explicit-any -function createIterResult(value: any, done: boolean): IteratorResult { - return { value, done }; -} - -interface AsyncIterable { - // deno-lint-ignore no-explicit-any - next(): Promise>; - // deno-lint-ignore no-explicit-any - return(): Promise>; - throw(err: Error): void; - // deno-lint-ignore no-explicit-any - [Symbol.asyncIterator](): any; -} - -export let defaultMaxListeners = 10; - -/** - * See also https://nodejs.org/api/events.html - */ -export default class EventEmitter { - public static captureRejectionSymbol = Symbol.for("nodejs.rejection"); - public static errorMonitor = Symbol("events.errorMonitor"); - public static get defaultMaxListeners() { - return defaultMaxListeners; - } - public static set defaultMaxListeners(value: number) { - defaultMaxListeners = value; - } - - private maxListeners: number | undefined; - private _events: Map< - string | symbol, - Array - >; - - public constructor() { - this._events = new Map(); - } - - private _addListener( - eventName: string | symbol, - listener: GenericFunction | WrappedFunction, - prepend: boolean, - ): this { - this.emit("newListener", eventName, listener); - if (this._events.has(eventName)) { - const listeners = this._events.get(eventName) as Array< - GenericFunction | WrappedFunction - >; - if (prepend) { - listeners.unshift(listener); - } else { - listeners.push(listener); - } - } else { - this._events.set(eventName, [listener]); - } - const max = this.getMaxListeners(); - if (max > 0 && this.listenerCount(eventName) > max) { - const warning = new Error( - `Possible EventEmitter memory leak detected. - ${this.listenerCount(eventName)} ${eventName.toString()} listeners. - Use emitter.setMaxListeners() to increase limit`, - ); - warning.name = "MaxListenersExceededWarning"; - console.warn(warning); - } - - return this; - } - - /** Alias for emitter.on(eventName, listener). */ - public addListener( - eventName: string | symbol, - listener: GenericFunction | WrappedFunction, - ): this { - return this._addListener(eventName, listener, false); - } - - /** - * Synchronously calls each of the listeners registered for the event named - * eventName, in the order they were registered, passing the supplied - * arguments to each. - * @return true if the event had listeners, false otherwise - */ - // deno-lint-ignore no-explicit-any - public emit(eventName: string | symbol, ...args: any[]): boolean { - if (this._events.has(eventName)) { - if ( - eventName === "error" && - this._events.get(EventEmitter.errorMonitor) - ) { - this.emit(EventEmitter.errorMonitor, ...args); - } - const listeners = (this._events.get( - eventName, - ) as GenericFunction[]).slice(); // We copy with slice() so array is not mutated during emit - for (const listener of listeners) { - try { - listener.apply(this, args); - } catch (err) { - this.emit("error", err); - } - } - return true; - } else if (eventName === "error") { - if (this._events.get(EventEmitter.errorMonitor)) { - this.emit(EventEmitter.errorMonitor, ...args); - } - const errMsg = args.length > 0 ? args[0] : Error("Unhandled error."); - throw errMsg; - } - return false; - } - - /** - * Returns an array listing the events for which the emitter has - * registered listeners. - */ - public eventNames(): [string | symbol] { - return Array.from(this._events.keys()) as [string | symbol]; - } - - /** - * Returns the current max listener value for the EventEmitter which is - * either set by emitter.setMaxListeners(n) or defaults to - * EventEmitter.defaultMaxListeners. - */ - public getMaxListeners(): number { - return this.maxListeners || EventEmitter.defaultMaxListeners; - } - - /** - * Returns the number of listeners listening to the event named - * eventName. - */ - public listenerCount(eventName: string | symbol): number { - if (this._events.has(eventName)) { - return (this._events.get(eventName) as GenericFunction[]).length; - } else { - return 0; - } - } - - private _listeners( - target: EventEmitter, - eventName: string | symbol, - unwrap: boolean, - ): GenericFunction[] { - if (!target._events.has(eventName)) { - return []; - } - const eventListeners = target._events.get(eventName) as GenericFunction[]; - - return unwrap - ? this.unwrapListeners(eventListeners) - : eventListeners.slice(0); - } - - private unwrapListeners(arr: GenericFunction[]): GenericFunction[] { - const unwrappedListeners = new Array(arr.length) as GenericFunction[]; - for (let i = 0; i < arr.length; i++) { - // deno-lint-ignore no-explicit-any - unwrappedListeners[i] = (arr[i] as any)["listener"] || arr[i]; - } - return unwrappedListeners; - } - - /** Returns a copy of the array of listeners for the event named eventName.*/ - public listeners(eventName: string | symbol): GenericFunction[] { - return this._listeners(this, eventName, true); - } - - /** - * Returns a copy of the array of listeners for the event named eventName, - * including any wrappers (such as those created by .once()). - */ - public rawListeners( - eventName: string | symbol, - ): Array { - return this._listeners(this, eventName, false); - } - - /** Alias for emitter.removeListener(). */ - public off(eventName: string | symbol, listener: GenericFunction): this { - return this.removeListener(eventName, listener); - } - - /** - * Adds the listener function to the end of the listeners array for the event - * named eventName. No checks are made to see if the listener has already - * been added. Multiple calls passing the same combination of eventName and - * listener will result in the listener being added, and called, multiple - * times. - */ - public on( - eventName: string | symbol, - listener: GenericFunction | WrappedFunction, - ): this { - return this._addListener(eventName, listener, false); - } - - /** - * Adds a one-time listener function for the event named eventName. The next - * time eventName is triggered, this listener is removed and then invoked. - */ - public once(eventName: string | symbol, listener: GenericFunction): this { - const wrapped: WrappedFunction = this.onceWrap(eventName, listener); - this.on(eventName, wrapped); - return this; - } - - // Wrapped function that calls EventEmitter.removeListener(eventName, self) on execution. - private onceWrap( - eventName: string | symbol, - listener: GenericFunction, - ): WrappedFunction { - const wrapper = function ( - this: { - eventName: string | symbol; - listener: GenericFunction; - rawListener: GenericFunction | WrappedFunction; - context: EventEmitter; - }, - // deno-lint-ignore no-explicit-any - ...args: any[] - ): void { - this.context.removeListener( - this.eventName, - this.rawListener as GenericFunction, - ); - this.listener.apply(this.context, args); - }; - const wrapperContext = { - eventName: eventName, - listener: listener, - rawListener: (wrapper as unknown) as WrappedFunction, - context: this, - }; - const wrapped = (wrapper.bind( - wrapperContext, - ) as unknown) as WrappedFunction; - wrapperContext.rawListener = wrapped; - wrapped.listener = listener; - return wrapped as WrappedFunction; - } - - /** - * Adds the listener function to the beginning of the listeners array for the - * event named eventName. No checks are made to see if the listener has - * already been added. Multiple calls passing the same combination of - * eventName and listener will result in the listener being added, and - * called, multiple times. - */ - public prependListener( - eventName: string | symbol, - listener: GenericFunction | WrappedFunction, - ): this { - return this._addListener(eventName, listener, true); - } - - /** - * Adds a one-time listener function for the event named eventName to the - * beginning of the listeners array. The next time eventName is triggered, - * this listener is removed, and then invoked. - */ - public prependOnceListener( - eventName: string | symbol, - listener: GenericFunction, - ): this { - const wrapped: WrappedFunction = this.onceWrap(eventName, listener); - this.prependListener(eventName, wrapped); - return this; - } - - /** Removes all listeners, or those of the specified eventName. */ - public removeAllListeners(eventName?: string | symbol): this { - if (this._events === undefined) { - return this; - } - - if (eventName) { - if (this._events.has(eventName)) { - const listeners = (this._events.get(eventName) as Array< - GenericFunction | WrappedFunction - >).slice(); // Create a copy; We use it AFTER it's deleted. - this._events.delete(eventName); - for (const listener of listeners) { - this.emit("removeListener", eventName, listener); - } - } - } else { - const eventList: [string | symbol] = this.eventNames(); - eventList.map((value: string | symbol) => { - this.removeAllListeners(value); - }); - } - - return this; - } - - /** - * Removes the specified listener from the listener array for the event - * named eventName. - */ - public removeListener( - eventName: string | symbol, - listener: GenericFunction, - ): this { - if (this._events.has(eventName)) { - const arr: - | Array - | undefined = this._events.get(eventName); - - assert(arr); - - let listenerIndex = -1; - for (let i = arr.length - 1; i >= 0; i--) { - // arr[i]["listener"] is the reference to the listener inside a bound 'once' wrapper - if ( - arr[i] == listener || - (arr[i] && (arr[i] as WrappedFunction)["listener"] == listener) - ) { - listenerIndex = i; - break; - } - } - - if (listenerIndex >= 0) { - arr.splice(listenerIndex, 1); - this.emit("removeListener", eventName, listener); - if (arr.length === 0) { - this._events.delete(eventName); - } - } - } - return this; - } - - /** - * By default EventEmitters will print a warning if more than 10 listeners - * are added for a particular event. This is a useful default that helps - * finding memory leaks. Obviously, not all events should be limited to just - * 10 listeners. The emitter.setMaxListeners() method allows the limit to be - * modified for this specific EventEmitter instance. The value can be set to - * Infinity (or 0) to indicate an unlimited number of listeners. - */ - public setMaxListeners(n: number): this { - if (n !== Infinity) { - if (n === 0) { - n = Infinity; - } else { - validateIntegerRange(n, "maxListeners", 0); - } - } - - this.maxListeners = n; - return this; - } - - /** - * Creates a Promise that is fulfilled when the EventEmitter emits the given - * event or that is rejected when the EventEmitter emits 'error'. The Promise - * will resolve with an array of all the arguments emitted to the given event. - */ - public static once( - emitter: EventEmitter | EventTarget, - name: string, - // deno-lint-ignore no-explicit-any - ): Promise { - return new Promise((resolve, reject) => { - if (emitter instanceof EventTarget) { - // EventTarget does not have `error` event semantics like Node - // EventEmitters, we do not listen to `error` events here. - emitter.addEventListener( - name, - (...args) => { - resolve(args); - }, - { once: true, passive: false, capture: false }, - ); - return; - } else if (emitter instanceof EventEmitter) { - // deno-lint-ignore no-explicit-any - const eventListener = (...args: any[]): void => { - if (errorListener !== undefined) { - emitter.removeListener("error", errorListener); - } - resolve(args); - }; - let errorListener: GenericFunction; - - // Adding an error listener is not optional because - // if an error is thrown on an event emitter we cannot - // guarantee that the actual event we are waiting will - // be fired. The result could be a silent way to create - // memory or file descriptor leaks, which is something - // we should avoid. - if (name !== "error") { - // deno-lint-ignore no-explicit-any - errorListener = (err: any): void => { - emitter.removeListener(name, eventListener); - reject(err); - }; - - emitter.once("error", errorListener); - } - - emitter.once(name, eventListener); - return; - } - }); - } - - /** - * Returns an AsyncIterator that iterates eventName events. It will throw if - * the EventEmitter emits 'error'. It removes all listeners when exiting the - * loop. The value returned by each iteration is an array composed of the - * emitted event arguments. - */ - public static on( - emitter: EventEmitter, - event: string | symbol, - ): AsyncIterable { - // deno-lint-ignore no-explicit-any - const unconsumedEventValues: any[] = []; - // deno-lint-ignore no-explicit-any - const unconsumedPromises: any[] = []; - let error: Error | null = null; - let finished = false; - - const iterator = { - // deno-lint-ignore no-explicit-any - next(): Promise> { - // First, we consume all unread events - // deno-lint-ignore no-explicit-any - const value: any = unconsumedEventValues.shift(); - if (value) { - return Promise.resolve(createIterResult(value, false)); - } - - // Then we error, if an error happened - // This happens one time if at all, because after 'error' - // we stop listening - if (error) { - const p: Promise = Promise.reject(error); - // Only the first element errors - error = null; - return p; - } - - // If the iterator is finished, resolve to done - if (finished) { - return Promise.resolve(createIterResult(undefined, true)); - } - - // Wait until an event happens - return new Promise(function (resolve, reject) { - unconsumedPromises.push({ resolve, reject }); - }); - }, - - // deno-lint-ignore no-explicit-any - return(): Promise> { - emitter.removeListener(event, eventHandler); - emitter.removeListener("error", errorHandler); - finished = true; - - for (const promise of unconsumedPromises) { - promise.resolve(createIterResult(undefined, true)); - } - - return Promise.resolve(createIterResult(undefined, true)); - }, - - throw(err: Error): void { - error = err; - emitter.removeListener(event, eventHandler); - emitter.removeListener("error", errorHandler); - }, - - // deno-lint-ignore no-explicit-any - [Symbol.asyncIterator](): any { - return this; - }, - }; - - emitter.on(event, eventHandler); - emitter.on("error", errorHandler); - - return iterator; - - // deno-lint-ignore no-explicit-any - function eventHandler(...args: any[]): void { - const promise = unconsumedPromises.shift(); - if (promise) { - promise.resolve(createIterResult(args, false)); - } else { - unconsumedEventValues.push(args); - } - } - - // deno-lint-ignore no-explicit-any - function errorHandler(err: any): void { - finished = true; - - const toError = unconsumedPromises.shift(); - if (toError) { - toError.reject(err); - } else { - // The next time we call next() - error = err; - } - - iterator.return(); - } - } -} - -export { EventEmitter }; -export const once = EventEmitter.once; -export const on = EventEmitter.on; -export const captureRejectionSymbol = EventEmitter.captureRejectionSymbol; -export const errorMonitor = EventEmitter.errorMonitor; diff --git a/std/node/events_test.ts b/std/node/events_test.ts deleted file mode 100644 index 6942fe8471..0000000000 --- a/std/node/events_test.ts +++ /dev/null @@ -1,687 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertThrows, - fail, -} from "../testing/asserts.ts"; -import EventEmitter, { on, once, WrappedFunction } from "./events.ts"; - -const shouldNeverBeEmitted = () => { - fail("Should never be called"); -}; - -Deno.test({ - name: - 'When adding a new event, "eventListener" event is fired before adding the listener', - fn() { - let eventsFired: string[] = []; - const testEmitter = new EventEmitter(); - testEmitter.on("newListener", (event: string) => { - if (event !== "newListener") { - eventsFired.push("newListener"); - } - }); - testEmitter.on("event", () => { - eventsFired.push("event"); - }); - assertEquals(eventsFired, ["newListener"]); - eventsFired = []; - testEmitter.emit("event"); - assertEquals(eventsFired, ["event"]); - }, -}); - -Deno.test({ - name: - 'When removing a listenert, "removeListener" event is fired after removal', - fn() { - const eventsFired: string[] = []; - const testEmitter = new EventEmitter(); - testEmitter.on("removeListener", () => { - eventsFired.push("removeListener"); - }); - const eventFunction = function (): void { - eventsFired.push("event"); - }; - testEmitter.on("event", eventFunction); - - assertEquals(eventsFired, []); - testEmitter.removeListener("event", eventFunction); - assertEquals(eventsFired, ["removeListener"]); - }, -}); - -Deno.test({ - name: - "Default max listeners is 10, but can be changed by direct assignment only", - fn() { - assertEquals(EventEmitter.defaultMaxListeners, 10); - new EventEmitter().setMaxListeners(20); - assertEquals(EventEmitter.defaultMaxListeners, 10); - EventEmitter.defaultMaxListeners = 20; - assertEquals(EventEmitter.defaultMaxListeners, 20); - EventEmitter.defaultMaxListeners = 10; //reset back to original value - - assertThrows(() => { - new EventEmitter().setMaxListeners(-1); - }); - - const ee = new EventEmitter(); - const noop = (): void => {}; - const origWarn = console.warn; - - for (let i = 10; i--;) { - ee.on("test", noop); - } - - // there are only sync actions until it gets restored, - // so it's safe to overwrite this - console.warn = (): void => fail("Infinity listeners should be allowed"); - - ee.setMaxListeners(Infinity); - ee.on("test", noop); - - // 0 means that unlimited listeners are allowed - ee.setMaxListeners(0); - ee.on("test", noop); - - console.warn = origWarn; - }, -}); - -Deno.test({ - name: "addListener adds a listener, and listener count is correct", - fn() { - const testEmitter = new EventEmitter(); - testEmitter.on("event", shouldNeverBeEmitted); - assertEquals(1, testEmitter.listenerCount("event")); - testEmitter.on("event", shouldNeverBeEmitted); - assertEquals(2, testEmitter.listenerCount("event")); - }, -}); - -Deno.test({ - name: "Emitted events are called synchronously in the order they were added", - fn() { - const testEmitter = new EventEmitter(); - const eventsFired: string[] = []; - testEmitter.on("event", (oneArg: string) => { - eventsFired.push("event(" + oneArg + ")"); - }); - testEmitter.on("event", (oneArg: string, twoArg: string) => { - eventsFired.push("event(" + oneArg + ", " + twoArg + ")"); - }); - - testEmitter.on("non-event", shouldNeverBeEmitted); - - testEmitter.on( - "event", - (oneArg: string, twoArg: string, threeArg: string) => { - eventsFired.push( - "event(" + oneArg + ", " + twoArg + ", " + threeArg + ")", - ); - }, - ); - testEmitter.emit("event", 1, 2, 3); - assertEquals(eventsFired, ["event(1)", "event(1, 2)", "event(1, 2, 3)"]); - }, -}); - -Deno.test({ - name: "Registered event names are returned as strings or Sybols", - fn() { - const testEmitter = new EventEmitter(); - testEmitter.on("event", shouldNeverBeEmitted); - testEmitter.on("event", shouldNeverBeEmitted); - const sym = Symbol("symbol"); - testEmitter.on(sym, shouldNeverBeEmitted); - assertEquals(testEmitter.eventNames(), ["event", sym]); - }, -}); - -Deno.test({ - name: "You can set and get max listeners", - fn() { - const testEmitter = new EventEmitter(); - assertEquals(testEmitter.getMaxListeners(), 10); - testEmitter.setMaxListeners(20); - assertEquals(testEmitter.getMaxListeners(), 20); - }, -}); - -Deno.test({ - name: "You can retrieve registered functions for an event", - fn() { - const testEmitter = new EventEmitter(); - testEmitter.on("someOtherEvent", shouldNeverBeEmitted); - testEmitter.on("event", shouldNeverBeEmitted); - const testFunction = (): void => {}; - testEmitter.on("event", testFunction); - assertEquals(testEmitter.listeners("event"), [ - shouldNeverBeEmitted, - testFunction, - ]); - }, -}); - -Deno.test({ - name: "Off is alias for removeListener", - fn() { - const testEmitter = new EventEmitter(); - testEmitter.on("event", shouldNeverBeEmitted); - assertEquals(testEmitter.listenerCount("event"), 1); - testEmitter.off("event", shouldNeverBeEmitted); - assertEquals(testEmitter.listenerCount("event"), 0); - }, -}); - -Deno.test({ - name: "Event registration can be chained", - fn() { - const testEmitter = new EventEmitter(); - testEmitter - .on("event", shouldNeverBeEmitted) - .on("event", shouldNeverBeEmitted); - assertEquals(testEmitter.listenerCount("event"), 2); - }, -}); - -Deno.test({ - name: "Events can be registered to only fire once", - fn() { - let eventsFired: string[] = []; - const testEmitter = new EventEmitter(); - //prove multiple emits on same event first (when registered with 'on') - testEmitter.on("multiple event", () => { - eventsFired.push("multiple event"); - }); - testEmitter.emit("multiple event"); - testEmitter.emit("multiple event"); - assertEquals(eventsFired, ["multiple event", "multiple event"]); - - //now prove multiple events registered via 'once' only emit once - eventsFired = []; - testEmitter.once("single event", () => { - eventsFired.push("single event"); - }); - testEmitter.emit("single event"); - testEmitter.emit("single event"); - assertEquals(eventsFired, ["single event"]); - }, -}); - -Deno.test({ - name: - "You can inject a listener into the start of the stack, rather than at the end", - fn() { - const eventsFired: string[] = []; - const testEmitter = new EventEmitter(); - testEmitter.on("event", () => { - eventsFired.push("first"); - }); - testEmitter.on("event", () => { - eventsFired.push("second"); - }); - testEmitter.prependListener("event", () => { - eventsFired.push("third"); - }); - testEmitter.emit("event"); - assertEquals(eventsFired, ["third", "first", "second"]); - }, -}); - -Deno.test({ - name: 'You can prepend a "once" listener', - fn() { - const eventsFired: string[] = []; - const testEmitter = new EventEmitter(); - testEmitter.on("event", () => { - eventsFired.push("first"); - }); - testEmitter.on("event", () => { - eventsFired.push("second"); - }); - testEmitter.prependOnceListener("event", () => { - eventsFired.push("third"); - }); - testEmitter.emit("event"); - testEmitter.emit("event"); - assertEquals(eventsFired, ["third", "first", "second", "first", "second"]); - }, -}); - -Deno.test({ - name: "Remove all listeners, which can also be chained", - fn() { - const testEmitter = new EventEmitter(); - testEmitter.on("event", shouldNeverBeEmitted); - testEmitter.on("event", shouldNeverBeEmitted); - testEmitter.on("other event", shouldNeverBeEmitted); - testEmitter.on("other event", shouldNeverBeEmitted); - testEmitter.once("other event", shouldNeverBeEmitted); - assertEquals(testEmitter.listenerCount("event"), 2); - assertEquals(testEmitter.listenerCount("other event"), 3); - - testEmitter.removeAllListeners("event").removeAllListeners("other event"); - - assertEquals(testEmitter.listenerCount("event"), 0); - assertEquals(testEmitter.listenerCount("other event"), 0); - }, -}); - -Deno.test({ - name: "Provide a non-existent event to removeAllListeners will do nothing", - fn() { - const testEmitter = new EventEmitter(); - testEmitter.on("event", shouldNeverBeEmitted); - testEmitter.on("event", shouldNeverBeEmitted); - testEmitter.on("other event", shouldNeverBeEmitted); - testEmitter.on("other event", shouldNeverBeEmitted); - testEmitter.once("other event", shouldNeverBeEmitted); - assertEquals(testEmitter.listenerCount("event"), 2); - assertEquals(testEmitter.listenerCount("other event"), 3); - - testEmitter.removeAllListeners("non-existent"); - - assertEquals(testEmitter.listenerCount("event"), 2); - assertEquals(testEmitter.listenerCount("other event"), 3); - }, -}); - -Deno.test({ - name: "Remove individual listeners, which can also be chained", - fn() { - const testEmitter = new EventEmitter(); - testEmitter.on("event", shouldNeverBeEmitted); - testEmitter.on("event", shouldNeverBeEmitted); - testEmitter.once("other event", shouldNeverBeEmitted); - assertEquals(testEmitter.listenerCount("event"), 2); - assertEquals(testEmitter.listenerCount("other event"), 1); - - testEmitter.removeListener("other event", shouldNeverBeEmitted); - assertEquals(testEmitter.listenerCount("event"), 2); - assertEquals(testEmitter.listenerCount("other event"), 0); - - testEmitter - .removeListener("event", shouldNeverBeEmitted) - .removeListener("event", shouldNeverBeEmitted); - - assertEquals(testEmitter.listenerCount("event"), 0); - assertEquals(testEmitter.listenerCount("other event"), 0); - }, -}); - -Deno.test({ - name: "It is OK to try to remove non-existent listener", - fn() { - const testEmitter = new EventEmitter(); - - const madeUpEvent = (): void => { - fail("Should never be called"); - }; - - testEmitter.on("event", shouldNeverBeEmitted); - assertEquals(testEmitter.listenerCount("event"), 1); - - testEmitter.removeListener("event", madeUpEvent); - testEmitter.removeListener("non-existent event", madeUpEvent); - - assertEquals(testEmitter.listenerCount("event"), 1); - }, -}); - -Deno.test({ - name: "all listeners complete execution even if removed before execution", - fn() { - const testEmitter = new EventEmitter(); - let eventsProcessed: string[] = []; - const listenerB = (): number => eventsProcessed.push("B"); - const listenerA = (): void => { - eventsProcessed.push("A"); - testEmitter.removeListener("event", listenerB); - }; - - testEmitter.on("event", listenerA); - testEmitter.on("event", listenerB); - - testEmitter.emit("event"); - assertEquals(eventsProcessed, ["A", "B"]); - - eventsProcessed = []; - testEmitter.emit("event"); - assertEquals(eventsProcessed, ["A"]); - }, -}); - -Deno.test({ - name: 'Raw listener will return event listener or wrapped "once" function', - fn() { - const testEmitter = new EventEmitter(); - const eventsProcessed: string[] = []; - const listenerA = (): number => eventsProcessed.push("A"); - const listenerB = (): number => eventsProcessed.push("B"); - testEmitter.on("event", listenerA); - testEmitter.once("once-event", listenerB); - - const rawListenersForEvent = testEmitter.rawListeners("event"); - const rawListenersForOnceEvent = testEmitter.rawListeners("once-event"); - - assertEquals(rawListenersForEvent.length, 1); - assertEquals(rawListenersForOnceEvent.length, 1); - assertEquals(rawListenersForEvent[0], listenerA); - assertEquals( - (rawListenersForOnceEvent[0] as WrappedFunction).listener, - listenerB, - ); - }, -}); - -Deno.test({ - name: - "Once wrapped raw listeners may be executed multiple times, until the wrapper is executed", - fn() { - const testEmitter = new EventEmitter(); - let eventsProcessed: string[] = []; - const listenerA = (): number => eventsProcessed.push("A"); - testEmitter.once("once-event", listenerA); - - const rawListenersForOnceEvent = testEmitter.rawListeners("once-event"); - const wrappedFn: WrappedFunction = - rawListenersForOnceEvent[0] as WrappedFunction; - wrappedFn.listener(); - wrappedFn.listener(); - wrappedFn.listener(); - assertEquals(eventsProcessed, ["A", "A", "A"]); - - eventsProcessed = []; - wrappedFn(); // executing the wrapped listener function will remove it from the event - assertEquals(eventsProcessed, ["A"]); - assertEquals(testEmitter.listeners("once-event").length, 0); - }, -}); - -Deno.test({ - name: "Can add once event listener to EventEmitter via standalone function", - async fn() { - const ee = new EventEmitter(); - setTimeout(() => { - ee.emit("event", 42, "foo"); - }, 0); - // deno-lint-ignore no-explicit-any - const valueArr: any[] = await once(ee, "event"); - assertEquals(valueArr, [42, "foo"]); - }, -}); - -Deno.test({ - name: "Can add once event listener to EventTarget via standalone function", - async fn() { - const et: EventTarget = new EventTarget(); - setTimeout(() => { - const event: Event = new Event("event", { composed: true }); - et.dispatchEvent(event); - }, 0); - // deno-lint-ignore no-explicit-any - const eventObj: any[] = await once(et, "event"); - assert(!eventObj[0].isTrusted); - }, -}); - -Deno.test({ - name: "Only valid integers are allowed for max listeners", - fn() { - const ee = new EventEmitter(); - ee.setMaxListeners(0); - assertThrows( - () => { - ee.setMaxListeners(-1); - }, - Error, - "must be >= 0", - ); - assertThrows( - () => { - ee.setMaxListeners(3.45); - }, - Error, - "must be 'an integer'", - ); - }, -}); - -Deno.test({ - name: "ErrorMonitor can spy on error events without consuming them", - fn() { - const ee = new EventEmitter(); - let events: string[] = []; - //unhandled error scenario should throw - assertThrows( - () => { - ee.emit("error"); - }, - Error, - "Unhandled error", - ); - - ee.on(EventEmitter.errorMonitor, () => { - events.push("errorMonitor event"); - }); - - //error is still unhandled but also intercepted by error monitor - assertThrows( - () => { - ee.emit("error"); - }, - Error, - "Unhandled error", - ); - assertEquals(events, ["errorMonitor event"]); - - //A registered error handler won't throw, but still be monitored - events = []; - ee.on("error", () => { - events.push("error"); - }); - ee.emit("error"); - assertEquals(events, ["errorMonitor event", "error"]); - }, -}); - -Deno.test({ - name: "asynchronous iteration of events are handled as expected", - async fn() { - const ee = new EventEmitter(); - setTimeout(() => { - ee.emit("foo", "bar"); - ee.emit("bar", 24); - ee.emit("foo", 42); - }, 0); - - const iterable = on(ee, "foo"); - - const expected = [["bar"], [42]]; - - for await (const event of iterable) { - const current = expected.shift(); - - assertEquals(current, event); - - if (expected.length === 0) { - break; - } - } - assertEquals(ee.listenerCount("foo"), 0); - assertEquals(ee.listenerCount("error"), 0); - }, -}); - -Deno.test({ - name: "asynchronous error handling of emitted events works as expected", - async fn() { - const ee = new EventEmitter(); - const _err = new Error("kaboom"); - setTimeout(() => { - ee.emit("error", _err); - }, 0); - - const iterable = on(ee, "foo"); - let thrown = false; - - try { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - for await (const event of iterable) { - fail("no events should be processed due to the error thrown"); - } - } catch (err) { - thrown = true; - assertEquals(err, _err); - } - assertEquals(thrown, true); - }, -}); - -Deno.test({ - name: "error thrown during asynchronous processing of events is handled", - async fn() { - const ee = new EventEmitter(); - const _err = new Error("kaboom"); - setTimeout(() => { - ee.emit("foo", 42); - ee.emit("error", _err); - }, 0); - - const iterable = on(ee, "foo"); - const expected = [[42]]; - let thrown = false; - - try { - for await (const event of iterable) { - const current = expected.shift(); - assertEquals(current, event); - } - } catch (err) { - thrown = true; - assertEquals(err, _err); - } - assertEquals(thrown, true); - assertEquals(ee.listenerCount("foo"), 0); - assertEquals(ee.listenerCount("error"), 0); - }, -}); - -Deno.test({ - name: - "error thrown in processing loop of asynchronous event prevents processing of additional events", - async fn() { - const ee = new EventEmitter(); - const _err = new Error("kaboom"); - - setTimeout(() => { - ee.emit("foo", 42); - ee.emit("foo", 999); - }, 0); - - try { - for await (const event of on(ee, "foo")) { - assertEquals(event, [42]); - throw _err; - } - } catch (err) { - assertEquals(err, _err); - } - - assertEquals(ee.listenerCount("foo"), 0); - assertEquals(ee.listenerCount("error"), 0); - }, -}); - -Deno.test({ - name: "asynchronous iterator next() works as expected", - async fn() { - const ee = new EventEmitter(); - const iterable = on(ee, "foo"); - - setTimeout(function () { - ee.emit("foo", "bar"); - ee.emit("foo", 42); - iterable.return(); - }, 0); - - const results = await Promise.all([ - iterable.next(), - iterable.next(), - iterable.next(), - ]); - - assertEquals(results, [ - { - value: ["bar"], - done: false, - }, - { - value: [42], - done: false, - }, - { - value: undefined, - done: true, - }, - ]); - - assertEquals(await iterable.next(), { - value: undefined, - done: true, - }); - }, -}); - -Deno.test({ - name: "async iterable throw handles various scenarios", - async fn() { - const ee = new EventEmitter(); - const iterable = on(ee, "foo"); - - setTimeout(() => { - ee.emit("foo", "bar"); - ee.emit("foo", 42); // lost in the queue - iterable.throw(_err); - }, 0); - - const _err = new Error("kaboom"); - let thrown = false; - - const expected = [["bar"], [42]]; - - try { - for await (const event of iterable) { - assertEquals(event, expected.shift()); - } - } catch (err) { - thrown = true; - assertEquals(err, _err); - } - assertEquals(thrown, true); - assertEquals(expected.length, 0); - assertEquals(ee.listenerCount("foo"), 0); - assertEquals(ee.listenerCount("error"), 0); - }, -}); - -// Event emitter's `on` previously referenced addListener internally, so overriding addListener -// would cause a deadlock -// This is a regression test -Deno.test("Elements that extend EventEmitter listener alias don't end up in a deadlock", () => { - class X extends EventEmitter { - addListener(eventName: string, listener: () => void) { - return super.on(eventName, listener); - } - } - - const x = new X(); - try { - x.on("x", () => {}); - } catch (e) { - fail(); - } -}); diff --git a/std/node/fs.ts b/std/node/fs.ts deleted file mode 100644 index f62f93168b..0000000000 --- a/std/node/fs.ts +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { access, accessSync } from "./_fs/_fs_access.ts"; -import { appendFile, appendFileSync } from "./_fs/_fs_appendFile.ts"; -import { chmod, chmodSync } from "./_fs/_fs_chmod.ts"; -import { chown, chownSync } from "./_fs/_fs_chown.ts"; -import { close, closeSync } from "./_fs/_fs_close.ts"; -import * as constants from "./_fs/_fs_constants.ts"; -import { readFile, readFileSync } from "./_fs/_fs_readFile.ts"; -import { readlink, readlinkSync } from "./_fs/_fs_readlink.ts"; -import { exists, existsSync } from "./_fs/_fs_exists.ts"; -import { mkdir, mkdirSync } from "./_fs/_fs_mkdir.ts"; -import { mkdtemp, mkdtempSync } from "./_fs/_fs_mkdtemp.ts"; -import { copyFile, copyFileSync } from "./_fs/_fs_copy.ts"; -import { writeFile, writeFileSync } from "./_fs/_fs_writeFile.ts"; -import { readdir, readdirSync } from "./_fs/_fs_readdir.ts"; -import { realpath, realpathSync } from "./_fs/_fs_realpath.ts"; -import { rename, renameSync } from "./_fs/_fs_rename.ts"; -import { rmdir, rmdirSync } from "./_fs/_fs_rmdir.ts"; -import { unlink, unlinkSync } from "./_fs/_fs_unlink.ts"; -import { watch } from "./_fs/_fs_watch.ts"; -import { open, openSync } from "./_fs/_fs_open.ts"; -import { stat, statSync } from "./_fs/_fs_stat.ts"; -import { lstat, lstatSync } from "./_fs/_fs_lstat.ts"; - -import * as promises from "./_fs/promises/mod.ts"; - -export default { - access, - accessSync, - appendFile, - appendFileSync, - chmod, - chmodSync, - chown, - chownSync, - close, - closeSync, - constants, - copyFile, - copyFileSync, - exists, - existsSync, - lstat, - lstatSync, - mkdir, - mkdirSync, - mkdtemp, - mkdtempSync, - open, - openSync, - promises, - readdir, - readdirSync, - readFile, - readFileSync, - readlink, - readlinkSync, - realpath, - realpathSync, - rename, - renameSync, - rmdir, - rmdirSync, - stat, - statSync, - unlink, - unlinkSync, - watch, - writeFile, - writeFileSync, -}; - -export { - access, - accessSync, - appendFile, - appendFileSync, - chmod, - chmodSync, - chown, - chownSync, - close, - closeSync, - constants, - copyFile, - copyFileSync, - exists, - existsSync, - lstat, - lstatSync, - mkdir, - mkdirSync, - mkdtemp, - mkdtempSync, - open, - openSync, - promises, - readdir, - readdirSync, - readFile, - readFileSync, - readlink, - readlinkSync, - realpath, - realpathSync, - rename, - renameSync, - rmdir, - rmdirSync, - stat, - statSync, - unlink, - unlinkSync, - watch, - writeFile, - writeFileSync, -}; diff --git a/std/node/global.d.ts b/std/node/global.d.ts deleted file mode 100644 index 94baf03b7f..0000000000 --- a/std/node/global.d.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import processModule from "./process.ts"; -import { Buffer as bufferModule } from "./buffer.ts"; -import timers from "./timers.ts"; - -// d.ts files allow us to declare Buffer as a value and as a type -// type something = Buffer | something_else; is quite common - -type GlobalType = { - process: typeof processModule; - Buffer: typeof bufferModule; - setImmediate: typeof timers.setImmediate; - clearImmediate: typeof timers.clearImmediate; -}; - -declare global { - interface Window { - global: GlobalType; - } - - interface globalThis { - global: GlobalType; - } - - var global: GlobalType; - var process: typeof processModule; - var Buffer: typeof bufferModule; - type Buffer = bufferModule; - var setImmediate: typeof timers.setImmediate; - var clearImmediate: typeof timers.clearImmediate; -} - -export {}; diff --git a/std/node/global.ts b/std/node/global.ts deleted file mode 100644 index 550f6ddf9e..0000000000 --- a/std/node/global.ts +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -/// -import processModule from "./process.ts"; -import { Buffer as bufferModule } from "./buffer.ts"; -import timers from "./timers.ts"; - -Object.defineProperty(globalThis, "global", { - value: globalThis, - writable: false, - enumerable: false, - configurable: true, -}); - -Object.defineProperty(globalThis, "process", { - value: processModule, - enumerable: false, - writable: true, - configurable: true, -}); - -Object.defineProperty(globalThis, "Buffer", { - value: bufferModule, - enumerable: false, - writable: true, - configurable: true, -}); - -Object.defineProperty(globalThis, "setImmediate", { - value: timers.setImmediate, - enumerable: true, - writable: true, - configurable: true, -}); - -Object.defineProperty(globalThis, "clearImmediate", { - value: timers.clearImmediate, - enumerable: true, - writable: true, - configurable: true, -}); - -export {}; diff --git a/std/node/global_test.ts b/std/node/global_test.ts deleted file mode 100644 index 2ff768f887..0000000000 --- a/std/node/global_test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import "./global.ts"; -import { assert, assertStrictEquals } from "../testing/asserts.ts"; -import { Buffer as BufferModule } from "./buffer.ts"; -import processModule from "./process.ts"; -import timers from "./timers.ts"; - -// Definitions for this are quite delicate -// This ensures modifications to the global namespace don't break on TypeScript - -// TODO(bartlomieju): -// Deno lint marks globals defined by this module as undefined -// probably gonna change in the future - -Deno.test("global is correctly defined", () => { - // deno-lint-ignore no-undef - assertStrictEquals(global, globalThis); - // deno-lint-ignore no-undef - assertStrictEquals(global.Buffer, BufferModule); - // deno-lint-ignore no-undef - assertStrictEquals(global.process, process); -}); - -Deno.test("Buffer is correctly defined", () => { - //Check that Buffer is defined as a type as well - type x = Buffer; - // deno-lint-ignore no-undef - assertStrictEquals(Buffer, BufferModule); - // deno-lint-ignore no-undef - assert(Buffer.from); - // deno-lint-ignore no-undef - assertStrictEquals(global.Buffer, BufferModule); - // deno-lint-ignore no-undef - assert(global.Buffer.from); - assertStrictEquals(globalThis.Buffer, BufferModule); - assert(globalThis.Buffer.from); - assertStrictEquals(window.Buffer, BufferModule); - assert(window.Buffer.from); -}); - -Deno.test("process is correctly defined", () => { - // deno-lint-ignore no-undef - assertStrictEquals(process, processModule); - // deno-lint-ignore no-undef - assert(process.arch); - // deno-lint-ignore no-undef - assertStrictEquals(global.process, processModule); - // deno-lint-ignore no-undef - assert(global.process.arch); - assertStrictEquals(globalThis.process, processModule); - assert(globalThis.process.arch); - assertStrictEquals(window.process, processModule); - assert(window.process.arch); -}); - -Deno.test("setImmediate is correctly defined", () => { - // deno-lint-ignore no-undef - assertStrictEquals(setImmediate, timers.setImmediate); - // deno-lint-ignore no-undef - assertStrictEquals(global.setImmediate, timers.setImmediate); - assertStrictEquals(globalThis.setImmediate, timers.setImmediate); - assertStrictEquals(window.setImmediate, timers.setImmediate); -}); - -Deno.test("clearImmediate is correctly defined", () => { - // deno-lint-ignore no-undef - assertStrictEquals(clearImmediate, timers.clearImmediate); - // deno-lint-ignore no-undef - assertStrictEquals(global.clearImmediate, timers.clearImmediate); - assertStrictEquals(globalThis.clearImmediate, timers.clearImmediate); - assertStrictEquals(window.clearImmediate, timers.clearImmediate); -}); diff --git a/std/node/module.ts b/std/node/module.ts deleted file mode 100644 index 0d6ef91332..0000000000 --- a/std/node/module.ts +++ /dev/null @@ -1,1163 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -import "./global.ts"; - -import * as nodeBuffer from "./buffer.ts"; -import * as nodeEvents from "./events.ts"; -import * as nodeFS from "./fs.ts"; -import * as nodeOs from "./os.ts"; -import * as nodePath from "./path.ts"; -import * as nodeQueryString from "./querystring.ts"; -import * as nodeStream from "./stream.ts"; -import * as nodeStringDecoder from "./string_decoder.ts"; -import * as nodeTimers from "./timers.ts"; -import * as nodeUtil from "./util.ts"; - -import * as path from "../path/mod.ts"; -import { assert } from "../_util/assert.ts"; -import { fileURLToPath, pathToFileURL } from "./url.ts"; -import { isWindows } from "../_util/os.ts"; - -const CHAR_FORWARD_SLASH = "/".charCodeAt(0); -const CHAR_BACKWARD_SLASH = "\\".charCodeAt(0); -const CHAR_COLON = ":".charCodeAt(0); - -const relativeResolveCache = Object.create(null); - -let requireDepth = 0; -let statCache: Map | null = null; - -type StatResult = -1 | 0 | 1; -// Returns 0 if the path refers to -// a file, 1 when it's a directory or < 0 on error. -function stat(filename: string): StatResult { - filename = path.toNamespacedPath(filename); - if (statCache !== null) { - const result = statCache.get(filename); - if (result !== undefined) return result; - } - try { - const info = Deno.statSync(filename); - const result = info.isFile ? 0 : 1; - if (statCache !== null) statCache.set(filename, result); - return result; - } catch (e) { - if (e instanceof Deno.errors.PermissionDenied) { - throw new Error("CJS loader requires --allow-read."); - } - return -1; - } -} - -function updateChildren( - parent: Module | null, - child: Module, - scan: boolean, -): void { - const children = parent && parent.children; - if (children && !(scan && children.includes(child))) { - children.push(child); - } -} - -class Module { - id: string; - // deno-lint-ignore no-explicit-any - exports: any; - parent: Module | null; - filename: string | null; - loaded: boolean; - children: Module[]; - paths: string[]; - path: string; - constructor(id = "", parent?: Module | null) { - this.id = id; - this.exports = {}; - this.parent = parent || null; - updateChildren(parent || null, this, false); - this.filename = null; - this.loaded = false; - this.children = []; - this.paths = []; - this.path = path.dirname(id); - } - static builtinModules: string[] = []; - static _extensions: { - // deno-lint-ignore no-explicit-any - [key: string]: (module: Module, filename: string) => any; - } = Object.create(null); - static _cache: { [key: string]: Module } = Object.create(null); - static _pathCache = Object.create(null); - static globalPaths: string[] = []; - // Proxy related code removed. - static wrapper = [ - "(function (exports, require, module, __filename, __dirname) { ", - "\n});", - ]; - - // Loads a module at the given file path. Returns that module's - // `exports` property. - // deno-lint-ignore no-explicit-any - require(id: string): any { - if (id === "") { - throw new Error(`id '${id}' must be a non-empty string`); - } - requireDepth++; - try { - return Module._load(id, this, /* isMain */ false); - } finally { - requireDepth--; - } - } - - // Given a file name, pass it to the proper extension handler. - load(filename: string): void { - assert(!this.loaded); - this.filename = filename; - this.paths = Module._nodeModulePaths(path.dirname(filename)); - - const extension = findLongestRegisteredExtension(filename); - // Removed ESM code - Module._extensions[extension](this, filename); - this.loaded = true; - // Removed ESM code - } - - // Run the file contents in the correct scope or sandbox. Expose - // the correct helper variables (require, module, exports) to - // the file. - // Returns exception, if any. - // deno-lint-ignore no-explicit-any - _compile(content: string, filename: string): any { - // manifest code removed - const compiledWrapper = wrapSafe(filename, content); - // inspector code remove - const dirname = path.dirname(filename); - const require = makeRequireFunction(this); - const exports = this.exports; - const thisValue = exports; - if (requireDepth === 0) { - statCache = new Map(); - } - const result = compiledWrapper.call( - thisValue, - exports, - require, - this, - filename, - dirname, - ); - if (requireDepth === 0) { - statCache = null; - } - return result; - } - - /* - * Check for node modules paths. - * */ - static _resolveLookupPaths( - request: string, - parent: Module | null, - ): string[] | null { - if ( - request.charAt(0) !== "." || - (request.length > 1 && - request.charAt(1) !== "." && - request.charAt(1) !== "/" && - (!isWindows || request.charAt(1) !== "\\")) - ) { - let paths = modulePaths; - if (parent !== null && parent.paths && parent.paths.length) { - paths = parent.paths.concat(paths); - } - - return paths.length > 0 ? paths : null; - } - - // With --eval, parent.id is not set and parent.filename is null. - if (!parent || !parent.id || !parent.filename) { - // Make require('./path/to/foo') work - normally the path is taken - // from realpath(__filename) but with eval there is no filename - return ["."].concat(Module._nodeModulePaths("."), modulePaths); - } - // Returns the parent path of the file - return [path.dirname(parent.filename)]; - } - - static _resolveFilename( - request: string, - parent: Module, - isMain: boolean, - options?: { paths: string[] }, - ): string { - // Polyfills. - if (nativeModuleCanBeRequiredByUsers(request)) { - return request; - } - - let paths: string[]; - - if (typeof options === "object" && options !== null) { - if (Array.isArray(options.paths)) { - const isRelative = request.startsWith("./") || - request.startsWith("../") || - (isWindows && request.startsWith(".\\")) || - request.startsWith("..\\"); - - if (isRelative) { - paths = options.paths; - } else { - const fakeParent = new Module("", null); - - paths = []; - - for (let i = 0; i < options.paths.length; i++) { - const path = options.paths[i]; - fakeParent.paths = Module._nodeModulePaths(path); - const lookupPaths = Module._resolveLookupPaths(request, fakeParent); - - for (let j = 0; j < lookupPaths!.length; j++) { - if (!paths.includes(lookupPaths![j])) { - paths.push(lookupPaths![j]); - } - } - } - } - } else if (options.paths === undefined) { - paths = Module._resolveLookupPaths(request, parent)!; - } else { - throw new Error("options.paths is invalid"); - } - } else { - paths = Module._resolveLookupPaths(request, parent)!; - } - - // Look up the filename first, since that's the cache key. - const filename = Module._findPath(request, paths, isMain); - if (!filename) { - const requireStack = []; - for (let cursor: Module | null = parent; cursor; cursor = cursor.parent) { - requireStack.push(cursor.filename || cursor.id); - } - let message = `Cannot find module '${request}'`; - if (requireStack.length > 0) { - message = message + "\nRequire stack:\n- " + requireStack.join("\n- "); - } - const err = new Error(message) as Error & { - code: string; - requireStack: string[]; - }; - err.code = "MODULE_NOT_FOUND"; - err.requireStack = requireStack; - throw err; - } - return filename as string; - } - - static _findPath( - request: string, - paths: string[], - isMain: boolean, - ): string | boolean { - const absoluteRequest = path.isAbsolute(request); - if (absoluteRequest) { - paths = [""]; - } else if (!paths || paths.length === 0) { - return false; - } - - const cacheKey = request + "\x00" + - (paths.length === 1 ? paths[0] : paths.join("\x00")); - const entry = Module._pathCache[cacheKey]; - if (entry) { - return entry; - } - - let exts; - let trailingSlash = request.length > 0 && - request.charCodeAt(request.length - 1) === CHAR_FORWARD_SLASH; - if (!trailingSlash) { - trailingSlash = /(?:^|\/)\.?\.$/.test(request); - } - - // For each path - for (let i = 0; i < paths.length; i++) { - // Don't search further if path doesn't exist - const curPath = paths[i]; - - if (curPath && stat(curPath) < 1) continue; - const basePath = resolveExports(curPath, request, absoluteRequest); - let filename; - - const rc = stat(basePath); - if (!trailingSlash) { - if (rc === 0) { - // File. - // preserveSymlinks removed - filename = toRealPath(basePath); - } - - if (!filename) { - // Try it with each of the extensions - if (exts === undefined) exts = Object.keys(Module._extensions); - filename = tryExtensions(basePath, exts, isMain); - } - } - - if (!filename && rc === 1) { - // Directory. - // try it with each of the extensions at "index" - if (exts === undefined) exts = Object.keys(Module._extensions); - filename = tryPackage(basePath, exts, isMain, request); - } - - if (filename) { - Module._pathCache[cacheKey] = filename; - return filename; - } - } - // trySelf removed. - - return false; - } - - // Check the cache for the requested file. - // 1. If a module already exists in the cache: return its exports object. - // 2. If the module is native: call - // `NativeModule.prototype.compileForPublicLoader()` and return the exports. - // 3. Otherwise, create a new module for the file and save it to the cache. - // Then have it load the file contents before returning its exports - // object. - // deno-lint-ignore no-explicit-any - static _load(request: string, parent: Module, isMain: boolean): any { - let relResolveCacheIdentifier: string | undefined; - if (parent) { - // Fast path for (lazy loaded) modules in the same directory. The indirect - // caching is required to allow cache invalidation without changing the old - // cache key names. - relResolveCacheIdentifier = `${parent.path}\x00${request}`; - const filename = relativeResolveCache[relResolveCacheIdentifier]; - if (filename !== undefined) { - const cachedModule = Module._cache[filename]; - if (cachedModule !== undefined) { - updateChildren(parent, cachedModule, true); - if (!cachedModule.loaded) { - return getExportsForCircularRequire(cachedModule); - } - return cachedModule.exports; - } - delete relativeResolveCache[relResolveCacheIdentifier]; - } - } - - const filename = Module._resolveFilename(request, parent, isMain); - - const cachedModule = Module._cache[filename]; - if (cachedModule !== undefined) { - updateChildren(parent, cachedModule, true); - if (!cachedModule.loaded) { - return getExportsForCircularRequire(cachedModule); - } - return cachedModule.exports; - } - - // Native module polyfills - const mod = loadNativeModule(filename, request); - if (mod) return mod.exports; - - // Don't call updateChildren(), Module constructor already does. - const module = new Module(filename, parent); - - if (isMain) { - // TODO(bartlomieju): set process info - // process.mainModule = module; - module.id = "."; - } - - Module._cache[filename] = module; - if (parent !== undefined) { - assert(relResolveCacheIdentifier); - relativeResolveCache[relResolveCacheIdentifier] = filename; - } - - let threw = true; - try { - // Source map code removed - module.load(filename); - threw = false; - } finally { - if (threw) { - delete Module._cache[filename]; - if (parent !== undefined) { - assert(relResolveCacheIdentifier); - delete relativeResolveCache[relResolveCacheIdentifier]; - } - } else if ( - module.exports && - Object.getPrototypeOf(module.exports) === - CircularRequirePrototypeWarningProxy - ) { - Object.setPrototypeOf(module.exports, PublicObjectPrototype); - } - } - - return module.exports; - } - - static wrap(script: string): string { - return `${Module.wrapper[0]}${script}${Module.wrapper[1]}`; - } - - static _nodeModulePaths(from: string): string[] { - if (isWindows) { - // Guarantee that 'from' is absolute. - from = path.resolve(from); - - // note: this approach *only* works when the path is guaranteed - // to be absolute. Doing a fully-edge-case-correct path.split - // that works on both Windows and Posix is non-trivial. - - // return root node_modules when path is 'D:\\'. - // path.resolve will make sure from.length >=3 in Windows. - if ( - from.charCodeAt(from.length - 1) === CHAR_BACKWARD_SLASH && - from.charCodeAt(from.length - 2) === CHAR_COLON - ) { - return [from + "node_modules"]; - } - - const paths = []; - for (let i = from.length - 1, p = 0, last = from.length; i >= 0; --i) { - const code = from.charCodeAt(i); - // The path segment separator check ('\' and '/') was used to get - // node_modules path for every path segment. - // Use colon as an extra condition since we can get node_modules - // path for drive root like 'C:\node_modules' and don't need to - // parse drive name. - if ( - code === CHAR_BACKWARD_SLASH || - code === CHAR_FORWARD_SLASH || - code === CHAR_COLON - ) { - if (p !== nmLen) paths.push(from.slice(0, last) + "\\node_modules"); - last = i; - p = 0; - } else if (p !== -1) { - if (nmChars[p] === code) { - ++p; - } else { - p = -1; - } - } - } - - return paths; - } else { - // posix - // Guarantee that 'from' is absolute. - from = path.resolve(from); - // Return early not only to avoid unnecessary work, but to *avoid* returning - // an array of two items for a root: [ '//node_modules', '/node_modules' ] - if (from === "/") return ["/node_modules"]; - - // note: this approach *only* works when the path is guaranteed - // to be absolute. Doing a fully-edge-case-correct path.split - // that works on both Windows and Posix is non-trivial. - const paths = []; - for (let i = from.length - 1, p = 0, last = from.length; i >= 0; --i) { - const code = from.charCodeAt(i); - if (code === CHAR_FORWARD_SLASH) { - if (p !== nmLen) paths.push(from.slice(0, last) + "/node_modules"); - last = i; - p = 0; - } else if (p !== -1) { - if (nmChars[p] === code) { - ++p; - } else { - p = -1; - } - } - } - - // Append /node_modules to handle root paths. - paths.push("/node_modules"); - - return paths; - } - } - - /** - * Create a `require` function that can be used to import CJS modules. - * Follows CommonJS resolution similar to that of Node.js, - * with `node_modules` lookup and `index.js` lookup support. - * Also injects available Node.js builtin module polyfills. - * - * const require = createRequire(import.meta.url); - * const fs = require("fs"); - * const leftPad = require("left-pad"); - * const cjsModule = require("./cjs_mod"); - * - * @param filename path or URL to current module - * @return Require function to import CJS modules - */ - static createRequire(filename: string | URL): RequireFunction { - let filepath: string; - if ( - filename instanceof URL || - (typeof filename === "string" && !path.isAbsolute(filename)) - ) { - filepath = fileURLToPath(filename); - } else if (typeof filename !== "string") { - throw new Error("filename should be a string"); - } else { - filepath = filename; - } - return createRequireFromPath(filepath); - } - - static _initPaths(): void { - const homeDir = Deno.env.get("HOME"); - const nodePath = Deno.env.get("NODE_PATH"); - - // Removed $PREFIX/bin/node case - - let paths = []; - - if (homeDir) { - paths.unshift(path.resolve(homeDir, ".node_libraries")); - paths.unshift(path.resolve(homeDir, ".node_modules")); - } - - if (nodePath) { - paths = nodePath - .split(path.delimiter) - .filter(function pathsFilterCB(path) { - return !!path; - }) - .concat(paths); - } - - modulePaths = paths; - - // Clone as a shallow copy, for introspection. - Module.globalPaths = modulePaths.slice(0); - } - - static _preloadModules(requests: string[]): void { - if (!Array.isArray(requests)) { - return; - } - - // Preloaded modules have a dummy parent module which is deemed to exist - // in the current working directory. This seeds the search path for - // preloaded modules. - const parent = new Module("internal/preload", null); - try { - parent.paths = Module._nodeModulePaths(Deno.cwd()); - } catch (e) { - if (e.code !== "ENOENT") { - throw e; - } - } - for (let n = 0; n < requests.length; n++) { - parent.require(requests[n]); - } - } -} - -// Polyfills. -const nativeModulePolyfill = new Map(); -// deno-lint-ignore no-explicit-any -function createNativeModule(id: string, exports: any): Module { - const mod = new Module(id); - mod.exports = exports; - mod.loaded = true; - return mod; -} - -nativeModulePolyfill.set("buffer", createNativeModule("buffer", nodeBuffer)); -nativeModulePolyfill.set("events", createNativeModule("events", nodeEvents)); -nativeModulePolyfill.set("fs", createNativeModule("fs", nodeFS)); -nativeModulePolyfill.set("os", createNativeModule("os", nodeOs)); -nativeModulePolyfill.set("path", createNativeModule("path", nodePath)); -nativeModulePolyfill.set( - "querystring", - createNativeModule("querystring", nodeQueryString), -); -nativeModulePolyfill.set( - "stream", - createNativeModule("string_decoder", nodeStream), -); -nativeModulePolyfill.set( - "string_decoder", - createNativeModule("string_decoder", nodeStringDecoder), -); -nativeModulePolyfill.set("timers", createNativeModule("timers", nodeTimers)); -nativeModulePolyfill.set("util", createNativeModule("util", nodeUtil)); - -function loadNativeModule( - _filename: string, - request: string, -): Module | undefined { - return nativeModulePolyfill.get(request); -} -function nativeModuleCanBeRequiredByUsers(request: string): boolean { - return nativeModulePolyfill.has(request); -} -// Populate with polyfill names -for (const id of nativeModulePolyfill.keys()) { - Module.builtinModules.push(id); -} - -let modulePaths: string[] = []; - -// Given a module name, and a list of paths to test, returns the first -// matching file in the following precedence. -// -// require("a.") -// -> a. -// -// require("a") -// -> a -// -> a. -// -> a/index. - -const packageJsonCache = new Map(); - -interface PackageInfo { - name?: string; - main?: string; - // deno-lint-ignore no-explicit-any - exports?: any; - // deno-lint-ignore no-explicit-any - type?: any; -} - -function readPackage(requestPath: string): PackageInfo | null { - const jsonPath = path.resolve(requestPath, "package.json"); - - const existing = packageJsonCache.get(jsonPath); - if (existing !== undefined) { - return existing; - } - - let json: string | undefined; - try { - json = new TextDecoder().decode( - Deno.readFileSync(path.toNamespacedPath(jsonPath)), - ); - } catch { - // pass - } - - if (json === undefined) { - packageJsonCache.set(jsonPath, null); - return null; - } - - try { - const parsed = JSON.parse(json); - const filtered = { - name: parsed.name, - main: parsed.main, - exports: parsed.exports, - type: parsed.type, - }; - packageJsonCache.set(jsonPath, filtered); - return filtered; - } catch (e) { - e.path = jsonPath; - e.message = "Error parsing " + jsonPath + ": " + e.message; - throw e; - } -} - -function readPackageScope( - checkPath: string, -): { path: string; data: PackageInfo } | false { - const rootSeparatorIndex = checkPath.indexOf(path.sep); - let separatorIndex; - while ( - (separatorIndex = checkPath.lastIndexOf(path.sep)) > rootSeparatorIndex - ) { - checkPath = checkPath.slice(0, separatorIndex); - if (checkPath.endsWith(path.sep + "node_modules")) return false; - const pjson = readPackage(checkPath); - if (pjson) { - return { - path: checkPath, - data: pjson, - }; - } - } - return false; -} - -function readPackageMain(requestPath: string): string | undefined { - const pkg = readPackage(requestPath); - return pkg ? pkg.main : undefined; -} - -// deno-lint-ignore no-explicit-any -function readPackageExports(requestPath: string): any | undefined { - const pkg = readPackage(requestPath); - return pkg ? pkg.exports : undefined; -} - -function tryPackage( - requestPath: string, - exts: string[], - isMain: boolean, - _originalPath: string, -): string | false { - const pkg = readPackageMain(requestPath); - - if (!pkg) { - return tryExtensions(path.resolve(requestPath, "index"), exts, isMain); - } - - const filename = path.resolve(requestPath, pkg); - let actual = tryFile(filename, isMain) || - tryExtensions(filename, exts, isMain) || - tryExtensions(path.resolve(filename, "index"), exts, isMain); - if (actual === false) { - actual = tryExtensions(path.resolve(requestPath, "index"), exts, isMain); - if (!actual) { - const err = new Error( - `Cannot find module '${filename}'. ` + - 'Please verify that the package.json has a valid "main" entry', - ) as Error & { code: string }; - err.code = "MODULE_NOT_FOUND"; - throw err; - } - } - return actual; -} - -// Check if the file exists and is not a directory -// if using --preserve-symlinks and isMain is false, -// keep symlinks intact, otherwise resolve to the -// absolute realpath. -function tryFile(requestPath: string, _isMain: boolean): string | false { - const rc = stat(requestPath); - return rc === 0 && toRealPath(requestPath); -} - -function toRealPath(requestPath: string): string { - return Deno.realPathSync(requestPath); -} - -// Given a path, check if the file exists with any of the set extensions -function tryExtensions( - p: string, - exts: string[], - isMain: boolean, -): string | false { - for (let i = 0; i < exts.length; i++) { - const filename = tryFile(p + exts[i], isMain); - - if (filename) { - return filename; - } - } - return false; -} - -// Find the longest (possibly multi-dot) extension registered in -// Module._extensions -function findLongestRegisteredExtension(filename: string): string { - const name = path.basename(filename); - let currentExtension; - let index; - let startIndex = 0; - while ((index = name.indexOf(".", startIndex)) !== -1) { - startIndex = index + 1; - if (index === 0) continue; // Skip dotfiles like .gitignore - currentExtension = name.slice(index); - if (Module._extensions[currentExtension]) return currentExtension; - } - return ".js"; -} - -// --experimental-resolve-self trySelf() support removed. - -// deno-lint-ignore no-explicit-any -function isConditionalDotExportSugar(exports: any, _basePath: string): boolean { - if (typeof exports === "string") return true; - if (Array.isArray(exports)) return true; - if (typeof exports !== "object") return false; - let isConditional = false; - let firstCheck = true; - for (const key of Object.keys(exports)) { - const curIsConditional = key[0] !== "."; - if (firstCheck) { - firstCheck = false; - isConditional = curIsConditional; - } else if (isConditional !== curIsConditional) { - throw new Error( - '"exports" cannot ' + - "contain some keys starting with '.' and some not. The exports " + - "object must either be an object of package subpath keys or an " + - "object of main entry condition name keys only.", - ); - } - } - return isConditional; -} - -function applyExports(basePath: string, expansion: string): string { - const mappingKey = `.${expansion}`; - - let pkgExports = readPackageExports(basePath); - if (pkgExports === undefined || pkgExports === null) { - return path.resolve(basePath, mappingKey); - } - - if (isConditionalDotExportSugar(pkgExports, basePath)) { - pkgExports = { ".": pkgExports }; - } - - if (typeof pkgExports === "object") { - if (Object.prototype.hasOwnProperty.call(pkgExports, mappingKey)) { - const mapping = pkgExports[mappingKey]; - return resolveExportsTarget( - pathToFileURL(basePath + "/"), - mapping, - "", - basePath, - mappingKey, - ); - } - - // Fallback to CJS main lookup when no main export is defined - if (mappingKey === ".") return basePath; - - let dirMatch = ""; - for (const candidateKey of Object.keys(pkgExports)) { - if (candidateKey[candidateKey.length - 1] !== "/") continue; - if ( - candidateKey.length > dirMatch.length && - mappingKey.startsWith(candidateKey) - ) { - dirMatch = candidateKey; - } - } - - if (dirMatch !== "") { - const mapping = pkgExports[dirMatch]; - const subpath = mappingKey.slice(dirMatch.length); - return resolveExportsTarget( - pathToFileURL(basePath + "/"), - mapping, - subpath, - basePath, - mappingKey, - ); - } - } - // Fallback to CJS main lookup when no main export is defined - if (mappingKey === ".") return basePath; - - const e = new Error( - `Package exports for '${basePath}' do not define ` + - `a '${mappingKey}' subpath`, - ) as Error & { code?: string }; - e.code = "MODULE_NOT_FOUND"; - throw e; -} - -// This only applies to requests of a specific form: -// 1. name/.* -// 2. @scope/name/.* -const EXPORTS_PATTERN = /^((?:@[^/\\%]+\/)?[^./\\%][^/\\%]*)(\/.*)?$/; -function resolveExports( - nmPath: string, - request: string, - absoluteRequest: boolean, -): string { - // The implementation's behavior is meant to mirror resolution in ESM. - if (!absoluteRequest) { - const [, name, expansion = ""] = request.match(EXPORTS_PATTERN) || []; - if (!name) { - return path.resolve(nmPath, request); - } - - const basePath = path.resolve(nmPath, name); - return applyExports(basePath, expansion); - } - - return path.resolve(nmPath, request); -} - -function resolveExportsTarget( - pkgPath: URL, - // deno-lint-ignore no-explicit-any - target: any, - subpath: string, - basePath: string, - mappingKey: string, -): string { - if (typeof target === "string") { - if ( - target.startsWith("./") && - (subpath.length === 0 || target.endsWith("/")) - ) { - const resolvedTarget = new URL(target, pkgPath); - const pkgPathPath = pkgPath.pathname; - const resolvedTargetPath = resolvedTarget.pathname; - if ( - resolvedTargetPath.startsWith(pkgPathPath) && - resolvedTargetPath.indexOf("/node_modules/", pkgPathPath.length - 1) === - -1 - ) { - const resolved = new URL(subpath, resolvedTarget); - const resolvedPath = resolved.pathname; - if ( - resolvedPath.startsWith(resolvedTargetPath) && - resolvedPath.indexOf("/node_modules/", pkgPathPath.length - 1) === -1 - ) { - return fileURLToPath(resolved); - } - } - } - } else if (Array.isArray(target)) { - for (const targetValue of target) { - if (Array.isArray(targetValue)) continue; - try { - return resolveExportsTarget( - pkgPath, - targetValue, - subpath, - basePath, - mappingKey, - ); - } catch (e) { - if (e.code !== "MODULE_NOT_FOUND") throw e; - } - } - } else if (typeof target === "object" && target !== null) { - // removed experimentalConditionalExports - if (Object.prototype.hasOwnProperty.call(target, "default")) { - try { - return resolveExportsTarget( - pkgPath, - target.default, - subpath, - basePath, - mappingKey, - ); - } catch (e) { - if (e.code !== "MODULE_NOT_FOUND") throw e; - } - } - } - let e: Error & { code?: string }; - if (mappingKey !== ".") { - e = new Error( - `Package exports for '${basePath}' do not define a ` + - `valid '${mappingKey}' target${subpath ? " for " + subpath : ""}`, - ); - } else { - e = new Error(`No valid exports main found for '${basePath}'`); - } - e.code = "MODULE_NOT_FOUND"; - throw e; -} - -// 'node_modules' character codes reversed -const nmChars = [115, 101, 108, 117, 100, 111, 109, 95, 101, 100, 111, 110]; -const nmLen = nmChars.length; - -// deno-lint-ignore no-explicit-any -function emitCircularRequireWarning(prop: any): void { - console.error( - `Accessing non-existent property '${ - String(prop) - }' of module exports inside circular dependency`, - ); -} - -// A Proxy that can be used as the prototype of a module.exports object and -// warns when non-existent properties are accessed. -const CircularRequirePrototypeWarningProxy = new Proxy( - {}, - { - // deno-lint-ignore no-explicit-any - get(target: Record, prop: string): any { - if (prop in target) return target[prop]; - emitCircularRequireWarning(prop); - return undefined; - }, - - getOwnPropertyDescriptor(target, prop): PropertyDescriptor | undefined { - if (Object.prototype.hasOwnProperty.call(target, prop)) { - return Object.getOwnPropertyDescriptor(target, prop); - } - emitCircularRequireWarning(prop); - return undefined; - }, - }, -); - -// Object.prototype and ObjectProtoype refer to our 'primordials' versions -// and are not identical to the versions on the global object. -const PublicObjectPrototype = globalThis.Object.prototype; - -// deno-lint-ignore no-explicit-any -function getExportsForCircularRequire(module: Module): any { - if ( - module.exports && - Object.getPrototypeOf(module.exports) === PublicObjectPrototype && - // Exclude transpiled ES6 modules / TypeScript code because those may - // employ unusual patterns for accessing 'module.exports'. That should be - // okay because ES6 modules have a different approach to circular - // dependencies anyway. - !module.exports.__esModule - ) { - // This is later unset once the module is done loading. - Object.setPrototypeOf(module.exports, CircularRequirePrototypeWarningProxy); - } - - return module.exports; -} - -type RequireWrapper = ( - // deno-lint-ignore no-explicit-any - exports: any, - // deno-lint-ignore no-explicit-any - require: any, - module: Module, - __filename: string, - __dirname: string, -) => void; - -function wrapSafe(filename: string, content: string): RequireWrapper { - // TODO(bartlomieju): fix this - const wrapper = Module.wrap(content); - // deno-lint-ignore no-explicit-any - const [f, err] = (Deno as any).core.evalContext(wrapper, filename); - if (err) { - throw err; - } - return f; - // ESM code removed. -} - -// Native extension for .js -Module._extensions[".js"] = (module: Module, filename: string): void => { - if (filename.endsWith(".js")) { - const pkg = readPackageScope(filename); - if (pkg !== false && pkg.data && pkg.data.type === "module") { - throw new Error("Importing ESM module"); - } - } - const content = new TextDecoder().decode(Deno.readFileSync(filename)); - module._compile(content, filename); -}; - -// Native extension for .json -Module._extensions[".json"] = (module: Module, filename: string): void => { - const content = new TextDecoder().decode(Deno.readFileSync(filename)); - // manifest code removed - try { - module.exports = JSON.parse(stripBOM(content)); - } catch (err) { - err.message = filename + ": " + err.message; - throw err; - } -}; - -// .node extension is not supported - -function createRequireFromPath(filename: string): RequireFunction { - // Allow a directory to be passed as the filename - const trailingSlash = filename.endsWith("/") || - (isWindows && filename.endsWith("\\")); - - const proxyPath = trailingSlash ? path.join(filename, "noop.js") : filename; - - const m = new Module(proxyPath); - m.filename = proxyPath; - - m.paths = Module._nodeModulePaths(m.path); - return makeRequireFunction(m); -} - -// deno-lint-ignore no-explicit-any -type Require = (id: string) => any; -// deno-lint-ignore no-explicit-any -type RequireResolve = (request: string, options: any) => string; -interface RequireResolveFunction extends RequireResolve { - paths: (request: string) => string[] | null; -} - -interface RequireFunction extends Require { - resolve: RequireResolveFunction; - // deno-lint-ignore no-explicit-any - extensions: { [key: string]: (module: Module, filename: string) => any }; - cache: { [key: string]: Module }; -} - -function makeRequireFunction(mod: Module): RequireFunction { - // deno-lint-ignore no-explicit-any - const require = function require(path: string): any { - return mod.require(path); - }; - - function resolve(request: string, options?: { paths: string[] }): string { - return Module._resolveFilename(request, mod, false, options); - } - - require.resolve = resolve; - - function paths(request: string): string[] | null { - return Module._resolveLookupPaths(request, mod); - } - - resolve.paths = paths; - // TODO(bartlomieju): set main - // require.main = process.mainModule; - - // Enable support to add extra extension types. - require.extensions = Module._extensions; - - require.cache = Module._cache; - - return require; -} - -/** - * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) - * because the buffer-to-string conversion in `fs.readFileSync()` - * translates it to FEFF, the UTF-16 BOM. - */ -function stripBOM(content: string): string { - if (content.charCodeAt(0) === 0xfeff) { - content = content.slice(1); - } - return content; -} - -export const builtinModules = Module.builtinModules; -export const createRequire = Module.createRequire; -export default Module; diff --git a/std/node/module_test.ts b/std/node/module_test.ts deleted file mode 100644 index 58c325f94d..0000000000 --- a/std/node/module_test.ts +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - assert, - assertEquals, - assertStringIncludes, -} from "../testing/asserts.ts"; - -import * as path from "../path/mod.ts"; -import { createRequire } from "./module.ts"; - -const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); -const testdataDir = path.resolve(moduleDir, path.join("_fs", "testdata")); - -const require = createRequire(import.meta.url); - -Deno.test("requireSuccess", function () { - // Relative to import.meta.url - const result = require("./tests/cjs/cjs_a.js"); - assert("helloA" in result); - assert("helloB" in result); - assert("C" in result); - assert("leftPad" in result); - assertEquals(result.helloA(), "A"); - assertEquals(result.helloB(), "B"); - assertEquals(result.C, "C"); - assertEquals(result.leftPad("pad", 4), " pad"); -}); - -Deno.test("requireCycle", function () { - const resultA = require("./tests/cjs/cjs_cycle_a"); - const resultB = require("./tests/cjs/cjs_cycle_b"); - assert(resultA); - assert(resultB); -}); - -Deno.test("requireBuiltin", function () { - const fs = require("fs"); - assert("readFileSync" in fs); - const { readFileSync, isNull, extname } = require("./tests/cjs/cjs_builtin"); - - const testData = path.relative( - Deno.cwd(), - path.join(testdataDir, "hello.txt"), - ); - assertEquals( - readFileSync(testData, { encoding: "utf8" }), - "hello world", - ); - assert(isNull(null)); - assertEquals(extname("index.html"), ".html"); -}); - -Deno.test("requireIndexJS", function () { - const { isIndex } = require("./tests/cjs"); - assert(isIndex); -}); - -Deno.test("requireNodeOs", function () { - const os = require("os"); - assert(os.arch); - assert(typeof os.arch() == "string"); -}); - -Deno.test("requireStack", function () { - const { hello } = require("./tests/cjs/cjs_throw"); - try { - hello(); - } catch (e) { - assertStringIncludes(e.stack, "/tests/cjs/cjs_throw.js"); - } -}); - -Deno.test("requireFileInSymlinkDir", () => { - const { C } = require("./tests/cjs/dir"); - assertEquals(C, "C"); -}); diff --git a/std/node/os.ts b/std/node/os.ts deleted file mode 100644 index 776eff92d6..0000000000 --- a/std/node/os.ts +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -import { notImplemented } from "./_utils.ts"; -import { validateIntegerRange } from "./_utils.ts"; -import { EOL as fsEOL } from "../fs/eol.ts"; -import process from "./process.ts"; - -const SEE_GITHUB_ISSUE = "See https://github.com/denoland/deno/issues/3802"; - -interface CPUTimes { - /** The number of milliseconds the CPU has spent in user mode */ - user: number; - - /** The number of milliseconds the CPU has spent in nice mode */ - nice: number; - - /** The number of milliseconds the CPU has spent in sys mode */ - sys: number; - - /** The number of milliseconds the CPU has spent in idle mode */ - idle: number; - - /** The number of milliseconds the CPU has spent in irq mode */ - irq: number; -} - -interface CPUCoreInfo { - model: string; - - /** in MHz */ - speed: number; - - times: CPUTimes; -} - -interface NetworkAddress { - /** The assigned IPv4 or IPv6 address */ - address: string; - - /** The IPv4 or IPv6 network mask */ - netmask: string; - - family: "IPv4" | "IPv6"; - - /** The MAC address of the network interface */ - mac: string; - - /** true if the network interface is a loopback or similar interface that is not remotely accessible; otherwise false */ - internal: boolean; - - /** The numeric IPv6 scope ID (only specified when family is IPv6) */ - scopeid?: number; - - /** The assigned IPv4 or IPv6 address with the routing prefix in CIDR notation. If the netmask is invalid, this property is set to null. */ - cidr: string; -} - -interface NetworkInterfaces { - [key: string]: NetworkAddress[]; -} - -export interface UserInfoOptions { - encoding: string; -} - -interface UserInfo { - username: string; - uid: number; - gid: number; - shell: string; - homedir: string; -} - -arch[Symbol.toPrimitive] = (): string => arch(); -endianness[Symbol.toPrimitive] = (): string => endianness(); -freemem[Symbol.toPrimitive] = (): number => freemem(); -homedir[Symbol.toPrimitive] = (): string | null => homedir(); -hostname[Symbol.toPrimitive] = (): string | null => hostname(); -platform[Symbol.toPrimitive] = (): string => platform(); -release[Symbol.toPrimitive] = (): string => release(); -totalmem[Symbol.toPrimitive] = (): number => totalmem(); -type[Symbol.toPrimitive] = (): string => type(); -uptime[Symbol.toPrimitive] = (): number => uptime(); - -/** Returns the operating system CPU architecture for which the Deno binary was compiled */ -export function arch(): string { - return Deno.build.arch; -} - -/** Not yet implemented */ -export function cpus(): CPUCoreInfo[] { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** - * Returns a string identifying the endianness of the CPU for which the Deno - * binary was compiled. Possible values are 'BE' for big endian and 'LE' for - * little endian. - **/ -export function endianness(): "BE" | "LE" { - // Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView#Endianness - const buffer = new ArrayBuffer(2); - new DataView(buffer).setInt16(0, 256, true /* littleEndian */); - // Int16Array uses the platform's endianness. - return new Int16Array(buffer)[0] === 256 ? "LE" : "BE"; -} - -/** Return free memory amount */ -export function freemem(): number { - return Deno.systemMemoryInfo().free; -} - -/** Not yet implemented */ -export function getPriority(pid = 0): number { - validateIntegerRange(pid, "pid"); - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Returns the string path of the current user's home directory. */ -export function homedir(): string | null { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Returns the host name of the operating system as a string. */ -export function hostname(): string { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Returns an array containing the 1, 5, and 15 minute load averages */ -export function loadavg(): number[] { - if (Deno.build.os === "windows") { - return [0, 0, 0]; - } - return Deno.loadavg(); -} - -/** Not yet implemented */ -export function networkInterfaces(): NetworkInterfaces { - notImplemented(SEE_GITHUB_ISSUE); -} -/** Returns the a string identifying the operating system platform. The value is set at compile time. Possible values are 'darwin', 'linux', and 'win32'. */ -export function platform(): string { - return process.platform; -} - -/** Returns the operating system as a string */ -export function release(): string { - return Deno.osRelease(); -} - -/** Not yet implemented */ -export function setPriority(pid: number, priority?: number): void { - /* The node API has the 'pid' as the first parameter and as optional. - This makes for a problematic implementation in Typescript. */ - if (priority === undefined) { - priority = pid; - pid = 0; - } - validateIntegerRange(pid, "pid"); - validateIntegerRange(priority, "priority", -20, 19); - - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Returns the operating system's default directory for temporary files as a string. */ -export function tmpdir(): string | null { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Return total physical memory amount */ -export function totalmem(): number { - return Deno.systemMemoryInfo().total; -} - -/** Returns operating system type (i.e. 'Windows_NT', 'Linux', 'Darwin') */ -export function type(): string { - switch (Deno.build.os) { - case "windows": - return "Windows_NT"; - case "linux": - return "Linux"; - case "darwin": - return "Darwin"; - default: - throw Error("unreachable"); - } -} - -/** Not yet implemented */ -export function uptime(): number { - notImplemented(SEE_GITHUB_ISSUE); -} - -/** Not yet implemented */ -export function userInfo( - options: UserInfoOptions = { encoding: "utf-8" }, -): UserInfo { - notImplemented(SEE_GITHUB_ISSUE); -} - -export const constants = { - // UV_UDP_REUSEADDR: 4, //see https://nodejs.org/docs/latest-v12.x/api/os.html#os_libuv_constants - dlopen: { - // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_dlopen_constants - }, - errno: { - // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_error_constants - }, - signals: Deno.Signal, - priority: { - // see https://nodejs.org/docs/latest-v12.x/api/os.html#os_priority_constants - }, -}; - -export const EOL = Deno.build.os == "windows" ? fsEOL.CRLF : fsEOL.LF; - -export default { - arch, - cpus, - endianness, - freemem, - getPriority, - homedir, - hostname, - loadavg, - networkInterfaces, - platform, - release, - setPriority, - tmpdir, - totalmem, - type, - uptime, - userInfo, - constants, - EOL, -}; diff --git a/std/node/os_test.ts b/std/node/os_test.ts deleted file mode 100644 index 6d5c124ce2..0000000000 --- a/std/node/os_test.ts +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals, assertThrows } from "../testing/asserts.ts"; -import * as os from "./os.ts"; - -Deno.test({ - name: "build architecture is a string", - fn() { - assertEquals(typeof os.arch(), "string"); - }, -}); - -Deno.test({ - name: "home directory is a string", - ignore: true, - fn() { - assertEquals(typeof os.homedir(), "string"); - }, -}); - -Deno.test({ - name: "tmp directory is a string", - ignore: true, - fn() { - assertEquals(typeof os.tmpdir(), "string"); - }, -}); - -Deno.test({ - name: "hostname is a string", - ignore: true, - 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: "Signals are as expected", - fn() { - // Test a few random signals for equality - assertEquals(os.constants.signals.SIGKILL, Deno.Signal.SIGKILL); - assertEquals(os.constants.signals.SIGCONT, Deno.Signal.SIGCONT); - assertEquals(os.constants.signals.SIGXFSZ, Deno.Signal.SIGXFSZ); - }, -}); - -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: "APIs not yet implemented", - fn() { - assertThrows( - () => { - os.cpus(); - }, - Error, - "Not implemented", - ); - assertThrows( - () => { - os.getPriority(); - }, - Error, - "Not implemented", - ); - assertThrows( - () => { - os.networkInterfaces(); - }, - Error, - "Not implemented", - ); - assertThrows( - () => { - os.setPriority(0); - }, - Error, - "Not implemented", - ); - assertThrows( - () => { - os.uptime(); - }, - Error, - "Not implemented", - ); - assertThrows( - () => { - os.userInfo(); - }, - Error, - "Not implemented", - ); - }, -}); diff --git a/std/node/path.ts b/std/node/path.ts deleted file mode 100644 index 0f29ad9b01..0000000000 --- a/std/node/path.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -export * from "../path/mod.ts"; -import * as m from "../path/mod.ts"; -export default m; diff --git a/std/node/process.ts b/std/node/process.ts deleted file mode 100644 index 419eb8cecf..0000000000 --- a/std/node/process.ts +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { notImplemented } from "./_utils.ts"; -import EventEmitter from "./events.ts"; -import { fromFileUrl } from "../path/mod.ts"; - -const notImplementedEvents = [ - "beforeExit", - "disconnect", - "message", - "multipleResolves", - "rejectionHandled", - "SIGBREAK", - "SIGBUS", - "SIGFPE", - "SIGHUP", - "SIGILL", - "SIGINT", - "SIGSEGV", - "SIGTERM", - "SIGWINCH", - "uncaughtException", - "uncaughtExceptionMonitor", - "unhandledRejection", - "warning", -]; - -/** https://nodejs.org/api/process.html#process_process_arch */ -export const arch = Deno.build.arch; - -function getArguments() { - return [Deno.execPath(), fromFileUrl(Deno.mainModule), ...Deno.args]; -} - -//deno-lint-ignore ban-ts-comment -//@ts-ignore -const _argv: { - [Deno.customInspect]: () => string; - [key: number]: string; -} = []; - -Object.defineProperty(_argv, Deno.customInspect, { - enumerable: false, - configurable: false, - get: function () { - return getArguments(); - }, -}); - -/** - * https://nodejs.org/api/process.html#process_process_argv - * Read permissions are required in order to get the executable route - * */ -export const argv: Record = new Proxy(_argv, { - get(target, prop) { - if (prop === Deno.customInspect) { - return target[Deno.customInspect]; - } - return getArguments()[prop as number]; - }, - ownKeys() { - return Reflect.ownKeys(getArguments()); - }, -}); - -/** https://nodejs.org/api/process.html#process_process_chdir_directory */ -export const chdir = Deno.chdir; - -/** https://nodejs.org/api/process.html#process_process_cwd */ -export const cwd = Deno.cwd; - -//deno-lint-ignore ban-ts-comment -//@ts-ignore -const _env: { - [Deno.customInspect]: () => string; -} = {}; - -Object.defineProperty(_env, Deno.customInspect, { - enumerable: false, - configurable: false, - get: function () { - return Deno.env.toObject(); - }, -}); - -/** - * https://nodejs.org/api/process.html#process_process_env - * Requires env permissions - * */ -export const env: Record = new Proxy(_env, { - get(target, prop) { - if (prop === Deno.customInspect) { - return target[Deno.customInspect]; - } - return Deno.env.get(String(prop)); - }, - ownKeys() { - return Reflect.ownKeys(Deno.env.toObject()); - }, - set(_target, prop, value) { - Deno.env.set(String(prop), String(value)); - return value; - }, -}); - -/** https://nodejs.org/api/process.html#process_process_exit_code */ -export const exit = Deno.exit; - -/** https://nodejs.org/api/process.html#process_process_nexttick_callback_args */ -export function nextTick(this: unknown, cb: () => void): void; -export function nextTick>( - this: unknown, - cb: (...args: T) => void, - ...args: T -): void; -export function nextTick>( - this: unknown, - cb: (...args: T) => void, - ...args: T -) { - if (args) { - queueMicrotask(() => cb.call(this, ...args)); - } else { - queueMicrotask(cb); - } -} - -/** https://nodejs.org/api/process.html#process_process_pid */ -export const pid = Deno.pid; - -/** https://nodejs.org/api/process.html#process_process_platform */ -export const platform = Deno.build.os === "windows" ? "win32" : Deno.build.os; - -/** https://nodejs.org/api/process.html#process_process_version */ -export const version = `v${Deno.version.deno}`; - -/** https://nodejs.org/api/process.html#process_process_versions */ -export const versions = { - node: Deno.version.deno, - ...Deno.version, -}; - -class Process extends EventEmitter { - constructor() { - super(); - - //This causes the exit event to be binded to the unload event - window.addEventListener("unload", () => { - //TODO(Soremwar) - //Get the exit code from the unload event - super.emit("exit", 0); - }); - } - - /** https://nodejs.org/api/process.html#process_process_arch */ - arch = arch; - - /** - * https://nodejs.org/api/process.html#process_process_argv - * Read permissions are required in order to get the executable route - * */ - argv = argv; - - /** https://nodejs.org/api/process.html#process_process_chdir_directory */ - chdir = chdir; - - /** https://nodejs.org/api/process.html#process_process_cwd */ - cwd = cwd; - - /** https://nodejs.org/api/process.html#process_process_exit_code */ - exit = exit; - - /** - * https://nodejs.org/api/process.html#process_process_env - * Requires env permissions - * */ - env = env; - - /** https://nodejs.org/api/process.html#process_process_nexttick_callback_args */ - nextTick = nextTick; - - /** https://nodejs.org/api/process.html#process_process_events */ - //deno-lint-ignore ban-types - on(event: typeof notImplementedEvents[number], listener: Function): never; - on(event: "exit", listener: (code: number) => void): this; - //deno-lint-ignore no-explicit-any - on(event: string, listener: (...args: any[]) => void): this { - if (notImplementedEvents.includes(event)) { - notImplemented(); - } - - super.on(event, listener); - - return this; - } - - /** https://nodejs.org/api/process.html#process_process_pid */ - pid = pid; - - /** https://nodejs.org/api/process.html#process_process_platform */ - platform = platform; - - removeAllListeners(_event: string): never { - notImplemented(); - } - - removeListener( - event: typeof notImplementedEvents[number], - //deno-lint-ignore ban-types - listener: Function, - ): never; - removeListener(event: "exit", listener: (code: number) => void): this; - //deno-lint-ignore no-explicit-any - removeListener(event: string, listener: (...args: any[]) => void): this { - if (notImplementedEvents.includes(event)) { - notImplemented(); - } - - super.removeListener("exit", listener); - - return this; - } - - /** https://nodejs.org/api/process.html#process_process_stderr */ - get stderr() { - return { - fd: Deno.stderr.rid, - get isTTY(): boolean { - return Deno.isatty(this.fd); - }, - pipe(_destination: Deno.Writer, _options: { end: boolean }): void { - // TODO(JayHelton): to be implemented - notImplemented(); - }, - // deno-lint-ignore ban-types - write(_chunk: string | Uint8Array, _callback: Function): void { - // TODO(JayHelton): to be implemented - notImplemented(); - }, - // deno-lint-ignore ban-types - on(_event: string, _callback: Function): void { - // TODO(JayHelton): to be implemented - notImplemented(); - }, - }; - } - - /** https://nodejs.org/api/process.html#process_process_stdin */ - get stdin() { - return { - fd: Deno.stdin.rid, - get isTTY(): boolean { - return Deno.isatty(this.fd); - }, - read(_size: number): void { - // TODO(JayHelton): to be implemented - notImplemented(); - }, - // deno-lint-ignore ban-types - on(_event: string, _callback: Function): void { - // TODO(JayHelton): to be implemented - notImplemented(); - }, - }; - } - - /** https://nodejs.org/api/process.html#process_process_stdout */ - get stdout() { - return { - fd: Deno.stdout.rid, - get isTTY(): boolean { - return Deno.isatty(this.fd); - }, - pipe(_destination: Deno.Writer, _options: { end: boolean }): void { - // TODO(JayHelton): to be implemented - notImplemented(); - }, - // deno-lint-ignore ban-types - write(_chunk: string | Uint8Array, _callback: Function): void { - // TODO(JayHelton): to be implemented - notImplemented(); - }, - // deno-lint-ignore ban-types - on(_event: string, _callback: Function): void { - // TODO(JayHelton): to be implemented - notImplemented(); - }, - }; - } - - /** https://nodejs.org/api/process.html#process_process_version */ - version = version; - - /** https://nodejs.org/api/process.html#process_process_versions */ - versions = versions; -} - -/** https://nodejs.org/api/process.html#process_process */ -const process = new Process(); - -Object.defineProperty(process, Symbol.toStringTag, { - enumerable: false, - writable: true, - configurable: false, - value: "process", -}); - -export const removeListener = process.removeListener; -export const removeAllListeners = process.removeAllListeners; -export const stderr = process.stderr; -export const stdin = process.stdin; -export const stdout = process.stdout; - -export default process; - -//TODO(Soremwar) -//Remove on 1.0 -//Kept for backwars compatibility with std -export { process }; diff --git a/std/node/process_exit_test.ts b/std/node/process_exit_test.ts deleted file mode 100644 index 54c8bcc01b..0000000000 --- a/std/node/process_exit_test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import "./global.ts"; - -//deno-lint-ignore no-undef -process.on("exit", () => { - console.log(1); -}); - -function unexpected() { - console.log(null); -} -//deno-lint-ignore no-undef -process.on("exit", unexpected); -//deno-lint-ignore no-undef -process.removeListener("exit", unexpected); - -//deno-lint-ignore no-undef -process.on("exit", () => { - console.log(2); -}); diff --git a/std/node/process_test.ts b/std/node/process_test.ts deleted file mode 100644 index 22cebf260e..0000000000 --- a/std/node/process_test.ts +++ /dev/null @@ -1,196 +0,0 @@ -// deno-lint-ignore-file no-undef -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import "./global.ts"; -import { assert, assertEquals, assertThrows } from "../testing/asserts.ts"; -import { stripColor } from "../fmt/colors.ts"; -import * as path from "../path/mod.ts"; -import { delay } from "../async/delay.ts"; -import { env } from "./process.ts"; - -Deno.test({ - name: "process.cwd and process.chdir success", - fn() { - assertEquals(process.cwd(), Deno.cwd()); - - const currentDir = Deno.cwd(); - - const tempDir = Deno.makeTempDirSync(); - process.chdir(tempDir); - assertEquals( - Deno.realPathSync(process.cwd()), - Deno.realPathSync(tempDir), - ); - - process.chdir(currentDir); - }, -}); - -Deno.test({ - name: "process.chdir failure", - fn() { - assertThrows( - () => { - process.chdir("non-existent-directory-name"); - }, - Deno.errors.NotFound, - "file", - // On every OS Deno returns: "No such file" except for Windows, where it's: - // "The system cannot find the file specified. (os error 2)" so "file" is - // the only common string here. - ); - }, -}); - -Deno.test({ - name: "process.version", - fn() { - assertEquals(typeof process, "object"); - assertEquals(typeof process.version, "string"); - assertEquals(typeof process.versions, "object"); - assertEquals(typeof process.versions.node, "string"); - }, -}); - -Deno.test({ - name: "process.platform", - fn() { - assertEquals(typeof process.platform, "string"); - }, -}); - -Deno.test({ - name: "process.arch", - fn() { - assertEquals(typeof process.arch, "string"); - // TODO(rsp): make sure that the arch strings should be the same in Node and Deno: - assertEquals(process.arch, Deno.build.arch); - }, -}); - -Deno.test({ - name: "process.pid", - fn() { - assertEquals(typeof process.pid, "number"); - assertEquals(process.pid, Deno.pid); - }, -}); - -Deno.test({ - name: "process.on", - async fn() { - assertEquals(typeof process.on, "function"); - assertThrows( - () => { - process.on("uncaughtException", (_err: Error) => {}); - }, - Error, - "implemented", - ); - - let triggered = false; - process.on("exit", () => { - triggered = true; - }); - process.emit("exit"); - assert(triggered); - - const cwd = path.dirname(path.fromFileUrl(import.meta.url)); - - const p = Deno.run({ - cmd: [ - Deno.execPath(), - "run", - "./process_exit_test.ts", - ], - cwd, - stdout: "piped", - }); - - const decoder = new TextDecoder(); - const rawOutput = await p.output(); - assertEquals( - stripColor(decoder.decode(rawOutput).trim()), - "1\n2", - ); - p.close(); - }, -}); - -Deno.test({ - name: "process.argv", - fn() { - assert(Array.isArray(process.argv)); - assert( - process.argv[0].match(/[^/\\]*deno[^/\\]*$/), - "deno included in the file name of argv[0]", - ); - assertEquals( - process.argv[1], - path.fromFileUrl(Deno.mainModule), - ); - }, -}); - -Deno.test({ - name: "process.env", - fn() { - Deno.env.set("HELLO", "WORLD"); - - assertEquals(typeof (process.env.HELLO), "string"); - assertEquals(process.env.HELLO, "WORLD"); - - assertEquals(typeof env.HELLO, "string"); - assertEquals(env.HELLO, "WORLD"); - }, -}); - -Deno.test({ - name: "process.stdin", - fn() { - assertEquals(typeof process.stdin.fd, "number"); - assertEquals(process.stdin.fd, Deno.stdin.rid); - // TODO(jayhelton) Uncomment out this assertion once PTY is supported - //assert(process.stdin.isTTY); - }, -}); - -Deno.test({ - name: "process.stdout", - fn() { - assertEquals(typeof process.stdout.fd, "number"); - assertEquals(process.stdout.fd, Deno.stdout.rid); - // TODO(jayhelton) Uncomment out this assertion once PTY is supported - // assert(process.stdout.isTTY); - }, -}); - -Deno.test({ - name: "process.stderr", - fn() { - assertEquals(typeof process.stderr.fd, "number"); - assertEquals(process.stderr.fd, Deno.stderr.rid); - // TODO(jayhelton) Uncomment out this assertion once PTY is supported - // assert(process.stderr.isTTY); - }, -}); - -Deno.test({ - name: "process.nextTick", - async fn() { - let withoutArguments = false; - process.nextTick(() => { - withoutArguments = true; - }); - - const expected = 12; - let result; - process.nextTick((x: number) => { - result = x; - }, 12); - - await delay(10); - assert(withoutArguments); - assertEquals(result, expected); - }, -}); diff --git a/std/node/querystring.ts b/std/node/querystring.ts deleted file mode 100644 index 4d9d0eb276..0000000000 --- a/std/node/querystring.ts +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -interface ParseOptions { - /** The function to use when decoding percent-encoded characters in the query string. */ - decodeURIComponent?: (string: string) => string; - /** Specifies the maximum number of keys to parse. */ - maxKeys?: number; -} - -export const hexTable = new Array(256); -for (let i = 0; i < 256; ++i) { - hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase(); -} - -/** - * Parses a URL query string into a collection of key and value pairs. - * @param str The URL query string to parse - * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. - * @param eq The substring used to delimit keys and values in the query string. Default: '='. - * @param options The parse options - */ -export function parse( - str: string, - sep = "&", - eq = "=", - { decodeURIComponent = unescape, maxKeys = 1000 }: ParseOptions = {}, -): { [key: string]: string[] | string } { - const entries = str - .split(sep) - .map((entry) => entry.split(eq).map(decodeURIComponent)); - const final: { [key: string]: string[] | string } = {}; - - let i = 0; - while (true) { - if ((Object.keys(final).length === maxKeys && !!maxKeys) || !entries[i]) { - break; - } - - const [key, val] = entries[i]; - if (final[key]) { - if (Array.isArray(final[key])) { - (final[key] as string[]).push(val); - } else { - final[key] = [final[key] as string, val]; - } - } else { - final[key] = val; - } - - i++; - } - - return final; -} - -interface StringifyOptions { - /** The function to use when converting URL-unsafe characters to percent-encoding in the query string. */ - encodeURIComponent?: (string: string) => string; -} - -export function encodeStr( - str: string, - noEscapeTable: number[], - hexTable: string[], -): string { - const len = str.length; - if (len === 0) return ""; - - let out = ""; - let lastPos = 0; - - for (let i = 0; i < len; i++) { - let c = str.charCodeAt(i); - // ASCII - if (c < 0x80) { - if (noEscapeTable[c] === 1) continue; - if (lastPos < i) out += str.slice(lastPos, i); - lastPos = i + 1; - out += hexTable[c]; - continue; - } - - if (lastPos < i) out += str.slice(lastPos, i); - - // Multi-byte characters ... - if (c < 0x800) { - lastPos = i + 1; - out += hexTable[0xc0 | (c >> 6)] + hexTable[0x80 | (c & 0x3f)]; - continue; - } - if (c < 0xd800 || c >= 0xe000) { - lastPos = i + 1; - out += hexTable[0xe0 | (c >> 12)] + - hexTable[0x80 | ((c >> 6) & 0x3f)] + - hexTable[0x80 | (c & 0x3f)]; - continue; - } - // Surrogate pair - ++i; - - // This branch should never happen because all URLSearchParams entries - // should already be converted to USVString. But, included for - // completion's sake anyway. - if (i >= len) throw new Deno.errors.InvalidData("invalid URI"); - - const c2 = str.charCodeAt(i) & 0x3ff; - - lastPos = i + 1; - c = 0x10000 + (((c & 0x3ff) << 10) | c2); - out += hexTable[0xf0 | (c >> 18)] + - hexTable[0x80 | ((c >> 12) & 0x3f)] + - hexTable[0x80 | ((c >> 6) & 0x3f)] + - hexTable[0x80 | (c & 0x3f)]; - } - if (lastPos === 0) return str; - if (lastPos < len) return out + str.slice(lastPos); - return out; -} - -/** - * Produces a URL query string from a given obj by iterating through the object's "own properties". - * @param obj The object to serialize into a URL query string. - * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. - * @param eq The substring used to delimit keys and values in the query string. Default: '='. - * @param options The stringify options - */ -export function stringify( - // deno-lint-ignore no-explicit-any - obj: Record, - sep = "&", - eq = "=", - { encodeURIComponent = escape }: StringifyOptions = {}, -): string { - const final = []; - - for (const entry of Object.entries(obj)) { - if (Array.isArray(entry[1])) { - for (const val of entry[1]) { - final.push(encodeURIComponent(entry[0]) + eq + encodeURIComponent(val)); - } - } else if (typeof entry[1] !== "object" && entry[1] !== undefined) { - final.push(entry.map(encodeURIComponent).join(eq)); - } else { - final.push(encodeURIComponent(entry[0]) + eq); - } - } - - return final.join(sep); -} - -/** Alias of querystring.parse() */ -export const decode = parse; -/** Alias of querystring.stringify() */ -export const encode = stringify; -export const unescape = decodeURIComponent; -export const escape = encodeURIComponent; - -export default { - parse, - encodeStr, - stringify, - hexTable, - decode, - encode, - unescape, - escape, -}; diff --git a/std/node/querystring_test.ts b/std/node/querystring_test.ts deleted file mode 100644 index d05a75eabd..0000000000 --- a/std/node/querystring_test.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { parse, stringify } from "./querystring.ts"; - -Deno.test({ - name: "stringify", - fn() { - assertEquals( - stringify({ - a: "hello", - b: 5, - c: true, - d: ["foo", "bar"], - }), - "a=hello&b=5&c=true&d=foo&d=bar", - ); - }, -}); - -Deno.test({ - name: "parse", - fn() { - assertEquals(parse("a=hello&b=5&c=true&d=foo&d=bar"), { - a: "hello", - b: "5", - c: "true", - d: ["foo", "bar"], - }); - }, -}); diff --git a/std/node/stream.ts b/std/node/stream.ts deleted file mode 100644 index 230c5a9d61..0000000000 --- a/std/node/stream.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -import Duplex from "./_stream/duplex.ts"; -import eos from "./_stream/end_of_stream.ts"; -import PassThrough from "./_stream/passthrough.ts"; -import pipeline from "./_stream/pipeline.ts"; -import * as promises from "./_stream/promises.ts"; -import Readable from "./_stream/readable.ts"; -import Stream from "./_stream/stream.ts"; -import Transform from "./_stream/transform.ts"; -import Writable from "./_stream/writable.ts"; - -const exports = { - Duplex, - finished: eos, - PassThrough, - pipeline, - promises, - Readable, - Stream, - Transform, - Writable, -}; - -export default exports; -export { - Duplex, - eos as finished, - PassThrough, - pipeline, - promises, - Readable, - Stream, - Transform, - Writable, -}; diff --git a/std/node/stream_test.ts b/std/node/stream_test.ts deleted file mode 100644 index f8d4ecfa58..0000000000 --- a/std/node/stream_test.ts +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright Node.js contributors. All rights reserved. - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -import { Readable, Transform, Writable } from "./stream.ts"; -import { Buffer } from "./buffer.ts"; -import { deferred } from "../async/mod.ts"; -import { assert, assertEquals } from "../testing/asserts.ts"; -import { mustCall } from "./_utils.ts"; - -Deno.test("Readable and Writable stream backpressure test", async () => { - let pushes = 0; - const total = 65500 + 40 * 1024; - - let rsExecuted = 0; - const rsExecutedExpected = 11; - const rsExpectedExecutions = deferred(); - - let wsExecuted = 0; - const wsExecutedExpected = 410; - const wsExpectedExecutions = deferred(); - - const rs = new Readable({ - read: function () { - rsExecuted++; - if (rsExecuted == rsExecutedExpected) { - rsExpectedExecutions.resolve(); - } - - if (pushes++ === 10) { - this.push(null); - return; - } - - assert(this._readableState.length <= total); - - this.push(Buffer.alloc(65500)); - for (let i = 0; i < 40; i++) { - this.push(Buffer.alloc(1024)); - } - }, - }); - - const ws = new Writable({ - write: function (_data, _enc, cb) { - wsExecuted++; - if (wsExecuted == wsExecutedExpected) { - wsExpectedExecutions.resolve(); - } - cb(); - }, - }); - - rs.pipe(ws); - - const rsTimeout = setTimeout(() => rsExpectedExecutions.reject(), 1000); - const wsTimeout = setTimeout(() => wsExpectedExecutions.reject(), 1000); - await rsExpectedExecutions; - await wsExpectedExecutions; - clearTimeout(rsTimeout); - clearTimeout(wsTimeout); - assertEquals(rsExecuted, rsExecutedExpected); - assertEquals(wsExecuted, wsExecutedExpected); -}); - -Deno.test("Readable can be piped through Transform", async () => { - const [readExecution, readCb] = mustCall(function (this: Readable) { - this.push("content"); - this.push(null); - }); - - const r = new Readable({ - read: readCb, - }); - - const [transformExecution, transformCb] = mustCall( - function ( - this: Transform, - chunk: unknown, - _e, - callback: (error?: Error | null) => void, - ) { - this.push(chunk); - callback(); - }, - ); - - const [flushExecution, flushCb] = mustCall( - function (this: Transform, callback: (error?: Error | null) => void) { - callback(); - }, - ); - - const t = new Transform({ - transform: transformCb, - flush: flushCb, - }); - - r.pipe(t); - - const [readableExecution, readableCb] = mustCall(function () { - while (true) { - const chunk = t.read(); - if (!chunk) { - break; - } - - assertEquals(chunk.toString(), "content"); - } - }, 2); - - t.on("readable", readableCb); - - await readExecution; - await transformExecution; - await flushExecution; - await readableExecution; -}); diff --git a/std/node/string_decoder.ts b/std/node/string_decoder.ts deleted file mode 100644 index d5aba80181..0000000000 --- a/std/node/string_decoder.ts +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -import { Buffer } from "./buffer.ts"; -import { normalizeEncoding as castEncoding, notImplemented } from "./_utils.ts"; - -enum NotImplemented { - "ascii", - "latin1", - "utf16le", -} - -function normalizeEncoding(enc?: string): string { - const encoding = castEncoding(enc ?? null); - if (encoding && encoding in NotImplemented) notImplemented(encoding); - if (!encoding && typeof enc === "string" && enc.toLowerCase() !== "raw") { - throw new Error(`Unknown encoding: ${enc}`); - } - return String(encoding); -} -/* - * Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a - * continuation byte. If an invalid byte is detected, -2 is returned. - * */ -function utf8CheckByte(byte: number): number { - if (byte <= 0x7f) return 0; - else if (byte >> 5 === 0x06) return 2; - else if (byte >> 4 === 0x0e) return 3; - else if (byte >> 3 === 0x1e) return 4; - return byte >> 6 === 0x02 ? -1 : -2; -} - -/* - * Checks at most 3 bytes at the end of a Buffer in order to detect an - * incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) - * needed to complete the UTF-8 character (if applicable) are returned. - * */ -function utf8CheckIncomplete( - self: StringDecoderBase, - buf: Buffer, - i: number, -): number { - let j = buf.length - 1; - if (j < i) return 0; - let nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 1; - return nb; - } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) self.lastNeed = nb - 2; - return nb; - } - if (--j < i || nb === -2) return 0; - nb = utf8CheckByte(buf[j]); - if (nb >= 0) { - if (nb > 0) { - if (nb === 2) nb = 0; - else self.lastNeed = nb - 3; - } - return nb; - } - return 0; -} - -/* - * Validates as many continuation bytes for a multi-byte UTF-8 character as - * needed or are available. If we see a non-continuation byte where we expect - * one, we "replace" the validated continuation bytes we've seen so far with - * a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding - * behavior. The continuation byte check is included three times in the case - * where all of the continuation bytes for a character exist in the same buffer. - * It is also done this way as a slight performance increase instead of using a - * loop. - * */ -function utf8CheckExtraBytes( - self: StringDecoderBase, - buf: Buffer, -): string | undefined { - if ((buf[0] & 0xc0) !== 0x80) { - self.lastNeed = 0; - return "\ufffd"; - } - if (self.lastNeed > 1 && buf.length > 1) { - if ((buf[1] & 0xc0) !== 0x80) { - self.lastNeed = 1; - return "\ufffd"; - } - if (self.lastNeed > 2 && buf.length > 2) { - if ((buf[2] & 0xc0) !== 0x80) { - self.lastNeed = 2; - return "\ufffd"; - } - } - } -} - -/* - * Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. - * */ -function utf8FillLastComplete( - this: StringDecoderBase, - buf: Buffer, -): string | undefined { - const p = this.lastTotal - this.lastNeed; - const r = utf8CheckExtraBytes(this, buf); - if (r !== undefined) return r; - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, p, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, p, 0, buf.length); - this.lastNeed -= buf.length; -} - -/* - * Attempts to complete a partial non-UTF-8 character using bytes from a Buffer - * */ -function utf8FillLastIncomplete( - this: StringDecoderBase, - buf: Buffer, -): string | undefined { - if (this.lastNeed <= buf.length) { - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); - return this.lastChar.toString(this.encoding, 0, this.lastTotal); - } - buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); - this.lastNeed -= buf.length; -} - -/* - * Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a - * partial character, the character's bytes are buffered until the required - * number of bytes are available. - * */ -function utf8Text(this: StringDecoderBase, buf: Buffer, i: number): string { - const total = utf8CheckIncomplete(this, buf, i); - if (!this.lastNeed) return buf.toString("utf8", i); - this.lastTotal = total; - const end = buf.length - (total - this.lastNeed); - buf.copy(this.lastChar, 0, end); - return buf.toString("utf8", i, end); -} - -/* - * For UTF-8, a replacement character is added when ending on a partial - * character. - * */ -function utf8End(this: Utf8Decoder, buf?: Buffer): string { - const r = buf && buf.length ? this.write(buf) : ""; - if (this.lastNeed) return r + "\ufffd"; - return r; -} - -function utf8Write( - this: Utf8Decoder | Base64Decoder, - buf: Buffer | string, -): string { - if (typeof buf === "string") { - return buf; - } - if (buf.length === 0) return ""; - let r; - let i; - if (this.lastNeed) { - r = this.fillLast(buf); - if (r === undefined) return ""; - i = this.lastNeed; - this.lastNeed = 0; - } else { - i = 0; - } - if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i); - return r || ""; -} - -function base64Text(this: StringDecoderBase, buf: Buffer, i: number): string { - const n = (buf.length - i) % 3; - if (n === 0) return buf.toString("base64", i); - this.lastNeed = 3 - n; - this.lastTotal = 3; - if (n === 1) { - this.lastChar[0] = buf[buf.length - 1]; - } else { - this.lastChar[0] = buf[buf.length - 2]; - this.lastChar[1] = buf[buf.length - 1]; - } - return buf.toString("base64", i, buf.length - n); -} - -function base64End(this: Base64Decoder, buf?: Buffer): string { - const r = buf && buf.length ? this.write(buf) : ""; - if (this.lastNeed) { - return r + this.lastChar.toString("base64", 0, 3 - this.lastNeed); - } - return r; -} - -function simpleWrite( - this: StringDecoderBase, - buf: Buffer | string, -): string { - if (typeof buf === "string") { - return buf; - } - return buf.toString(this.encoding); -} - -function simpleEnd(this: GenericDecoder, buf?: Buffer): string { - return buf && buf.length ? this.write(buf) : ""; -} - -class StringDecoderBase { - public lastChar: Buffer; - public lastNeed = 0; - public lastTotal = 0; - constructor(public encoding: string, nb: number) { - this.lastChar = Buffer.allocUnsafe(nb); - } -} - -class Base64Decoder extends StringDecoderBase { - public end = base64End; - public fillLast = utf8FillLastIncomplete; - public text = base64Text; - public write = utf8Write; - - constructor(encoding?: string) { - super(normalizeEncoding(encoding), 3); - } -} - -class GenericDecoder extends StringDecoderBase { - public end = simpleEnd; - public fillLast = undefined; - public text = utf8Text; - public write = simpleWrite; - - constructor(encoding?: string) { - super(normalizeEncoding(encoding), 4); - } -} - -class Utf8Decoder extends StringDecoderBase { - public end = utf8End; - public fillLast = utf8FillLastComplete; - public text = utf8Text; - public write = utf8Write; - - constructor(encoding?: string) { - super(normalizeEncoding(encoding), 4); - } -} - -/* - * StringDecoder provides an interface for efficiently splitting a series of - * buffers into a series of JS strings without breaking apart multi-byte - * characters. - * */ -export class StringDecoder { - public encoding: string; - public end: (buf?: Buffer) => string; - public fillLast: ((buf: Buffer) => string | undefined) | undefined; - public lastChar: Buffer; - public lastNeed: number; - public lastTotal: number; - public text: (buf: Buffer, n: number) => string; - public write: (buf: Buffer) => string; - - constructor(encoding?: string) { - let decoder; - switch (encoding) { - case "utf8": - decoder = new Utf8Decoder(encoding); - break; - case "base64": - decoder = new Base64Decoder(encoding); - break; - default: - decoder = new GenericDecoder(encoding); - } - this.encoding = decoder.encoding; - this.end = decoder.end; - this.fillLast = decoder.fillLast; - this.lastChar = decoder.lastChar; - this.lastNeed = decoder.lastNeed; - this.lastTotal = decoder.lastTotal; - this.text = decoder.text; - this.write = decoder.write; - } -} - -export default { StringDecoder }; diff --git a/std/node/string_decoder_test.ts b/std/node/string_decoder_test.ts deleted file mode 100644 index 70f50820da..0000000000 --- a/std/node/string_decoder_test.ts +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { Buffer } from "./buffer.ts"; -import { StringDecoder } from "./string_decoder.ts"; - -Deno.test({ - name: "String decoder is encoding utf8 correctly", - fn() { - let decoder; - - decoder = new StringDecoder("utf8"); - assertEquals(decoder.write(Buffer.from("E1", "hex")), ""); - assertEquals(decoder.end(), "\ufffd"); - - decoder = new StringDecoder("utf8"); - assertEquals(decoder.write(Buffer.from("E18B", "hex")), ""); - assertEquals(decoder.end(), "\ufffd"); - - decoder = new StringDecoder("utf8"); - assertEquals(decoder.write(Buffer.from("\ufffd")), "\ufffd"); - assertEquals(decoder.end(), ""); - - decoder = new StringDecoder("utf8"); - assertEquals( - decoder.write(Buffer.from("\ufffd\ufffd\ufffd")), - "\ufffd\ufffd\ufffd", - ); - assertEquals(decoder.end(), ""); - - decoder = new StringDecoder("utf8"); - assertEquals(decoder.write(Buffer.from("EFBFBDE2", "hex")), "\ufffd"); - assertEquals(decoder.end(), "\ufffd"); - - decoder = new StringDecoder("utf8"); - assertEquals(decoder.write(Buffer.from("F1", "hex")), ""); - assertEquals(decoder.write(Buffer.from("41F2", "hex")), "\ufffdA"); - assertEquals(decoder.end(), "\ufffd"); - - decoder = new StringDecoder("utf8"); - assertEquals(decoder.text(Buffer.from([0x41]), 2), ""); - }, -}); - -Deno.test({ - name: "String decoder is encoding base64 correctly", - fn() { - let decoder; - - decoder = new StringDecoder("base64"); - assertEquals(decoder.write(Buffer.from("E1", "hex")), "4Q=="); - assertEquals(decoder.end(), "4QAA"); - - decoder = new StringDecoder("base64"); - assertEquals(decoder.write(Buffer.from("E18B", "hex")), "4Ys="); - assertEquals(decoder.end(), "4YsA"); - - decoder = new StringDecoder("base64"); - assertEquals(decoder.write(Buffer.from("\ufffd")), "77+9"); - assertEquals(decoder.end(), ""); - - decoder = new StringDecoder("base64"); - assertEquals( - decoder.write(Buffer.from("\ufffd\ufffd\ufffd")), - "77+977+977+9", - ); - assertEquals(decoder.end(), ""); - - decoder = new StringDecoder("base64"); - assertEquals(decoder.write(Buffer.from("EFBFBDE2", "hex")), "77+94g=="); - assertEquals(decoder.end(), "4gAA"); - - decoder = new StringDecoder("base64"); - assertEquals(decoder.write(Buffer.from("F1", "hex")), "8Q=="); - assertEquals(decoder.write(Buffer.from("41F2", "hex")), "8UHy"); - assertEquals(decoder.end(), ""); - - decoder = new StringDecoder("base64"); - assertEquals(decoder.text(Buffer.from([0x41]), 2), "QQ=="); - }, -}); - -Deno.test({ - name: "String decoder is encoding hex correctly", - fn() { - let decoder; - - decoder = new StringDecoder("hex"); - assertEquals(decoder.write(Buffer.from("E1", "hex")), "e1"); - assertEquals(decoder.end(), ""); - - decoder = new StringDecoder("hex"); - assertEquals(decoder.write(Buffer.from("E18B", "hex")), "e18b"); - assertEquals(decoder.end(), ""); - - decoder = new StringDecoder("hex"); - assertEquals(decoder.write(Buffer.from("\ufffd")), "efbfbd"); - assertEquals(decoder.end(), ""); - - decoder = new StringDecoder("hex"); - assertEquals( - decoder.write(Buffer.from("\ufffd\ufffd\ufffd")), - "efbfbdefbfbdefbfbd", - ); - assertEquals(decoder.end(), ""); - - decoder = new StringDecoder("hex"); - assertEquals(decoder.write(Buffer.from("EFBFBDE2", "hex")), "efbfbde2"); - assertEquals(decoder.end(), ""); - - decoder = new StringDecoder("hex"); - assertEquals(decoder.write(Buffer.from("F1", "hex")), "f1"); - assertEquals(decoder.write(Buffer.from("41F2", "hex")), "41f2"); - assertEquals(decoder.end(), ""); - - decoder = new StringDecoder("hex"); - assertEquals(decoder.text(Buffer.from([0x41]), 2), ""); - }, -}); diff --git a/std/node/tests/cjs/cjs_a.js b/std/node/tests/cjs/cjs_a.js deleted file mode 100644 index b2dae2b6b0..0000000000 --- a/std/node/tests/cjs/cjs_a.js +++ /dev/null @@ -1,11 +0,0 @@ -// deno-lint-ignore-file no-undef -// deno-lint-ignore-file -const { helloB } = require("./cjs_b.js"); -const C = require("./subdir/cjs_c"); -const leftPad = require("left-pad"); - -function helloA() { - return "A"; -} - -module.exports = { helloA, helloB, C, leftPad }; diff --git a/std/node/tests/cjs/cjs_b.js b/std/node/tests/cjs/cjs_b.js deleted file mode 100644 index 2bbf75809f..0000000000 --- a/std/node/tests/cjs/cjs_b.js +++ /dev/null @@ -1,6 +0,0 @@ -function helloB() { - return "B"; -} - -// deno-lint-ignore no-undef -module.exports = { helloB }; diff --git a/std/node/tests/cjs/cjs_builtin.js b/std/node/tests/cjs/cjs_builtin.js deleted file mode 100644 index 3a971f6055..0000000000 --- a/std/node/tests/cjs/cjs_builtin.js +++ /dev/null @@ -1,11 +0,0 @@ -// deno-lint-ignore-file no-undef -// deno-lint-ignore-file -const fs = require("fs"); -const util = require("util"); -const path = require("path"); - -module.exports = { - readFileSync: fs.readFileSync, - isNull: util.isNull, - extname: path.extname, -}; diff --git a/std/node/tests/cjs/cjs_cycle_a.js b/std/node/tests/cjs/cjs_cycle_a.js deleted file mode 100644 index d73da20b90..0000000000 --- a/std/node/tests/cjs/cjs_cycle_a.js +++ /dev/null @@ -1,4 +0,0 @@ -// deno-lint-ignore-file no-undef -module.exports = false; -require("./cjs_cycle_a"); -module.exports = true; diff --git a/std/node/tests/cjs/cjs_cycle_b.js b/std/node/tests/cjs/cjs_cycle_b.js deleted file mode 100644 index bd18094ce9..0000000000 --- a/std/node/tests/cjs/cjs_cycle_b.js +++ /dev/null @@ -1,4 +0,0 @@ -// deno-lint-ignore-file no-undef -module.exports = false; -require("./cjs_cycle_b"); -module.exports = true; diff --git a/std/node/tests/cjs/cjs_throw.js b/std/node/tests/cjs/cjs_throw.js deleted file mode 100644 index 3def8cc852..0000000000 --- a/std/node/tests/cjs/cjs_throw.js +++ /dev/null @@ -1,6 +0,0 @@ -function hello() { - throw new Error("bye"); -} - -// deno-lint-ignore no-undef -module.exports = { hello }; diff --git a/std/node/tests/cjs/dir b/std/node/tests/cjs/dir deleted file mode 120000 index abec30059c..0000000000 --- a/std/node/tests/cjs/dir +++ /dev/null @@ -1 +0,0 @@ -./subdir/dir \ No newline at end of file diff --git a/std/node/tests/cjs/index.js b/std/node/tests/cjs/index.js deleted file mode 100644 index d55928a892..0000000000 --- a/std/node/tests/cjs/index.js +++ /dev/null @@ -1,2 +0,0 @@ -// deno-lint-ignore no-undef -module.exports = { isIndex: true }; diff --git a/std/node/tests/cjs/subdir/cjs_c.js b/std/node/tests/cjs/subdir/cjs_c.js deleted file mode 100644 index b24caa0c4e..0000000000 --- a/std/node/tests/cjs/subdir/cjs_c.js +++ /dev/null @@ -1,2 +0,0 @@ -// deno-lint-ignore no-undef -module.exports = "C"; diff --git a/std/node/tests/cjs/subdir/dir/index.js b/std/node/tests/cjs/subdir/dir/index.js deleted file mode 100644 index 0b4d113868..0000000000 --- a/std/node/tests/cjs/subdir/dir/index.js +++ /dev/null @@ -1,4 +0,0 @@ -// deno-lint-ignore-file no-undef -const C = require("../cjs_c"); - -module.exports = { C }; diff --git a/std/node/tests/node_modules/left-pad/README.md b/std/node/tests/node_modules/left-pad/README.md deleted file mode 100644 index e86ca7cc59..0000000000 --- a/std/node/tests/node_modules/left-pad/README.md +++ /dev/null @@ -1,36 +0,0 @@ -## left-pad - -String left pad - -[![Build Status][travis-image]][travis-url] - -## Install - -```bash -$ npm install left-pad -``` - -## Usage - -```js -const leftPad = require('left-pad') - -leftPad('foo', 5) -// => " foo" - -leftPad('foobar', 6) -// => "foobar" - -leftPad(1, 2, '0') -// => "01" - -leftPad(17, 5, 0) -// => "00017" -``` - -**NOTE:** The third argument should be a single `char`. However the module doesn't throw an error if you supply more than one `char`s. See [#28](https://github.com/stevemao/left-pad/pull/28). - -**NOTE:** Characters having code points outside of [BMP plan](https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane) are considered a two distinct characters. See [#58](https://github.com/stevemao/left-pad/issues/58). - -[travis-image]: https://travis-ci.org/stevemao/left-pad.svg?branch=master -[travis-url]: https://travis-ci.org/stevemao/left-pad diff --git a/std/node/tests/node_modules/left-pad/index.js b/std/node/tests/node_modules/left-pad/index.js deleted file mode 100644 index 8501bca1b6..0000000000 --- a/std/node/tests/node_modules/left-pad/index.js +++ /dev/null @@ -1,52 +0,0 @@ -/* This program is free software. It comes without any warranty, to - * the extent permitted by applicable law. You can redistribute it - * and/or modify it under the terms of the Do What The Fuck You Want - * To Public License, Version 2, as published by Sam Hocevar. See - * http://www.wtfpl.net/ for more details. */ -"use strict"; -module.exports = leftPad; - -var cache = [ - "", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " ", - " " -]; - -function leftPad(str, len, ch) { - // convert `str` to a `string` - str = str + ""; - // `len` is the `pad`'s length now - len = len - str.length; - // doesn't need to pad - if (len <= 0) return str; - // `ch` defaults to `' '` - if (!ch && ch !== 0) ch = " "; - // convert `ch` to a `string` cuz it could be a number - ch = ch + ""; - // cache common use cases - if (ch === " " && len < 10) return cache[len] + str; - // `pad` starts with an empty string - var pad = ""; - // loop - while (true) { - // add `ch` to `pad` if `len` is odd - if (len & 1) pad += ch; - // divide `len` by 2, ditch the remainder - len >>= 1; - // "double" the `ch` so this operation count grows logarithmically on `len` - // each time `ch` is "doubled", the `len` would need to be "doubled" too - // similar to finding a value in binary search tree, hence O(log(n)) - if (len) ch += ch; - // `len` is 0, exit the loop - else break; - } - // pad `str`! - return pad + str; -} diff --git a/std/node/tests/node_modules/left-pad/package.json b/std/node/tests/node_modules/left-pad/package.json deleted file mode 100644 index 57be042717..0000000000 --- a/std/node/tests/node_modules/left-pad/package.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "_from": "left-pad", - "_id": "left-pad@1.3.0", - "_inBundle": false, - "_integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "_location": "/left-pad", - "_phantomChildren": {}, - "_requested": { - "type": "tag", - "registry": true, - "raw": "left-pad", - "name": "left-pad", - "escapedName": "left-pad", - "rawSpec": "", - "saveSpec": null, - "fetchSpec": "latest" - }, - "_requiredBy": [ - "#USER", - "/" - ], - "_resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "_shasum": "5b8a3a7765dfe001261dde915589e782f8c94d1e", - "_spec": "left-pad", - "_where": "/Users/kun/Projects/Deno/deno/std/node/tests", - "author": { - "name": "azer" - }, - "bugs": { - "url": "https://github.com/stevemao/left-pad/issues" - }, - "bundleDependencies": false, - "deprecated": "use String.prototype.padStart()", - "description": "String left pad", - "devDependencies": { - "benchmark": "^2.1.0", - "fast-check": "0.0.8", - "tape": "*" - }, - "homepage": "https://github.com/stevemao/left-pad#readme", - "keywords": [ - "leftpad", - "left", - "pad", - "padding", - "string", - "repeat" - ], - "license": "WTFPL", - "main": "index.js", - "maintainers": [ - { - "name": "Cameron Westland", - "email": "camwest@gmail.com" - } - ], - "name": "left-pad", - "repository": { - "url": "git+ssh://git@github.com/stevemao/left-pad.git", - "type": "git" - }, - "scripts": { - "bench": "node perf/perf.js", - "test": "node test" - }, - "types": "index.d.ts", - "version": "1.3.0" -} diff --git a/std/node/tests/package.json b/std/node/tests/package.json deleted file mode 100644 index 08c54d5884..0000000000 --- a/std/node/tests/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "deno_std_node", - "version": "0.0.1", - "description": "", - "main": "index.js", - "dependencies": { - "left-pad": "^1.3.0" - }, - "devDependencies": {}, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC" -} diff --git a/std/node/timers.ts b/std/node/timers.ts deleted file mode 100644 index e81f7e76b4..0000000000 --- a/std/node/timers.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// TODO(bartlomieju): implement the 'NodeJS.Timeout' and 'NodeJS.Immediate' versions of the timers. -// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/1163ead296d84e7a3c80d71e7c81ecbd1a130e9a/types/node/v12/globals.d.ts#L1120-L1131 -export const setTimeout = globalThis.setTimeout; -export const clearTimeout = globalThis.clearTimeout; -export const setInterval = globalThis.setInterval; -export const clearInterval = globalThis.clearInterval; -export const setImmediate = ( - // deno-lint-ignore no-explicit-any - cb: (...args: any[]) => void, - // deno-lint-ignore no-explicit-any - ...args: any[] -): number => globalThis.setTimeout(cb, 0, ...args); -export const clearImmediate = globalThis.clearTimeout; - -export default { - setTimeout, - clearTimeout, - setInterval, - clearInterval, - setImmediate, - clearImmediate, -}; diff --git a/std/node/url.ts b/std/node/url.ts deleted file mode 100644 index 9d2fbe7578..0000000000 --- a/std/node/url.ts +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -import { - CHAR_BACKWARD_SLASH, - CHAR_FORWARD_SLASH, - CHAR_LOWERCASE_A, - CHAR_LOWERCASE_Z, -} from "../path/_constants.ts"; -import * as path from "./path.ts"; -import { isWindows } from "../_util/os.ts"; - -const forwardSlashRegEx = /\//g; -const percentRegEx = /%/g; -const backslashRegEx = /\\/g; -const newlineRegEx = /\n/g; -const carriageReturnRegEx = /\r/g; -const tabRegEx = /\t/g; - -const _url = URL; -export { _url as URL }; - -/** - * Get fully resolved platform-specific file path from the given URL string/ object - * @param path The file URL string or URL object to convert to a path - */ -export function fileURLToPath(path: string | URL): string { - if (typeof path === "string") path = new URL(path); - else if (!(path instanceof URL)) { - throw new Deno.errors.InvalidData( - "invalid argument path , must be a string or URL", - ); - } - if (path.protocol !== "file:") { - throw new Deno.errors.InvalidData("invalid url scheme"); - } - return isWindows ? getPathFromURLWin(path) : getPathFromURLPosix(path); -} - -function getPathFromURLWin(url: URL): string { - const hostname = url.hostname; - let pathname = url.pathname; - for (let n = 0; n < pathname.length; n++) { - if (pathname[n] === "%") { - const third = pathname.codePointAt(n + 2) || 0x20; - if ( - (pathname[n + 1] === "2" && third === 102) || // 2f 2F / - (pathname[n + 1] === "5" && third === 99) - ) { - // 5c 5C \ - throw new Deno.errors.InvalidData( - "must not include encoded \\ or / characters", - ); - } - } - } - - pathname = pathname.replace(forwardSlashRegEx, "\\"); - pathname = decodeURIComponent(pathname); - if (hostname !== "") { - // TODO(bartlomieju): add support for punycode encodings - return `\\\\${hostname}${pathname}`; - } else { - // Otherwise, it's a local path that requires a drive letter - const letter = pathname.codePointAt(1)! | 0x20; - const sep = pathname[2]; - if ( - letter < CHAR_LOWERCASE_A || - letter > CHAR_LOWERCASE_Z || // a..z A..Z - sep !== ":" - ) { - throw new Deno.errors.InvalidData("file url path must be absolute"); - } - return pathname.slice(1); - } -} - -function getPathFromURLPosix(url: URL): string { - if (url.hostname !== "") { - throw new Deno.errors.InvalidData("invalid file url hostname"); - } - const pathname = url.pathname; - for (let n = 0; n < pathname.length; n++) { - if (pathname[n] === "%") { - const third = pathname.codePointAt(n + 2) || 0x20; - if (pathname[n + 1] === "2" && third === 102) { - throw new Deno.errors.InvalidData( - "must not include encoded / characters", - ); - } - } - } - return decodeURIComponent(pathname); -} - -/** Get fully resolved platform-specific File URL from the given file path */ -export function pathToFileURL(filepath: string): URL { - let resolved = path.resolve(filepath); - // path.resolve strips trailing slashes so we must add them back - const filePathLast = filepath.charCodeAt(filepath.length - 1); - if ( - (filePathLast === CHAR_FORWARD_SLASH || - (isWindows && filePathLast === CHAR_BACKWARD_SLASH)) && - resolved[resolved.length - 1] !== path.sep - ) { - resolved += "/"; - } - const outURL = new URL("file://"); - if (resolved.includes("%")) resolved = resolved.replace(percentRegEx, "%25"); - // In posix, "/" is a valid character in paths - if (!isWindows && resolved.includes("\\")) { - resolved = resolved.replace(backslashRegEx, "%5C"); - } - if (resolved.includes("\n")) resolved = resolved.replace(newlineRegEx, "%0A"); - if (resolved.includes("\r")) { - resolved = resolved.replace(carriageReturnRegEx, "%0D"); - } - if (resolved.includes("\t")) resolved = resolved.replace(tabRegEx, "%09"); - outURL.pathname = resolved; - return outURL; -} - -export default { - fileURLToPath, - pathToFileURL, - URL, -}; diff --git a/std/node/url_test.ts b/std/node/url_test.ts deleted file mode 100644 index 7da8e3bb4e..0000000000 --- a/std/node/url_test.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import * as url from "./url.ts"; - -Deno.test({ - name: "[url] URL", - fn() { - assertEquals(url.URL, URL); - }, -}); diff --git a/std/node/util.ts b/std/node/util.ts deleted file mode 100644 index cbc1e11c5c..0000000000 --- a/std/node/util.ts +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -export { promisify } from "./_util/_util_promisify.ts"; -export { callbackify } from "./_util/_util_callbackify.ts"; -import { ERR_INVALID_ARG_TYPE, ERR_OUT_OF_RANGE, errorMap } from "./_errors.ts"; -import * as types from "./_util/_util_types.ts"; -export { types }; - -const NumberIsSafeInteger = Number.isSafeInteger; - -const DEFAULT_INSPECT_OPTIONS = { - showHidden: false, - depth: 2, - colors: false, - customInspect: true, - showProxy: false, - maxArrayLength: 100, - maxStringLength: Infinity, - breakLength: 80, - compact: 3, - sorted: false, - getters: false, -}; - -inspect.defaultOptions = DEFAULT_INSPECT_OPTIONS; -inspect.custom = Deno.customInspect; - -// TODO(schwarzkopfb): make it in-line with Node's implementation -// Ref: https://nodejs.org/dist/latest-v14.x/docs/api/util.html#util_util_inspect_object_options -// deno-lint-ignore no-explicit-any -export function inspect(object: unknown, ...opts: any): string { - opts = { ...DEFAULT_INSPECT_OPTIONS, ...opts }; - return Deno.inspect(object, { - depth: opts.depth, - iterableLimit: opts.maxArrayLength, - compact: !!opts.compact, - sorted: !!opts.sorted, - showProxy: !!opts.showProxy, - }); -} - -/** @deprecated - use `Array.isArray()` instead. */ -export function isArray(value: unknown): boolean { - return Array.isArray(value); -} - -/** @deprecated - use `typeof value === "boolean" || value instanceof Boolean` instead. */ -export function isBoolean(value: unknown): boolean { - return typeof value === "boolean" || value instanceof Boolean; -} - -/** @deprecated - use `value === null` instead. */ -export function isNull(value: unknown): boolean { - return value === null; -} - -/** @deprecated - use `value === null || value === undefined` instead. */ -export function isNullOrUndefined(value: unknown): boolean { - return value === null || value === undefined; -} - -/** @deprecated - use `typeof value === "number" || value instanceof Number` instead. */ -export function isNumber(value: unknown): boolean { - return typeof value === "number" || value instanceof Number; -} - -/** @deprecated - use `typeof value === "string" || value instanceof String` instead. */ -export function isString(value: unknown): boolean { - return typeof value === "string" || value instanceof String; -} - -/** @deprecated - use `typeof value === "symbol"` instead. */ -export function isSymbol(value: unknown): boolean { - return typeof value === "symbol"; -} - -/** @deprecated - use `value === undefined` instead. */ -export function isUndefined(value: unknown): boolean { - return value === undefined; -} - -/** @deprecated - use `value !== null && typeof value === "object"` instead. */ -export function isObject(value: unknown): boolean { - return value !== null && typeof value === "object"; -} - -/** @deprecated - use `e instanceof Error` instead. */ -export function isError(e: unknown): boolean { - return e instanceof Error; -} - -/** @deprecated - use `typeof value === "function"` instead. */ -export function isFunction(value: unknown): boolean { - return typeof value === "function"; -} - -/** @deprecated - use `value instanceof RegExp` instead. */ -export function isRegExp(value: unknown): boolean { - return value instanceof RegExp; -} - -/** @deprecated - use `value === null || (typeof value !== "object" && typeof value !== "function")` instead. */ -export function isPrimitive(value: unknown): boolean { - return ( - value === null || (typeof value !== "object" && typeof value !== "function") - ); -} - -/** - * Returns a system error name from an error code number. - * @param code error code number - */ -export function getSystemErrorName(code: number): string | undefined { - if (typeof code !== "number") { - throw new ERR_INVALID_ARG_TYPE("err", "number", code); - } - if (code >= 0 || !NumberIsSafeInteger(code)) { - throw new ERR_OUT_OF_RANGE("err", "a negative integer", code); - } - return errorMap.get(code)?.[0]; -} - -/** - * https://nodejs.org/api/util.html#util_util_deprecate_fn_msg_code - * @param _code This implementation of deprecate won't apply the deprecation code - */ -export function deprecate, B>( - this: unknown, - callback: (...args: A) => B, - msg: string, - _code?: string, -) { - return function (this: unknown, ...args: A) { - console.warn(msg); - return callback.apply(this, args); - }; -} - -import { _TextDecoder, _TextEncoder } from "./_utils.ts"; - -/** The global TextDecoder */ -export type TextDecoder = import("./_utils.ts")._TextDecoder; -export const TextDecoder = _TextDecoder; - -/** The global TextEncoder */ -export type TextEncoder = import("./_utils.ts")._TextEncoder; -export const TextEncoder = _TextEncoder; - -export default { - inspect, - isArray, - isBoolean, - isNull, - isNullOrUndefined, - isNumber, - isString, - isSymbol, - isUndefined, - isObject, - isError, - isFunction, - isRegExp, - isPrimitive, - getSystemErrorName, - deprecate, - TextDecoder, - TextEncoder, -}; diff --git a/std/node/util_test.ts b/std/node/util_test.ts deleted file mode 100644 index 893994559a..0000000000 --- a/std/node/util_test.ts +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { - assert, - assertEquals, - assertStrictEquals, - assertThrows, -} from "../testing/asserts.ts"; -import { stripColor } from "../fmt/colors.ts"; -import * as util from "./util.ts"; - -Deno.test({ - name: "[util] inspect", - fn() { - assertEquals(stripColor(util.inspect({ foo: 123 })), "{ foo: 123 }"); - }, -}); - -Deno.test({ - name: "[util] isBoolean", - fn() { - assert(util.isBoolean(true)); - assert(util.isBoolean(new Boolean())); - assert(util.isBoolean(new Boolean(true))); - assert(util.isBoolean(false)); - assert(!util.isBoolean("deno")); - assert(!util.isBoolean("true")); - }, -}); - -Deno.test({ - name: "[util] isNull", - fn() { - let n; - assert(util.isNull(null)); - assert(!util.isNull(n)); - assert(!util.isNull(0)); - assert(!util.isNull({})); - }, -}); - -Deno.test({ - name: "[util] isNullOrUndefined", - fn() { - let n; - assert(util.isNullOrUndefined(null)); - assert(util.isNullOrUndefined(n)); - assert(!util.isNullOrUndefined({})); - assert(!util.isNullOrUndefined("undefined")); - }, -}); - -Deno.test({ - name: "[util] isNumber", - fn() { - assert(util.isNumber(666)); - assert(util.isNumber(new Number(666))); - assert(!util.isNumber("999")); - assert(!util.isNumber(null)); - }, -}); - -Deno.test({ - name: "[util] isString", - fn() { - assert(util.isString("deno")); - assert(util.isString(new String("DIO"))); - assert(!util.isString(1337)); - }, -}); - -Deno.test({ - name: "[util] isSymbol", - fn() { - assert(util.isSymbol(Symbol())); - assert(!util.isSymbol(123)); - assert(!util.isSymbol("string")); - }, -}); - -Deno.test({ - name: "[util] isUndefined", - fn() { - let t; - assert(util.isUndefined(t)); - assert(!util.isUndefined("undefined")); - assert(!util.isUndefined({})); - }, -}); - -Deno.test({ - name: "[util] isObject", - fn() { - const dio = { stand: "Za Warudo" }; - assert(util.isObject(dio)); - assert(util.isObject(new RegExp(/Toki Wo Tomare/))); - assert(!util.isObject("Jotaro")); - }, -}); - -Deno.test({ - name: "[util] isError", - fn() { - const java = new Error(); - const nodejs = new TypeError(); - const deno = "Future"; - assert(util.isError(java)); - assert(util.isError(nodejs)); - assert(!util.isError(deno)); - }, -}); - -Deno.test({ - name: "[util] isFunction", - fn() { - const f = function (): void {}; - assert(util.isFunction(f)); - assert(!util.isFunction({})); - assert(!util.isFunction(new RegExp(/f/))); - }, -}); - -Deno.test({ - name: "[util] isRegExp", - fn() { - assert(util.isRegExp(new RegExp(/f/))); - assert(util.isRegExp(/fuManchu/)); - assert(!util.isRegExp({ evil: "eye" })); - assert(!util.isRegExp(null)); - }, -}); - -Deno.test({ - name: "[util] isArray", - fn() { - assert(util.isArray([])); - assert(!util.isArray({ yaNo: "array" })); - assert(!util.isArray(null)); - }, -}); - -Deno.test({ - name: "[util] isPrimitive", - fn() { - const stringType = "hasti"; - const booleanType = true; - const integerType = 2; - const symbolType = Symbol("anything"); - - const functionType = function doBest(): void {}; - const objectType = { name: "ali" }; - const arrayType = [1, 2, 3]; - - assert(util.isPrimitive(stringType)); - assert(util.isPrimitive(booleanType)); - assert(util.isPrimitive(integerType)); - assert(util.isPrimitive(symbolType)); - assert(util.isPrimitive(null)); - assert(util.isPrimitive(undefined)); - assert(!util.isPrimitive(functionType)); - assert(!util.isPrimitive(arrayType)); - assert(!util.isPrimitive(objectType)); - }, -}); - -Deno.test({ - name: "[util] TextDecoder", - fn() { - assert(util.TextDecoder === TextDecoder); - const td: util.TextDecoder = new util.TextDecoder(); - assert(td instanceof TextDecoder); - }, -}); - -Deno.test({ - name: "[util] TextEncoder", - fn() { - assert(util.TextEncoder === TextEncoder); - const te: util.TextEncoder = new util.TextEncoder(); - assert(te instanceof TextEncoder); - }, -}); - -Deno.test({ - name: "[util] isDate", - fn() { - // Test verifies the method is exposed. See _util/_util_types_test for details - assert(util.types.isDate(new Date())); - }, -}); - -Deno.test({ - name: "[util] getSystemErrorName()", - fn() { - type FnTestInvalidArg = (code?: unknown) => void; - - assertThrows( - () => (util.getSystemErrorName as FnTestInvalidArg)(), - TypeError, - ); - assertThrows( - () => (util.getSystemErrorName as FnTestInvalidArg)(1), - RangeError, - ); - - assertStrictEquals(util.getSystemErrorName(-424242), undefined); - - switch (Deno.build.os) { - case "windows": - assertStrictEquals(util.getSystemErrorName(-4091), "EADDRINUSE"); - break; - - case "darwin": - assertStrictEquals(util.getSystemErrorName(-48), "EADDRINUSE"); - break; - - case "linux": - assertStrictEquals(util.getSystemErrorName(-98), "EADDRINUSE"); - break; - } - }, -}); - -Deno.test("[util] deprecate", () => { - const warn = console.warn.bind(null); - - let output; - console.warn = function (str: string) { - output = str; - warn(output); - }; - - const message = "x is deprecated"; - - const expected = 12; - let result; - const x = util.deprecate(() => { - result = expected; - }, message); - - x(); - - assertEquals(expected, result); - assertEquals(output, message); - - console.warn = warn; -}); diff --git a/std/path/README.md b/std/path/README.md deleted file mode 100644 index c1debfc31d..0000000000 --- a/std/path/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Deno Path Manipulation Libraries - -Usage: - -```ts -import * as path from "https://deno.land/std@$STD_VERSION/path/mod.ts"; -``` - -### globToRegExp - -Generate a regex based on glob pattern and options This was meant to be using -the `fs.walk` function but can be used anywhere else. - -```ts -import { globToRegExp } from "https://deno.land/std@$STD_VERSION/path/glob.ts"; - -globToRegExp("foo/**/*.json", { - flags: "g", - extended: true, - globstar: true, -}); // returns the regex to find all .json files in the folder foo. -``` diff --git a/std/path/_constants.ts b/std/path/_constants.ts deleted file mode 100644 index 88374ae059..0000000000 --- a/std/path/_constants.ts +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -// This module is browser compatible. - -// Alphabet chars. -export const CHAR_UPPERCASE_A = 65; /* A */ -export const CHAR_LOWERCASE_A = 97; /* a */ -export const CHAR_UPPERCASE_Z = 90; /* Z */ -export const CHAR_LOWERCASE_Z = 122; /* z */ - -// Non-alphabetic chars. -export const CHAR_DOT = 46; /* . */ -export const CHAR_FORWARD_SLASH = 47; /* / */ -export const CHAR_BACKWARD_SLASH = 92; /* \ */ -export const CHAR_VERTICAL_LINE = 124; /* | */ -export const CHAR_COLON = 58; /* : */ -export const CHAR_QUESTION_MARK = 63; /* ? */ -export const CHAR_UNDERSCORE = 95; /* _ */ -export const CHAR_LINE_FEED = 10; /* \n */ -export const CHAR_CARRIAGE_RETURN = 13; /* \r */ -export const CHAR_TAB = 9; /* \t */ -export const CHAR_FORM_FEED = 12; /* \f */ -export const CHAR_EXCLAMATION_MARK = 33; /* ! */ -export const CHAR_HASH = 35; /* # */ -export const CHAR_SPACE = 32; /* */ -export const CHAR_NO_BREAK_SPACE = 160; /* \u00A0 */ -export const CHAR_ZERO_WIDTH_NOBREAK_SPACE = 65279; /* \uFEFF */ -export const CHAR_LEFT_SQUARE_BRACKET = 91; /* [ */ -export const CHAR_RIGHT_SQUARE_BRACKET = 93; /* ] */ -export const CHAR_LEFT_ANGLE_BRACKET = 60; /* < */ -export const CHAR_RIGHT_ANGLE_BRACKET = 62; /* > */ -export const CHAR_LEFT_CURLY_BRACKET = 123; /* { */ -export const CHAR_RIGHT_CURLY_BRACKET = 125; /* } */ -export const CHAR_HYPHEN_MINUS = 45; /* - */ -export const CHAR_PLUS = 43; /* + */ -export const CHAR_DOUBLE_QUOTE = 34; /* " */ -export const CHAR_SINGLE_QUOTE = 39; /* ' */ -export const CHAR_PERCENT = 37; /* % */ -export const CHAR_SEMICOLON = 59; /* ; */ -export const CHAR_CIRCUMFLEX_ACCENT = 94; /* ^ */ -export const CHAR_GRAVE_ACCENT = 96; /* ` */ -export const CHAR_AT = 64; /* @ */ -export const CHAR_AMPERSAND = 38; /* & */ -export const CHAR_EQUAL = 61; /* = */ - -// Digits -export const CHAR_0 = 48; /* 0 */ -export const CHAR_9 = 57; /* 9 */ diff --git a/std/path/_interface.ts b/std/path/_interface.ts deleted file mode 100644 index d5015cbebe..0000000000 --- a/std/path/_interface.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. - -/** - * A parsed path object generated by path.parse() or consumed by path.format(). - */ -export interface ParsedPath { - /** - * The root of the path such as '/' or 'c:\' - */ - root: string; - /** - * The full directory path such as '/home/user/dir' or 'c:\path\dir' - */ - dir: string; - /** - * The file name including extension (if any) such as 'index.html' - */ - base: string; - /** - * The file extension (if any) such as '.html' - */ - ext: string; - /** - * The file name without extension (if any) such as 'index' - */ - name: string; -} - -export type FormatInputPathObject = Partial; diff --git a/std/path/_util.ts b/std/path/_util.ts deleted file mode 100644 index 046c443378..0000000000 --- a/std/path/_util.ts +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -// This module is browser compatible. - -import type { FormatInputPathObject } from "./_interface.ts"; -import { - CHAR_BACKWARD_SLASH, - CHAR_DOT, - CHAR_FORWARD_SLASH, - CHAR_LOWERCASE_A, - CHAR_LOWERCASE_Z, - CHAR_UPPERCASE_A, - CHAR_UPPERCASE_Z, -} from "./_constants.ts"; - -export function assertPath(path: string): void { - if (typeof path !== "string") { - throw new TypeError( - `Path must be a string. Received ${JSON.stringify(path)}`, - ); - } -} - -export function isPosixPathSeparator(code: number): boolean { - return code === CHAR_FORWARD_SLASH; -} - -export function isPathSeparator(code: number): boolean { - return isPosixPathSeparator(code) || code === CHAR_BACKWARD_SLASH; -} - -export function isWindowsDeviceRoot(code: number): boolean { - return ( - (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z) || - (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) - ); -} - -// Resolves . and .. elements in a path with directory names -export function normalizeString( - path: string, - allowAboveRoot: boolean, - separator: string, - isPathSeparator: (code: number) => boolean, -): string { - let res = ""; - let lastSegmentLength = 0; - let lastSlash = -1; - let dots = 0; - let code: number | undefined; - for (let i = 0, len = path.length; i <= len; ++i) { - if (i < len) code = path.charCodeAt(i); - else if (isPathSeparator(code!)) break; - else code = CHAR_FORWARD_SLASH; - - if (isPathSeparator(code!)) { - if (lastSlash === i - 1 || dots === 1) { - // NOOP - } else if (lastSlash !== i - 1 && dots === 2) { - if ( - res.length < 2 || - lastSegmentLength !== 2 || - res.charCodeAt(res.length - 1) !== CHAR_DOT || - res.charCodeAt(res.length - 2) !== CHAR_DOT - ) { - if (res.length > 2) { - const lastSlashIndex = res.lastIndexOf(separator); - if (lastSlashIndex === -1) { - res = ""; - lastSegmentLength = 0; - } else { - res = res.slice(0, lastSlashIndex); - lastSegmentLength = res.length - 1 - res.lastIndexOf(separator); - } - lastSlash = i; - dots = 0; - continue; - } else if (res.length === 2 || res.length === 1) { - res = ""; - lastSegmentLength = 0; - lastSlash = i; - dots = 0; - continue; - } - } - if (allowAboveRoot) { - if (res.length > 0) res += `${separator}..`; - else res = ".."; - lastSegmentLength = 2; - } - } else { - if (res.length > 0) res += separator + path.slice(lastSlash + 1, i); - else res = path.slice(lastSlash + 1, i); - lastSegmentLength = i - lastSlash - 1; - } - lastSlash = i; - dots = 0; - } else if (code === CHAR_DOT && dots !== -1) { - ++dots; - } else { - dots = -1; - } - } - return res; -} - -export function _format( - sep: string, - pathObject: FormatInputPathObject, -): string { - const dir: string | undefined = pathObject.dir || pathObject.root; - const base: string = pathObject.base || - (pathObject.name || "") + (pathObject.ext || ""); - if (!dir) return base; - if (dir === pathObject.root) return dir + base; - return dir + sep + base; -} diff --git a/std/path/basename_test.ts b/std/path/basename_test.ts deleted file mode 100644 index 9e2265db9e..0000000000 --- a/std/path/basename_test.ts +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -Deno.test("basename", function () { - assertEquals(path.basename(".js", ".js"), ""); - assertEquals(path.basename(""), ""); - assertEquals(path.basename("/dir/basename.ext"), "basename.ext"); - assertEquals(path.basename("/basename.ext"), "basename.ext"); - assertEquals(path.basename("basename.ext"), "basename.ext"); - assertEquals(path.basename("basename.ext/"), "basename.ext"); - assertEquals(path.basename("basename.ext//"), "basename.ext"); - assertEquals(path.basename("aaa/bbb", "/bbb"), "bbb"); - assertEquals(path.basename("aaa/bbb", "a/bbb"), "bbb"); - assertEquals(path.basename("aaa/bbb", "bbb"), "bbb"); - assertEquals(path.basename("aaa/bbb//", "bbb"), "bbb"); - assertEquals(path.basename("aaa/bbb", "bb"), "b"); - assertEquals(path.basename("aaa/bbb", "b"), "bb"); - assertEquals(path.basename("/aaa/bbb", "/bbb"), "bbb"); - assertEquals(path.basename("/aaa/bbb", "a/bbb"), "bbb"); - assertEquals(path.basename("/aaa/bbb", "bbb"), "bbb"); - assertEquals(path.basename("/aaa/bbb//", "bbb"), "bbb"); - assertEquals(path.basename("/aaa/bbb", "bb"), "b"); - assertEquals(path.basename("/aaa/bbb", "b"), "bb"); - assertEquals(path.basename("/aaa/bbb"), "bbb"); - assertEquals(path.basename("/aaa/"), "aaa"); - assertEquals(path.basename("/aaa/b"), "b"); - assertEquals(path.basename("/a/b"), "b"); - assertEquals(path.basename("//a"), "a"); - - // On unix a backslash is just treated as any other character. - assertEquals( - path.posix.basename("\\dir\\basename.ext"), - "\\dir\\basename.ext", - ); - assertEquals(path.posix.basename("\\basename.ext"), "\\basename.ext"); - assertEquals(path.posix.basename("basename.ext"), "basename.ext"); - assertEquals(path.posix.basename("basename.ext\\"), "basename.ext\\"); - assertEquals(path.posix.basename("basename.ext\\\\"), "basename.ext\\\\"); - assertEquals(path.posix.basename("foo"), "foo"); - - // POSIX filenames may include control characters - const controlCharFilename = "Icon" + String.fromCharCode(13); - assertEquals( - path.posix.basename("/a/b/" + controlCharFilename), - controlCharFilename, - ); -}); - -Deno.test("basenameWin32", function () { - assertEquals(path.win32.basename("\\dir\\basename.ext"), "basename.ext"); - assertEquals(path.win32.basename("\\basename.ext"), "basename.ext"); - assertEquals(path.win32.basename("basename.ext"), "basename.ext"); - assertEquals(path.win32.basename("basename.ext\\"), "basename.ext"); - assertEquals(path.win32.basename("basename.ext\\\\"), "basename.ext"); - assertEquals(path.win32.basename("foo"), "foo"); - assertEquals(path.win32.basename("aaa\\bbb", "\\bbb"), "bbb"); - assertEquals(path.win32.basename("aaa\\bbb", "a\\bbb"), "bbb"); - assertEquals(path.win32.basename("aaa\\bbb", "bbb"), "bbb"); - assertEquals(path.win32.basename("aaa\\bbb\\\\\\\\", "bbb"), "bbb"); - assertEquals(path.win32.basename("aaa\\bbb", "bb"), "b"); - assertEquals(path.win32.basename("aaa\\bbb", "b"), "bb"); - assertEquals(path.win32.basename("C:"), ""); - assertEquals(path.win32.basename("C:."), "."); - assertEquals(path.win32.basename("C:\\"), ""); - assertEquals(path.win32.basename("C:\\dir\\base.ext"), "base.ext"); - assertEquals(path.win32.basename("C:\\basename.ext"), "basename.ext"); - assertEquals(path.win32.basename("C:basename.ext"), "basename.ext"); - assertEquals(path.win32.basename("C:basename.ext\\"), "basename.ext"); - assertEquals(path.win32.basename("C:basename.ext\\\\"), "basename.ext"); - assertEquals(path.win32.basename("C:foo"), "foo"); - assertEquals(path.win32.basename("file:stream"), "file:stream"); -}); diff --git a/std/path/common.ts b/std/path/common.ts deleted file mode 100644 index 390c04f1db..0000000000 --- a/std/path/common.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. - -import { SEP } from "./separator.ts"; - -/** Determines the common path from a set of paths, using an optional separator, - * which defaults to the OS default separator. - * - * import { common } from "https://deno.land/std/path/mod.ts"; - * const p = common([ - * "./deno/std/path/mod.ts", - * "./deno/std/fs/mod.ts", - * ]); - * console.log(p); // "./deno/std/" - * - */ -export function common(paths: string[], sep = SEP): string { - const [first = "", ...remaining] = paths; - if (first === "" || remaining.length === 0) { - return first.substring(0, first.lastIndexOf(sep) + 1); - } - const parts = first.split(sep); - - let endOfPrefix = parts.length; - for (const path of remaining) { - const compare = path.split(sep); - for (let i = 0; i < endOfPrefix; i++) { - if (compare[i] !== parts[i]) { - endOfPrefix = i; - } - } - - if (endOfPrefix === 0) { - return ""; - } - } - const prefix = parts.slice(0, endOfPrefix).join(sep); - return prefix.endsWith(sep) ? prefix : `${prefix}${sep}`; -} diff --git a/std/path/common_test.ts b/std/path/common_test.ts deleted file mode 100644 index b34b54741f..0000000000 --- a/std/path/common_test.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import { common } from "./mod.ts"; - -Deno.test({ - name: "path - common - basic usage", - fn() { - const actual = common( - [ - "file://deno/cli/js/deno.ts", - "file://deno/std/path/mod.ts", - "file://deno/cli/js/main.ts", - ], - "/", - ); - assertEquals(actual, "file://deno/"); - }, -}); - -Deno.test({ - name: "path - common - no shared", - fn() { - const actual = common( - ["file://deno/cli/js/deno.ts", "https://deno.land/std/path/mod.ts"], - "/", - ); - assertEquals(actual, ""); - }, -}); - -Deno.test({ - name: "path - common - windows sep", - fn() { - const actual = common( - [ - "c:\\deno\\cli\\js\\deno.ts", - "c:\\deno\\std\\path\\mod.ts", - "c:\\deno\\cli\\js\\main.ts", - ], - "\\", - ); - assertEquals(actual, "c:\\deno\\"); - }, -}); diff --git a/std/path/dirname_test.ts b/std/path/dirname_test.ts deleted file mode 100644 index 210d3a9ec5..0000000000 --- a/std/path/dirname_test.ts +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -Deno.test("dirname", function () { - assertEquals(path.posix.dirname("/a/b/"), "/a"); - assertEquals(path.posix.dirname("/a/b"), "/a"); - assertEquals(path.posix.dirname("/a"), "/"); - assertEquals(path.posix.dirname(""), "."); - assertEquals(path.posix.dirname("/"), "/"); - assertEquals(path.posix.dirname("////"), "/"); - assertEquals(path.posix.dirname("//a"), "//"); - assertEquals(path.posix.dirname("foo"), "."); -}); - -Deno.test("dirnameWin32", function () { - assertEquals(path.win32.dirname("c:\\"), "c:\\"); - assertEquals(path.win32.dirname("c:\\foo"), "c:\\"); - assertEquals(path.win32.dirname("c:\\foo\\"), "c:\\"); - assertEquals(path.win32.dirname("c:\\foo\\bar"), "c:\\foo"); - assertEquals(path.win32.dirname("c:\\foo\\bar\\"), "c:\\foo"); - assertEquals(path.win32.dirname("c:\\foo\\bar\\baz"), "c:\\foo\\bar"); - assertEquals(path.win32.dirname("\\"), "\\"); - assertEquals(path.win32.dirname("\\foo"), "\\"); - assertEquals(path.win32.dirname("\\foo\\"), "\\"); - assertEquals(path.win32.dirname("\\foo\\bar"), "\\foo"); - assertEquals(path.win32.dirname("\\foo\\bar\\"), "\\foo"); - assertEquals(path.win32.dirname("\\foo\\bar\\baz"), "\\foo\\bar"); - assertEquals(path.win32.dirname("c:"), "c:"); - assertEquals(path.win32.dirname("c:foo"), "c:"); - assertEquals(path.win32.dirname("c:foo\\"), "c:"); - assertEquals(path.win32.dirname("c:foo\\bar"), "c:foo"); - assertEquals(path.win32.dirname("c:foo\\bar\\"), "c:foo"); - assertEquals(path.win32.dirname("c:foo\\bar\\baz"), "c:foo\\bar"); - assertEquals(path.win32.dirname("file:stream"), "."); - assertEquals(path.win32.dirname("dir\\file:stream"), "dir"); - assertEquals(path.win32.dirname("\\\\unc\\share"), "\\\\unc\\share"); - assertEquals(path.win32.dirname("\\\\unc\\share\\foo"), "\\\\unc\\share\\"); - assertEquals(path.win32.dirname("\\\\unc\\share\\foo\\"), "\\\\unc\\share\\"); - assertEquals( - path.win32.dirname("\\\\unc\\share\\foo\\bar"), - "\\\\unc\\share\\foo", - ); - assertEquals( - path.win32.dirname("\\\\unc\\share\\foo\\bar\\"), - "\\\\unc\\share\\foo", - ); - assertEquals( - path.win32.dirname("\\\\unc\\share\\foo\\bar\\baz"), - "\\\\unc\\share\\foo\\bar", - ); - assertEquals(path.win32.dirname("/a/b/"), "/a"); - assertEquals(path.win32.dirname("/a/b"), "/a"); - assertEquals(path.win32.dirname("/a"), "/"); - assertEquals(path.win32.dirname(""), "."); - assertEquals(path.win32.dirname("/"), "/"); - assertEquals(path.win32.dirname("////"), "/"); - assertEquals(path.win32.dirname("foo"), "."); -}); diff --git a/std/path/extname_test.ts b/std/path/extname_test.ts deleted file mode 100644 index 7e1abf4bc4..0000000000 --- a/std/path/extname_test.ts +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -const slashRE = /\//g; - -const pairs = [ - ["", ""], - ["/path/to/file", ""], - ["/path/to/file.ext", ".ext"], - ["/path.to/file.ext", ".ext"], - ["/path.to/file", ""], - ["/path.to/.file", ""], - ["/path.to/.file.ext", ".ext"], - ["/path/to/f.ext", ".ext"], - ["/path/to/..ext", ".ext"], - ["/path/to/..", ""], - ["file", ""], - ["file.ext", ".ext"], - [".file", ""], - [".file.ext", ".ext"], - ["/file", ""], - ["/file.ext", ".ext"], - ["/.file", ""], - ["/.file.ext", ".ext"], - [".path/file.ext", ".ext"], - ["file.ext.ext", ".ext"], - ["file.", "."], - [".", ""], - ["./", ""], - [".file.ext", ".ext"], - [".file", ""], - [".file.", "."], - [".file..", "."], - ["..", ""], - ["../", ""], - ["..file.ext", ".ext"], - ["..file", ".file"], - ["..file.", "."], - ["..file..", "."], - ["...", "."], - ["...ext", ".ext"], - ["....", "."], - ["file.ext/", ".ext"], - ["file.ext//", ".ext"], - ["file/", ""], - ["file//", ""], - ["file./", "."], - ["file.//", "."], -]; - -Deno.test("extname", function () { - pairs.forEach(function (p) { - const input = p[0]; - const expected = p[1]; - assertEquals(expected, path.posix.extname(input)); - }); - - // On *nix, backslash is a valid name component like any other character. - assertEquals(path.posix.extname(".\\"), ""); - assertEquals(path.posix.extname("..\\"), ".\\"); - assertEquals(path.posix.extname("file.ext\\"), ".ext\\"); - assertEquals(path.posix.extname("file.ext\\\\"), ".ext\\\\"); - assertEquals(path.posix.extname("file\\"), ""); - assertEquals(path.posix.extname("file\\\\"), ""); - assertEquals(path.posix.extname("file.\\"), ".\\"); - assertEquals(path.posix.extname("file.\\\\"), ".\\\\"); -}); - -Deno.test("extnameWin32", function () { - pairs.forEach(function (p) { - const input = p[0].replace(slashRE, "\\"); - const expected = p[1]; - assertEquals(expected, path.win32.extname(input)); - assertEquals(expected, path.win32.extname("C:" + input)); - }); - - // On Windows, backslash is a path separator. - assertEquals(path.win32.extname(".\\"), ""); - assertEquals(path.win32.extname("..\\"), ""); - assertEquals(path.win32.extname("file.ext\\"), ".ext"); - assertEquals(path.win32.extname("file.ext\\\\"), ".ext"); - assertEquals(path.win32.extname("file\\"), ""); - assertEquals(path.win32.extname("file\\\\"), ""); - assertEquals(path.win32.extname("file.\\"), "."); - assertEquals(path.win32.extname("file.\\\\"), "."); -}); diff --git a/std/path/from_file_url_test.ts b/std/path/from_file_url_test.ts deleted file mode 100644 index 61a45a89be..0000000000 --- a/std/path/from_file_url_test.ts +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { posix, win32 } from "./mod.ts"; -import { assertEquals, assertThrows } from "../testing/asserts.ts"; - -Deno.test("[path] fromFileUrl", function () { - assertEquals(posix.fromFileUrl(new URL("file:///home/foo")), "/home/foo"); - assertEquals(posix.fromFileUrl("file:///"), "/"); - assertEquals(posix.fromFileUrl("file:///home/foo"), "/home/foo"); - assertEquals(posix.fromFileUrl("file:///home/foo%20bar"), "/home/foo bar"); - assertEquals(posix.fromFileUrl("file:///%"), "/%"); - assertEquals(posix.fromFileUrl("file://localhost/foo"), "/foo"); - assertEquals(posix.fromFileUrl("file:///C:"), "/C:"); - assertEquals(posix.fromFileUrl("file:///C:/"), "/C:/"); - assertEquals(posix.fromFileUrl("file:///C:/Users/"), "/C:/Users/"); - assertEquals(posix.fromFileUrl("file:///C:foo/bar"), "/C:foo/bar"); - assertThrows( - () => posix.fromFileUrl("http://localhost/foo"), - TypeError, - "Must be a file URL.", - ); - assertThrows( - () => posix.fromFileUrl("abcd://localhost/foo"), - TypeError, - "Must be a file URL.", - ); -}); - -Deno.test("[path] fromFileUrl (win32)", function () { - assertEquals(win32.fromFileUrl(new URL("file:///home/foo")), "\\home\\foo"); - assertEquals(win32.fromFileUrl("file:///"), "\\"); - assertEquals(win32.fromFileUrl("file:///home/foo"), "\\home\\foo"); - assertEquals(win32.fromFileUrl("file:///home/foo%20bar"), "\\home\\foo bar"); - assertEquals(win32.fromFileUrl("file:///%"), "\\%"); - assertEquals(win32.fromFileUrl("file://localhost/foo"), "\\\\localhost\\foo"); - assertEquals(win32.fromFileUrl("file:///C:"), "C:\\"); - assertEquals(win32.fromFileUrl("file:///C:/"), "C:\\"); - // Drop the hostname if a drive letter is parsed. - assertEquals(win32.fromFileUrl("file://localhost/C:/"), "C:\\"); - assertEquals(win32.fromFileUrl("file:///C:/Users/"), "C:\\Users\\"); - assertEquals(win32.fromFileUrl("file:///C:foo/bar"), "\\C:foo\\bar"); - assertThrows( - () => win32.fromFileUrl("http://localhost/foo"), - TypeError, - "Must be a file URL.", - ); - assertThrows( - () => win32.fromFileUrl("abcd://localhost/foo"), - TypeError, - "Must be a file URL.", - ); -}); diff --git a/std/path/glob.ts b/std/path/glob.ts deleted file mode 100644 index d599c1b88e..0000000000 --- a/std/path/glob.ts +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. - -import { osType } from "../_util/os.ts"; -import { join, normalize } from "./mod.ts"; -import { SEP, SEP_PATTERN } from "./separator.ts"; - -export interface GlobOptions { - /** Extended glob syntax. - * See https://www.linuxjournal.com/content/bash-extended-globbing. Defaults - * to true. */ - extended?: boolean; - /** Globstar syntax. - * See https://www.linuxjournal.com/content/globstar-new-bash-globbing-option. - * If false, `**` is treated like `*`. Defaults to true. */ - globstar?: boolean; - /** Operating system. Defaults to the native OS. */ - os?: typeof Deno.build.os; -} - -export type GlobToRegExpOptions = GlobOptions; - -// deno-fmt-ignore -const regExpEscapeChars = ["!", "$", "(", ")", "*", "+", ".", "=", "?", "[", "\\", "^", "{", "|"]; -const rangeEscapeChars = ["-", "\\", "]"]; - -/** Convert a glob string to a regular expression. - * - * Tries to match bash glob expansion as closely as possible. - * - * Basic glob syntax: - * - `*` - Matches everything without leaving the path segment. - * - `{foo,bar}` - Matches `foo` or `bar`. - * - `[abcd]` - Matches `a`, `b`, `c` or `d`. - * - `[a-d]` - Matches `a`, `b`, `c` or `d`. - * - `[!abcd]` - Matches any single character besides `a`, `b`, `c` or `d`. - * - `[[::]]` - Matches any character belonging to ``. - * - `[[:alnum:]]` - Matches any digit or letter. - * - `[[:digit:]abc]` - Matches any digit, `a`, `b` or `c`. - * - See https://facelessuser.github.io/wcmatch/glob/#posix-character-classes - * for a complete list of supported character classes. - * - `\` - Escapes the next character for an `os` other than `"windows"`. - * - \` - Escapes the next character for `os` set to `"windows"`. - * - `/` - Path separator. - * - `\` - Additional path separator only for `os` set to `"windows"`. - * - * Extended syntax: - * - Requires `{ extended: true }`. - * - `?(foo|bar)` - Matches 0 or 1 instance of `{foo,bar}`. - * - `@(foo|bar)` - Matches 1 instance of `{foo,bar}`. They behave the same. - * - `*(foo|bar)` - Matches _n_ instances of `{foo,bar}`. - * - `+(foo|bar)` - Matches _n > 0_ instances of `{foo,bar}`. - * - `!(foo|bar)` - Matches anything other than `{foo,bar}`. - * - See https://www.linuxjournal.com/content/bash-extended-globbing. - * - * Globstar syntax: - * - Requires `{ globstar: true }`. - * - `**` - Matches any number of any path segments. - * - Must comprise its entire path segment in the provided glob. - * - See https://www.linuxjournal.com/content/globstar-new-bash-globbing-option. - * - * Note the following properties: - * - The generated `RegExp` is anchored at both start and end. - * - Repeating and trailing separators are tolerated. Trailing separators in the - * provided glob have no meaning and are discarded. - * - Absolute globs will only match absolute paths, etc. - * - Empty globs will match nothing. - * - Any special glob syntax must be contained to one path segment. For example, - * `?(foo|bar/baz)` is invalid. The separator will take precendence and the - * first segment ends with an unclosed group. - * - If a path segment ends with unclosed groups or a dangling escape prefix, a - * parse error has occured. Every character for that segment is taken - * literally in this event. - * - * Limitations: - * - A negative group like `!(foo|bar)` will wrongly be converted to a negative - * look-ahead followed by a wildcard. This means that `!(foo).js` will wrongly - * fail to match `foobar.js`, even though `foobar` is not `foo`. Effectively, - * `!(foo|bar)` is treated like `!(@(foo|bar)*)`. This will work correctly if - * the group occurs not nested at the end of the segment. */ -export function globToRegExp( - glob: string, - { extended = true, globstar: globstarOption = true, os = osType }: - GlobToRegExpOptions = {}, -): RegExp { - if (glob == "") { - return /(?!)/; - } - - const sep = os == "windows" ? "(?:\\\\|/)+" : "/+"; - const sepMaybe = os == "windows" ? "(?:\\\\|/)*" : "/*"; - const seps = os == "windows" ? ["\\", "/"] : ["/"]; - const globstar = os == "windows" - ? "(?:[^\\\\/]*(?:\\\\|/|$)+)*" - : "(?:[^/]*(?:/|$)+)*"; - const wildcard = os == "windows" ? "[^\\\\/]*" : "[^/]*"; - const escapePrefix = os == "windows" ? "`" : "\\"; - - // Remove trailing separators. - let newLength = glob.length; - for (; newLength > 1 && seps.includes(glob[newLength - 1]); newLength--); - glob = glob.slice(0, newLength); - - let regExpString = ""; - - // Terminates correctly. Trust that `j` is incremented every iteration. - for (let j = 0; j < glob.length;) { - let segment = ""; - const groupStack = []; - let inRange = false; - let inEscape = false; - let endsWithSep = false; - let i = j; - - // Terminates with `i` at the non-inclusive end of the current segment. - for (; i < glob.length && !seps.includes(glob[i]); i++) { - if (inEscape) { - inEscape = false; - const escapeChars = inRange ? rangeEscapeChars : regExpEscapeChars; - segment += escapeChars.includes(glob[i]) ? `\\${glob[i]}` : glob[i]; - continue; - } - - if (glob[i] == escapePrefix) { - inEscape = true; - continue; - } - - if (glob[i] == "[") { - if (!inRange) { - inRange = true; - segment += "["; - if (glob[i + 1] == "!") { - i++; - segment += "^"; - } else if (glob[i + 1] == "^") { - i++; - segment += "\\^"; - } - continue; - } else if (glob[i + 1] == ":") { - let k = i + 1; - let value = ""; - while (glob[k + 1] != null && glob[k + 1] != ":") { - value += glob[k + 1]; - k++; - } - if (glob[k + 1] == ":" && glob[k + 2] == "]") { - i = k + 2; - if (value == "alnum") segment += "\\dA-Za-z"; - else if (value == "alpha") segment += "A-Za-z"; - else if (value == "ascii") segment += "\x00-\x7F"; - else if (value == "blank") segment += "\t "; - else if (value == "cntrl") segment += "\x00-\x1F\x7F"; - else if (value == "digit") segment += "\\d"; - else if (value == "graph") segment += "\x21-\x7E"; - else if (value == "lower") segment += "a-z"; - else if (value == "print") segment += "\x20-\x7E"; - else if (value == "punct") { - segment += "!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_‘{|}~"; - } else if (value == "space") segment += "\\s\v"; - else if (value == "upper") segment += "A-Z"; - else if (value == "word") segment += "\\w"; - else if (value == "xdigit") segment += "\\dA-Fa-f"; - continue; - } - } - } - - if (glob[i] == "]" && inRange) { - inRange = false; - segment += "]"; - continue; - } - - if (inRange) { - if (glob[i] == "\\") { - segment += `\\\\`; - } else { - segment += glob[i]; - } - continue; - } - - if ( - glob[i] == ")" && groupStack.length > 0 && - groupStack[groupStack.length - 1] != "BRACE" - ) { - segment += ")"; - const type = groupStack.pop()!; - if (type == "!") { - segment += wildcard; - } else if (type != "@") { - segment += type; - } - continue; - } - - if ( - glob[i] == "|" && groupStack.length > 0 && - groupStack[groupStack.length - 1] != "BRACE" - ) { - segment += "|"; - continue; - } - - if (glob[i] == "+" && extended && glob[i + 1] == "(") { - i++; - groupStack.push("+"); - segment += "(?:"; - continue; - } - - if (glob[i] == "@" && extended && glob[i + 1] == "(") { - i++; - groupStack.push("@"); - segment += "(?:"; - continue; - } - - if (glob[i] == "?") { - if (extended && glob[i + 1] == "(") { - i++; - groupStack.push("?"); - segment += "(?:"; - } else { - segment += "."; - } - continue; - } - - if (glob[i] == "!" && extended && glob[i + 1] == "(") { - i++; - groupStack.push("!"); - segment += "(?!"; - continue; - } - - if (glob[i] == "{") { - groupStack.push("BRACE"); - segment += "(?:"; - continue; - } - - if (glob[i] == "}" && groupStack[groupStack.length - 1] == "BRACE") { - groupStack.pop(); - segment += ")"; - continue; - } - - if (glob[i] == "," && groupStack[groupStack.length - 1] == "BRACE") { - segment += "|"; - continue; - } - - if (glob[i] == "*") { - if (extended && glob[i + 1] == "(") { - i++; - groupStack.push("*"); - segment += "(?:"; - } else { - const prevChar = glob[i - 1]; - let numStars = 1; - while (glob[i + 1] == "*") { - i++; - numStars++; - } - const nextChar = glob[i + 1]; - if ( - globstarOption && numStars == 2 && - [...seps, undefined].includes(prevChar) && - [...seps, undefined].includes(nextChar) - ) { - segment += globstar; - endsWithSep = true; - } else { - segment += wildcard; - } - } - continue; - } - - segment += regExpEscapeChars.includes(glob[i]) ? `\\${glob[i]}` : glob[i]; - } - - // Check for unclosed groups or a dangling backslash. - if (groupStack.length > 0 || inRange || inEscape) { - // Parse failure. Take all characters from this segment literally. - segment = ""; - for (const c of glob.slice(j, i)) { - segment += regExpEscapeChars.includes(c) ? `\\${c}` : c; - endsWithSep = false; - } - } - - regExpString += segment; - if (!endsWithSep) { - regExpString += i < glob.length ? sep : sepMaybe; - endsWithSep = true; - } - - // Terminates with `i` at the start of the next segment. - while (seps.includes(glob[i])) i++; - - // Check that the next value of `j` is indeed higher than the current value. - if (!(i > j)) { - throw new Error("Assertion failure: i > j (potential infinite loop)"); - } - j = i; - } - - regExpString = `^${regExpString}$`; - return new RegExp(regExpString); -} - -/** Test whether the given string is a glob */ -export function isGlob(str: string): boolean { - const chars: Record = { "{": "}", "(": ")", "[": "]" }; - const regex = - /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; - - if (str === "") { - return false; - } - - let match: RegExpExecArray | null; - - while ((match = regex.exec(str))) { - if (match[2]) return true; - let idx = match.index + match[0].length; - - // if an open bracket/brace/paren is escaped, - // set the index to the next closing character - const open = match[1]; - const close = open ? chars[open] : null; - if (open && close) { - const n = str.indexOf(close, idx); - if (n !== -1) { - idx = n + 1; - } - } - - str = str.slice(idx); - } - - return false; -} - -/** Like normalize(), but doesn't collapse "**\/.." when `globstar` is true. */ -export function normalizeGlob( - glob: string, - { globstar = false }: GlobOptions = {}, -): string { - if (glob.match(/\0/g)) { - throw new Error(`Glob contains invalid characters: "${glob}"`); - } - if (!globstar) { - return normalize(glob); - } - const s = SEP_PATTERN.source; - const badParentPattern = new RegExp( - `(?<=(${s}|^)\\*\\*${s})\\.\\.(?=${s}|$)`, - "g", - ); - return normalize(glob.replace(badParentPattern, "\0")).replace(/\0/g, ".."); -} - -/** Like join(), but doesn't collapse "**\/.." when `globstar` is true. */ -export function joinGlobs( - globs: string[], - { extended = false, globstar = false }: GlobOptions = {}, -): string { - if (!globstar || globs.length == 0) { - return join(...globs); - } - if (globs.length === 0) return "."; - let joined: string | undefined; - for (const glob of globs) { - const path = glob; - if (path.length > 0) { - if (!joined) joined = path; - else joined += `${SEP}${path}`; - } - } - if (!joined) return "."; - return normalizeGlob(joined, { extended, globstar }); -} diff --git a/std/path/glob_test.ts b/std/path/glob_test.ts deleted file mode 100644 index cf46a3c5c9..0000000000 --- a/std/path/glob_test.ts +++ /dev/null @@ -1,638 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "../testing/asserts.ts"; -import { - globToRegExp, - GlobToRegExpOptions, - isGlob, - joinGlobs, - normalizeGlob, -} from "./glob.ts"; -import { SEP } from "./mod.ts"; - -function match( - glob: string, - path: string, - opts: GlobToRegExpOptions = {}, -): boolean { - if (opts.os == null) { - const matchDarwin = path.match( - globToRegExp(glob, { ...opts, os: "darwin" }), - ); - if (matchDarwin) { - assertEquals(matchDarwin.length, 1); - } - const matchLinux = path.match(globToRegExp(glob, { ...opts, os: "linux" })); - if (matchLinux) { - assertEquals(matchLinux.length, 1); - } - const matchWindows = path.match( - globToRegExp(glob, { ...opts, os: "windows" }), - ); - if (matchWindows) { - assertEquals(matchWindows.length, 1); - } - return !!matchDarwin && !!matchLinux && !!matchWindows; - } else { - const match = path.match(globToRegExp(glob, opts)); - if (match) { - assertEquals(match.length, 1); - } - return !!match; - } -} - -Deno.test({ - name: "[path] globToRegExp() Basic RegExp", - fn(): void { - assertEquals(globToRegExp("*.js", { os: "linux" }), /^[^/]*\.js\/*$/); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Empty glob", - fn(): void { - assertEquals(globToRegExp(""), /(?!)/); - assertEquals(globToRegExp("*.js", { os: "linux" }), /^[^/]*\.js\/*$/); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() * (wildcard)", - fn(): void { - assert(match("*", "foo", { extended: false, globstar: false })); - assert(match("*", "foo", { extended: false, globstar: false })); - assert(match("f*", "foo", { extended: false, globstar: false })); - assert(match("f*", "foo", { extended: false, globstar: false })); - assert(match("*o", "foo", { extended: false, globstar: false })); - assert(match("*o", "foo", { extended: false, globstar: false })); - assert(match("u*orn", "unicorn", { extended: false, globstar: false })); - assert(match("u*orn", "unicorn", { extended: false, globstar: false })); - assert(!match("ico", "unicorn", { extended: false, globstar: false })); - assert(match("u*nicorn", "unicorn", { extended: false, globstar: false })); - assert(match("u*nicorn", "unicorn", { extended: false, globstar: false })); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() ? (match one character)", - fn(): void { - assert(match("f?o", "foo", { extended: false, globstar: false })); - assert(match("f?o?", "fooo", { extended: false, globstar: false })); - assert(!match("f?oo", "foo", { extended: false, globstar: false })); - assert(!match("?fo", "fooo", { extended: false, globstar: false })); - assert(!match("f?oo", "foo", { extended: false, globstar: false })); - assert(!match("foo?", "foo", { extended: false, globstar: false })); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() [seq] (character range)", - fn(): void { - assert(match("fo[oz]", "foo", { extended: false, globstar: false })); - assert(match("fo[oz]", "foz", { extended: false, globstar: false })); - assert(!match("fo[oz]", "fog", { extended: false, globstar: false })); - assert(match("fo[a-z]", "fob", { extended: false, globstar: false })); - assert(!match("fo[a-d]", "fot", { extended: false, globstar: false })); - assert(!match("fo[!tz]", "fot", { extended: false, globstar: false })); - assert(match("fo[!tz]", "fob", { extended: false, globstar: false })); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() [[:alnum:]] (character class in range)", - fn(): void { - assert( - match( - "[[:alnum:]]/bar.txt", - "a/bar.txt", - { extended: false, globstar: false }, - ), - ); - assert( - match( - "[[:alnum:]abc]/bar.txt", - "1/bar.txt", - { extended: false, globstar: false }, - ), - ); - assert( - !match( - "[[:alnum:]]/bar.txt", - "!/bar.txt", - { extended: false, globstar: false }, - ), - ); - for (const c of "09AGZagz") { - assert(match("[[:alnum:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "AGZagz") { - assert(match("[[:alpha:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "\x00\x20\x7F") { - assert(match("[[:ascii:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "\t ") { - assert(match("[[:blank:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "\x00\x1F\x7F") { - assert(match("[[:cntrl:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "09") { - assert(match("[[:digit:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "\x21\x7E") { - assert(match("[[:graph:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "az") { - assert(match("[[:lower:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "\x20\x7E") { - assert(match("[[:print:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "!\"#$%&'()*+,-./:;<=>?@[\\]^_‘{|}~") { - assert(match("[[:punct:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "\t\n\v\f\r ") { - assert(match("[[:space:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "AZ") { - assert(match("[[:upper:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "09AZaz_") { - assert(match("[[:word:]]", c, { extended: false, globstar: false }), c); - } - for (const c of "09AFaf") { - assert(match("[[:xdigit:]]", c, { extended: false, globstar: false }), c); - } - }, -}); - -Deno.test({ - name: "[path] globToRegExp() {} (brace expansion)", - fn(): void { - assert( - match("foo{bar,baaz}", "foobaaz", { extended: false, globstar: false }), - ); - assert( - match("foo{bar,baaz}", "foobar", { extended: false, globstar: false }), - ); - assert( - !match("foo{bar,baaz}", "foobuzz", { extended: false, globstar: false }), - ); - assert( - match("foo{bar,b*z}", "foobuzz", { extended: false, globstar: false }), - ); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Complex matches", - fn(): void { - assert( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://foo.baaz.com/jquery.min.js", - { extended: false, globstar: false }, - ), - ); - assert( - match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://moz.buzz.com/index.html", - { extended: false, globstar: false }, - ), - ); - assert( - !match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://moz.buzz.com/index.htm", - { extended: false, globstar: false }, - ), - ); - assert( - !match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://moz.bar.com/index.html", - { extended: false, globstar: false }, - ), - ); - assert( - !match( - "http://?o[oz].b*z.com/{*.js,*.html}", - "http://flozz.buzz.com/index.html", - { extended: false, globstar: false }, - ), - ); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() ** (globstar)", - fn(): void { - assert(match("/foo/**", "/foo/bar.txt")); - assert(match("/foo/**", "/foo/bar/baz.txt")); - assert(!match("/foo/**", "/foo/bar/baz.txt", { globstar: false })); - assert(match("/foo/**", "/foo/bar", { globstar: false })); - assert(match("/foo/**/*.txt", "/foo/bar/baz.txt")); - assert(match("/foo/**/*.txt", "/foo/bar/baz/qux.txt")); - assert(match("/foo/**/bar.txt", "/foo/bar.txt")); - assert(match("/foo/**/**/bar.txt", "/foo/bar.txt")); - assert(match("/foo/**/*/baz.txt", "/foo/bar/baz.txt")); - assert(match("/foo/**/*.txt", "/foo/bar.txt")); - assert(match("/foo/**/**/*.txt", "/foo/bar.txt")); - assert(match("/foo/**/*/*.txt", "/foo/bar/baz.txt")); - assert(match("**/*.txt", "/foo/bar/baz/qux.txt")); - assert(match("**/foo.txt", "foo.txt")); - assert(match("**/*.txt", "foo.txt")); - assert(!match("/foo/**.txt", "/foo/bar/baz/qux.txt")); - assert( - !match("/foo/bar**/*.txt", "/foo/bar/baz/qux.txt"), - ); - assert(!match("/foo/bar**", "/foo/bar/baz.txt")); - assert(!match("**/.txt", "/foo/bar/baz/qux.txt")); - assert( - !match( - "http://foo.com/*", - "http://foo.com/bar/baz/jquery.min.js", - ), - ); - assert( - !match("http://foo.com/*", "http://foo.com/bar/baz/jquery.min.js"), - ); - assert( - match("http://foo.com/**", "http://foo.com/bar/baz/jquery.min.js"), - ); - assert( - match( - "http://foo.com/**/jquery.min.js", - "http://foo.com/bar/baz/jquery.min.js", - ), - ); - assert( - !match( - "http://foo.com/*/jquery.min.js", - "http://foo.com/bar/baz/jquery.min.js", - ), - ); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() ?(pattern-list) (extended: match zero or one)", - fn(): void { - assert(match("?(foo).txt", "foo.txt")); - assert(!match("?(foo).txt", "foo.txt", { extended: false })); - assert(match("?(foo).txt", "a(foo).txt", { extended: false })); - assert(match("?(foo).txt", ".txt")); - assert(match("?(foo|bar)baz.txt", "foobaz.txt")); - assert(match("?(ba[zr]|qux)baz.txt", "bazbaz.txt")); - assert(match("?(ba[zr]|qux)baz.txt", "barbaz.txt")); - assert(match("?(ba[zr]|qux)baz.txt", "quxbaz.txt")); - assert(match("?(ba[!zr]|qux)baz.txt", "batbaz.txt")); - assert(match("?(ba*|qux)baz.txt", "batbaz.txt")); - assert(match("?(ba*|qux)baz.txt", "batttbaz.txt")); - assert(match("?(ba*|qux)baz.txt", "quxbaz.txt")); - assert(match("?(ba?(z|r)|qux)baz.txt", "bazbaz.txt")); - assert(match("?(ba?(z|?(r))|qux)baz.txt", "bazbaz.txt")); - assert(!match("?(foo|bar)baz.txt", "foobarbaz.txt")); - assert(!match("?(ba[zr]|qux)baz.txt", "bazquxbaz.txt")); - assert(!match("?(ba[!zr]|qux)baz.txt", "bazbaz.txt")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() *(pattern-list) (extended: match zero or more)", - fn(): void { - assert(match("*(foo).txt", "foo.txt")); - assert(!match("*(foo).txt", "foo.txt", { extended: false })); - assert(match("*(foo).txt", "bar(foo).txt", { extended: false })); - assert(match("*(foo).txt", "foofoo.txt")); - assert(match("*(foo).txt", ".txt")); - assert(match("*(fooo).txt", ".txt")); - assert(!match("*(fooo).txt", "foo.txt")); - assert(match("*(foo|bar).txt", "foobar.txt")); - assert(match("*(foo|bar).txt", "barbar.txt")); - assert(match("*(foo|bar).txt", "barfoobar.txt")); - assert(match("*(foo|bar).txt", ".txt")); - assert(match("*(foo|ba[rt]).txt", "bat.txt")); - assert(match("*(foo|b*[rt]).txt", "blat.txt")); - assert(!match("*(foo|b*[rt]).txt", "tlat.txt")); - assert(match("*(*).txt", "whatever.txt")); - assert(match("*(foo|bar)/**/*.txt", "foo/hello/world/bar.txt")); - assert(match("*(foo|bar)/**/*.txt", "foo/world/bar.txt")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() +(pattern-list) (extended: match 1 or more)", - fn(): void { - assert(match("+(foo).txt", "foo.txt")); - assert(!match("+(foo).txt", "foo.txt", { extended: false })); - assert(match("+(foo).txt", "+(foo).txt", { extended: false })); - assert(!match("+(foo).txt", ".txt")); - assert(match("+(foo|bar).txt", "foobar.txt")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() @(pattern-list) (extended: match one)", - fn(): void { - assert(match("@(foo).txt", "foo.txt")); - assert(!match("@(foo).txt", "foo.txt", { extended: false })); - assert(match("@(foo).txt", "@(foo).txt", { extended: false })); - assert(match("@(foo|baz)bar.txt", "foobar.txt")); - assert(!match("@(foo|baz)bar.txt", "foobazbar.txt")); - assert(!match("@(foo|baz)bar.txt", "foofoobar.txt")); - assert(!match("@(foo|baz)bar.txt", "toofoobar.txt")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() !(pattern-list) (extended: match any except)", - fn(): void { - assert(match("!(boo).txt", "foo.txt")); - assert(!match("!(boo).txt", "foo.txt", { extended: false })); - assert(match("!(boo).txt", "!(boo).txt", { extended: false })); - assert(match("!(foo|baz)bar.txt", "buzbar.txt")); - assert(match("!({foo,bar})baz.txt", "notbaz.txt")); - assert(!match("!({foo,bar})baz.txt", "foobaz.txt")); - }, -}); - -Deno.test({ - name: - "[path] globToRegExp() Special extended characters should match themselves", - fn(): void { - const glob = "\\/$^+.()=!|,.*"; - assert(match(glob, glob)); - assert(match(glob, glob, { extended: false })); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Special extended characters in range", - fn(): void { - assertEquals(globToRegExp("[?*+@!|]", { os: "linux" }), /^[?*+@!|]\/*$/); - assertEquals(globToRegExp("[!?*+@!|]", { os: "linux" }), /^[^?*+@!|]\/*$/); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Special RegExp characters in range", - fn(): void { - // Excluding characters checked in the previous test. - assertEquals(globToRegExp("[\\\\$^.=]", { os: "linux" }), /^[\\$^.=]\/*$/); - assertEquals( - globToRegExp("[!\\\\$^.=]", { os: "linux" }), - /^[^\\$^.=]\/*$/, - ); - assertEquals(globToRegExp("[^^]", { os: "linux" }), /^[\^^]\/*$/); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Repeating separators", - fn() { - assert(match("foo/bar", "foo//bar")); - assert(match("foo//bar", "foo/bar")); - assert(match("foo//bar", "foo//bar")); - assert(match("**/bar", "foo//bar")); - assert(match("**//bar", "foo/bar")); - assert(match("**//bar", "foo//bar")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Trailing separators", - fn() { - assert(match("foo", "foo/")); - assert(match("foo/", "foo")); - assert(match("foo/", "foo/")); - assert(match("**", "foo/")); - assert(match("**/", "foo")); - assert(match("**/", "foo/")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Backslashes on Windows", - fn() { - assert(match("foo/bar", "foo\\bar", { os: "windows" })); - assert(match("foo\\bar", "foo/bar", { os: "windows" })); - assert(match("foo\\bar", "foo\\bar", { os: "windows" })); - assert(match("**/bar", "foo\\bar", { os: "windows" })); - assert(match("**\\bar", "foo/bar", { os: "windows" })); - assert(match("**\\bar", "foo\\bar", { os: "windows" })); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Unclosed groups", - fn() { - assert(match("{foo,bar}/[ab", "foo/[ab")); - assert(match("{foo,bar}/{foo,bar", "foo/{foo,bar")); - assert(match("{foo,bar}/?(foo|bar", "foo/?(foo|bar")); - assert(match("{foo,bar}/@(foo|bar", "foo/@(foo|bar")); - assert(match("{foo,bar}/*(foo|bar", "foo/*(foo|bar")); - assert(match("{foo,bar}/+(foo|bar", "foo/+(foo|bar")); - assert(match("{foo,bar}/!(foo|bar", "foo/!(foo|bar")); - assert(match("{foo,bar}/?({)}", "foo/?({)}")); - assert(match("{foo,bar}/{?(})", "foo/{?(})")); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Escape glob characters", - fn() { - assert(match("\\[ab]", "[ab]", { os: "linux" })); - assert(match("`[ab]", "[ab]", { os: "windows" })); - assert(match("\\{foo,bar}", "{foo,bar}", { os: "linux" })); - assert(match("`{foo,bar}", "{foo,bar}", { os: "windows" })); - assert(match("\\?(foo|bar)", "?(foo|bar)", { os: "linux" })); - assert(match("`?(foo|bar)", "?(foo|bar)", { os: "windows" })); - assert(match("\\@(foo|bar)", "@(foo|bar)", { os: "linux" })); - assert(match("`@(foo|bar)", "@(foo|bar)", { os: "windows" })); - assert(match("\\*(foo|bar)", "*(foo|bar)", { os: "linux" })); - assert(match("`*(foo|bar)", "*(foo|bar)", { os: "windows" })); - assert(match("\\+(foo|bar)", "+(foo|bar)", { os: "linux" })); - assert(match("`+(foo|bar)", "+(foo|bar)", { os: "windows" })); - assert(match("\\!(foo|bar)", "!(foo|bar)", { os: "linux" })); - assert(match("`!(foo|bar)", "!(foo|bar)", { os: "windows" })); - assert(match("@\\(foo|bar)", "@(foo|bar)", { os: "linux" })); - assert(match("@`(foo|bar)", "@(foo|bar)", { os: "windows" })); - assert(match("{foo,bar}/[ab]\\", "foo/[ab]\\", { os: "linux" })); - assert(match("{foo,bar}/[ab]`", "foo/[ab]`", { os: "windows" })); - }, -}); - -Deno.test({ - name: "[path] globToRegExp() Dangling escape prefix", - fn() { - assert(match("{foo,bar}/[ab]\\", "foo/[ab]\\", { os: "linux" })); - assert(match("{foo,bar}/[ab]`", "foo/[ab]`", { os: "windows" })); - }, -}); - -Deno.test({ - name: "[path] GlobToRegExpOptions::extended", - fn() { - const pattern1 = globToRegExp("?(foo|bar)"); - assertEquals("foo".match(pattern1)?.[0], "foo"); - assertEquals("bar".match(pattern1)?.[0], "bar"); - - const pattern2 = globToRegExp("?(foo|bar)", { extended: false }); - assertEquals("foo".match(pattern2)?.[0], undefined); - assertEquals("bar".match(pattern2)?.[0], undefined); - assertEquals("?(foo|bar)".match(pattern2)?.[0], "?(foo|bar)"); - }, -}); - -Deno.test({ - name: "[path] GlobToRegExpOptions::globstar", - fn() { - const pattern1 = globToRegExp("**/foo"); - assertEquals("foo".match(pattern1)?.[0], "foo"); - assertEquals("path/to/foo".match(pattern1)?.[0], "path/to/foo"); - - const pattern2 = globToRegExp("**/foo", { globstar: false }); - assertEquals("foo".match(pattern2)?.[0], undefined); - assertEquals("path/to/foo".match(pattern2)?.[0], undefined); - assertEquals("path-to/foo".match(pattern2)?.[0], "path-to/foo"); - }, -}); - -Deno.test({ - name: "[path] GlobToRegExpOptions::os", - fn() { - const pattern1 = globToRegExp("foo/bar", { os: "linux" }); - assertEquals("foo/bar".match(pattern1)?.[0], "foo/bar"); - assertEquals("foo\\bar".match(pattern1)?.[0], undefined); - - const pattern2 = globToRegExp("foo/bar", { os: "windows" }); - assertEquals("foo/bar".match(pattern2)?.[0], "foo/bar"); - assertEquals("foo\\bar".match(pattern2)?.[0], "foo\\bar"); - }, -}); - -Deno.test({ - name: "[path] isGlob()", - fn(): void { - // should be true if valid glob pattern - assert(isGlob("!foo.js")); - assert(isGlob("*.js")); - assert(isGlob("!*.js")); - assert(isGlob("!foo")); - assert(isGlob("!foo.js")); - assert(isGlob("**/abc.js")); - assert(isGlob("abc/*.js")); - assert(isGlob("@.(?:abc)")); - assert(isGlob("@.(?!abc)")); - - // should be false if invalid glob pattern - assert(!isGlob("")); - assert(!isGlob("~/abc")); - assert(!isGlob("~/abc")); - assert(!isGlob("~/(abc)")); - assert(!isGlob("+~(abc)")); - assert(!isGlob(".")); - assert(!isGlob("@.(abc)")); - assert(!isGlob("aa")); - assert(!isGlob("who?")); - assert(!isGlob("why!?")); - assert(!isGlob("where???")); - assert(!isGlob("abc!/def/!ghi.js")); - assert(!isGlob("abc.js")); - assert(!isGlob("abc/def/!ghi.js")); - assert(!isGlob("abc/def/ghi.js")); - - // Should be true if path has regex capture group - assert(isGlob("abc/(?!foo).js")); - assert(isGlob("abc/(?:foo).js")); - assert(isGlob("abc/(?=foo).js")); - assert(isGlob("abc/(a|b).js")); - assert(isGlob("abc/(a|b|c).js")); - assert(isGlob("abc/(foo bar)/*.js")); - - // Should be false if the path has parens but is not a valid capture group - assert(!isGlob("abc/(?foo).js")); - assert(!isGlob("abc/(a b c).js")); - assert(!isGlob("abc/(ab).js")); - assert(!isGlob("abc/(abc).js")); - assert(!isGlob("abc/(foo bar).js")); - - // should be false if the capture group is imbalanced - assert(!isGlob("abc/(?ab.js")); - assert(!isGlob("abc/(ab.js")); - assert(!isGlob("abc/(a|b.js")); - assert(!isGlob("abc/(a|b|c.js")); - - // should be true if the path has a regex character class - assert(isGlob("abc/[abc].js")); - assert(isGlob("abc/[^abc].js")); - assert(isGlob("abc/[1-3].js")); - - // should be false if the character class is not balanced - assert(!isGlob("abc/[abc.js")); - assert(!isGlob("abc/[^abc.js")); - assert(!isGlob("abc/[1-3.js")); - - // should be false if the character class is escaped - assert(!isGlob("abc/\\[abc].js")); - assert(!isGlob("abc/\\[^abc].js")); - assert(!isGlob("abc/\\[1-3].js")); - - // should be true if the path has brace characters - assert(isGlob("abc/{a,b}.js")); - assert(isGlob("abc/{a..z}.js")); - assert(isGlob("abc/{a..z..2}.js")); - - // should be false if (basic) braces are not balanced - assert(!isGlob("abc/\\{a,b}.js")); - assert(!isGlob("abc/\\{a..z}.js")); - assert(!isGlob("abc/\\{a..z..2}.js")); - - // should be true if the path has regex characters - assert(isGlob("!&(abc)")); - assert(isGlob("!*.js")); - assert(isGlob("!foo")); - assert(isGlob("!foo.js")); - assert(isGlob("**/abc.js")); - assert(isGlob("*.js")); - assert(isGlob("*z(abc)")); - assert(isGlob("[1-10].js")); - assert(isGlob("[^abc].js")); - assert(isGlob("[a-j]*[^c]b/c")); - assert(isGlob("[abc].js")); - assert(isGlob("a/b/c/[a-z].js")); - assert(isGlob("abc/(aaa|bbb).js")); - assert(isGlob("abc/*.js")); - assert(isGlob("abc/{a,b}.js")); - assert(isGlob("abc/{a..z..2}.js")); - assert(isGlob("abc/{a..z}.js")); - - assert(!isGlob("$(abc)")); - assert(!isGlob("&(abc)")); - assert(!isGlob("Who?.js")); - assert(!isGlob("? (abc)")); - assert(!isGlob("?.js")); - assert(!isGlob("abc/?.js")); - - // should be false if regex characters are escaped - assert(!isGlob("\\?.js")); - assert(!isGlob("\\[1-10\\].js")); - assert(!isGlob("\\[^abc\\].js")); - assert(!isGlob("\\[a-j\\]\\*\\[^c\\]b/c")); - assert(!isGlob("\\[abc\\].js")); - assert(!isGlob("\\a/b/c/\\[a-z\\].js")); - assert(!isGlob("abc/\\(aaa|bbb).js")); - assert(!isGlob("abc/\\?.js")); - }, -}); - -Deno.test("[path] normalizeGlob() Globstar", function (): void { - assertEquals(normalizeGlob(`**${SEP}..`, { globstar: true }), `**${SEP}..`); -}); - -Deno.test("[path] joinGlobs() Globstar", function (): void { - assertEquals(joinGlobs(["**", ".."], { globstar: true }), `**${SEP}..`); -}); diff --git a/std/path/isabsolute_test.ts b/std/path/isabsolute_test.ts deleted file mode 100644 index 88ed544177..0000000000 --- a/std/path/isabsolute_test.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -Deno.test("isAbsolute", function () { - assertEquals(path.posix.isAbsolute("/home/foo"), true); - assertEquals(path.posix.isAbsolute("/home/foo/.."), true); - assertEquals(path.posix.isAbsolute("bar/"), false); - assertEquals(path.posix.isAbsolute("./baz"), false); -}); - -Deno.test("isAbsoluteWin32", function () { - assertEquals(path.win32.isAbsolute("/"), true); - assertEquals(path.win32.isAbsolute("//"), true); - assertEquals(path.win32.isAbsolute("//server"), true); - assertEquals(path.win32.isAbsolute("//server/file"), true); - assertEquals(path.win32.isAbsolute("\\\\server\\file"), true); - assertEquals(path.win32.isAbsolute("\\\\server"), true); - assertEquals(path.win32.isAbsolute("\\\\"), true); - assertEquals(path.win32.isAbsolute("c"), false); - assertEquals(path.win32.isAbsolute("c:"), false); - assertEquals(path.win32.isAbsolute("c:\\"), true); - assertEquals(path.win32.isAbsolute("c:/"), true); - assertEquals(path.win32.isAbsolute("c://"), true); - assertEquals(path.win32.isAbsolute("C:/Users/"), true); - assertEquals(path.win32.isAbsolute("C:\\Users\\"), true); - assertEquals(path.win32.isAbsolute("C:cwd/another"), false); - assertEquals(path.win32.isAbsolute("C:cwd\\another"), false); - assertEquals(path.win32.isAbsolute("directory/directory"), false); - assertEquals(path.win32.isAbsolute("directory\\directory"), false); -}); diff --git a/std/path/join_test.ts b/std/path/join_test.ts deleted file mode 100644 index 122376be10..0000000000 --- a/std/path/join_test.ts +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -const backslashRE = /\\/g; - -const joinTests = - // arguments result - [ - [[".", "x/b", "..", "/b/c.js"], "x/b/c.js"], - [[], "."], - [["/.", "x/b", "..", "/b/c.js"], "/x/b/c.js"], - [["/foo", "../../../bar"], "/bar"], - [["foo", "../../../bar"], "../../bar"], - [["foo/", "../../../bar"], "../../bar"], - [["foo/x", "../../../bar"], "../bar"], - [["foo/x", "./bar"], "foo/x/bar"], - [["foo/x/", "./bar"], "foo/x/bar"], - [["foo/x/", ".", "bar"], "foo/x/bar"], - [["./"], "./"], - [[".", "./"], "./"], - [[".", ".", "."], "."], - [[".", "./", "."], "."], - [[".", "/./", "."], "."], - [[".", "/////./", "."], "."], - [["."], "."], - [["", "."], "."], - [["", "foo"], "foo"], - [["foo", "/bar"], "foo/bar"], - [["", "/foo"], "/foo"], - [["", "", "/foo"], "/foo"], - [["", "", "foo"], "foo"], - [["foo", ""], "foo"], - [["foo/", ""], "foo/"], - [["foo", "", "/bar"], "foo/bar"], - [["./", "..", "/foo"], "../foo"], - [["./", "..", "..", "/foo"], "../../foo"], - [[".", "..", "..", "/foo"], "../../foo"], - [["", "..", "..", "/foo"], "../../foo"], - [["/"], "/"], - [["/", "."], "/"], - [["/", ".."], "/"], - [["/", "..", ".."], "/"], - [[""], "."], - [["", ""], "."], - [[" /foo"], " /foo"], - [[" ", "foo"], " /foo"], - [[" ", "."], " "], - [[" ", "/"], " /"], - [[" ", ""], " "], - [["/", "foo"], "/foo"], - [["/", "/foo"], "/foo"], - [["/", "//foo"], "/foo"], - [["/", "", "/foo"], "/foo"], - [["", "/", "foo"], "/foo"], - [["", "/", "/foo"], "/foo"], - ]; - -// Windows-specific join tests -const windowsJoinTests = [ - // arguments result - // UNC path expected - [["//foo/bar"], "\\\\foo\\bar\\"], - [["\\/foo/bar"], "\\\\foo\\bar\\"], - [["\\\\foo/bar"], "\\\\foo\\bar\\"], - // UNC path expected - server and share separate - [["//foo", "bar"], "\\\\foo\\bar\\"], - [["//foo/", "bar"], "\\\\foo\\bar\\"], - [["//foo", "/bar"], "\\\\foo\\bar\\"], - // UNC path expected - questionable - [["//foo", "", "bar"], "\\\\foo\\bar\\"], - [["//foo/", "", "bar"], "\\\\foo\\bar\\"], - [["//foo/", "", "/bar"], "\\\\foo\\bar\\"], - // UNC path expected - even more questionable - [["", "//foo", "bar"], "\\\\foo\\bar\\"], - [["", "//foo/", "bar"], "\\\\foo\\bar\\"], - [["", "//foo/", "/bar"], "\\\\foo\\bar\\"], - // No UNC path expected (no double slash in first component) - [["\\", "foo/bar"], "\\foo\\bar"], - [["\\", "/foo/bar"], "\\foo\\bar"], - [["", "/", "/foo/bar"], "\\foo\\bar"], - // No UNC path expected (no non-slashes in first component - - // questionable) - [["//", "foo/bar"], "\\foo\\bar"], - [["//", "/foo/bar"], "\\foo\\bar"], - [["\\\\", "/", "/foo/bar"], "\\foo\\bar"], - [["//"], "\\"], - // No UNC path expected (share name missing - questionable). - [["//foo"], "\\foo"], - [["//foo/"], "\\foo\\"], - [["//foo", "/"], "\\foo\\"], - [["//foo", "", "/"], "\\foo\\"], - // No UNC path expected (too many leading slashes - questionable) - [["///foo/bar"], "\\foo\\bar"], - [["////foo", "bar"], "\\foo\\bar"], - [["\\\\\\/foo/bar"], "\\foo\\bar"], - // Drive-relative vs drive-absolute paths. This merely describes the - // status quo, rather than being obviously right - [["c:"], "c:."], - [["c:."], "c:."], - [["c:", ""], "c:."], - [["", "c:"], "c:."], - [["c:.", "/"], "c:.\\"], - [["c:.", "file"], "c:file"], - [["c:", "/"], "c:\\"], - [["c:", "file"], "c:\\file"], -]; - -Deno.test("join", function () { - joinTests.forEach(function (p) { - const _p = p[0] as string[]; - const actual = path.posix.join.apply(null, _p); - assertEquals(actual, p[1]); - }); -}); - -Deno.test("joinWin32", function () { - joinTests.forEach(function (p) { - const _p = p[0] as string[]; - const actual = path.win32.join.apply(null, _p).replace(backslashRE, "/"); - assertEquals(actual, p[1]); - }); - windowsJoinTests.forEach(function (p) { - const _p = p[0] as string[]; - const actual = path.win32.join.apply(null, _p); - assertEquals(actual, p[1]); - }); -}); diff --git a/std/path/mod.ts b/std/path/mod.ts deleted file mode 100644 index 5fd793c75a..0000000000 --- a/std/path/mod.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported mostly from https://github.com/browserify/path-browserify/ -// This module is browser compatible. - -import { isWindows } from "../_util/os.ts"; -import * as _win32 from "./win32.ts"; -import * as _posix from "./posix.ts"; - -const path = isWindows ? _win32 : _posix; - -export const win32 = _win32; -export const posix = _posix; -export const { - basename, - delimiter, - dirname, - extname, - format, - fromFileUrl, - isAbsolute, - join, - normalize, - parse, - relative, - resolve, - sep, - toFileUrl, - toNamespacedPath, -} = path; - -export * from "./common.ts"; -export { SEP, SEP_PATTERN } from "./separator.ts"; -export * from "./_interface.ts"; -export * from "./glob.ts"; diff --git a/std/path/parse_format_test.ts b/std/path/parse_format_test.ts deleted file mode 100644 index 7855883592..0000000000 --- a/std/path/parse_format_test.ts +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import type { FormatInputPathObject, ParsedPath } from "./mod.ts"; - -import { assertEquals } from "../testing/asserts.ts"; -import { posix, win32 } from "./mod.ts"; - -type FormatTestCase = [FormatInputPathObject, string]; -type ParseTestCase = [string, ParsedPath]; - -const winPaths: Array<[string, string]> = [ - // [path, root] - ["C:\\path\\dir\\index.html", "C:\\"], - ["C:\\another_path\\DIR\\1\\2\\33\\\\index", "C:\\"], - ["another_path\\DIR with spaces\\1\\2\\33\\index", ""], - ["\\", "\\"], - ["\\foo\\C:", "\\"], - ["file", ""], - ["file:stream", ""], - [".\\file", ""], - ["C:", "C:"], - ["C:.", "C:"], - ["C:..", "C:"], - ["C:abc", "C:"], - ["C:\\", "C:\\"], - ["C:\\abc", "C:\\"], - ["", ""], - // unc - ["\\\\server\\share\\file_path", "\\\\server\\share\\"], - [ - "\\\\server two\\shared folder\\file path.zip", - "\\\\server two\\shared folder\\", - ], - ["\\\\teela\\admin$\\system32", "\\\\teela\\admin$\\"], - ["\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\"], -]; - -const winSpecialCaseParseTests: ParseTestCase[] = [ - ["/foo/bar", { root: "/", dir: "/foo", base: "bar", ext: "", name: "bar" }], -]; - -const winSpecialCaseFormatTests: FormatTestCase[] = [ - [{ dir: "some\\dir" }, "some\\dir\\"], - [{ base: "index.html" }, "index.html"], - [{ root: "C:\\" }, "C:\\"], - [{ name: "index", ext: ".html" }, "index.html"], - [{ dir: "some\\dir", name: "index", ext: ".html" }, "some\\dir\\index.html"], - [{ root: "C:\\", name: "index", ext: ".html" }, "C:\\index.html"], - [{}, ""], -]; - -const unixPaths: Array<[string, string]> = [ - // [path, root] - ["/home/user/dir/file.txt", "/"], - ["/home/user/a dir/another File.zip", "/"], - ["/home/user/a dir//another&File.", "/"], - ["/home/user/a$$$dir//another File.zip", "/"], - ["user/dir/another File.zip", ""], - ["file", ""], - [".\\file", ""], - ["./file", ""], - ["C:\\foo", ""], - ["/", "/"], - ["", ""], - [".", ""], - ["..", ""], - ["/foo", "/"], - ["/foo.", "/"], - ["/foo.bar", "/"], - ["/.", "/"], - ["/.foo", "/"], - ["/.foo.bar", "/"], - ["/foo/bar.baz", "/"], -]; - -const unixSpecialCaseFormatTests: FormatTestCase[] = [ - [{ dir: "some/dir" }, "some/dir/"], - [{ base: "index.html" }, "index.html"], - [{ root: "/" }, "/"], - [{ name: "index", ext: ".html" }, "index.html"], - [{ dir: "some/dir", name: "index", ext: ".html" }, "some/dir/index.html"], - [{ root: "/", name: "index", ext: ".html" }, "/index.html"], - [{}, ""], -]; - -function checkParseFormat( - path: typeof win32 | typeof posix, - testCases: Array<[string, string]>, -): void { - testCases.forEach(([element, root]) => { - const output = path.parse(element); - assertEquals(typeof output.root, "string"); - assertEquals(typeof output.dir, "string"); - assertEquals(typeof output.base, "string"); - assertEquals(typeof output.ext, "string"); - assertEquals(typeof output.name, "string"); - assertEquals(path.format(output), element); - assertEquals(output.root, root); - assertEquals(output.dir, output.dir ? path.dirname(element) : ""); - assertEquals(output.base, path.basename(element)); - assertEquals(output.ext, path.extname(element)); - }); -} - -function checkSpecialCaseParseFormat( - path: typeof win32 | typeof posix, - testCases: ParseTestCase[], -): void { - testCases.forEach(([element, expect]) => { - assertEquals(path.parse(element), expect); - }); -} - -function checkFormat( - path: typeof win32 | typeof posix, - testCases: FormatTestCase[], -): void { - testCases.forEach((testCase) => { - assertEquals(path.format(testCase[0]), testCase[1]); - }); -} - -Deno.test("parseWin32", function () { - checkParseFormat(win32, winPaths); - checkSpecialCaseParseFormat(win32, winSpecialCaseParseTests); -}); - -Deno.test("parse", function () { - checkParseFormat(posix, unixPaths); -}); - -Deno.test("formatWin32", function () { - checkFormat(win32, winSpecialCaseFormatTests); -}); - -Deno.test("format", function () { - checkFormat(posix, unixSpecialCaseFormatTests); -}); - -// Test removal of trailing path separators -const windowsTrailingTests: ParseTestCase[] = [ - [".\\", { root: "", dir: "", base: ".", ext: "", name: "." }], - ["\\\\", { root: "\\", dir: "\\", base: "", ext: "", name: "" }], - ["\\\\", { root: "\\", dir: "\\", base: "", ext: "", name: "" }], - [ - "c:\\foo\\\\\\", - { root: "c:\\", dir: "c:\\", base: "foo", ext: "", name: "foo" }, - ], - [ - "D:\\foo\\\\\\bar.baz", - { - root: "D:\\", - dir: "D:\\foo\\\\", - base: "bar.baz", - ext: ".baz", - name: "bar", - }, - ], -]; - -const posixTrailingTests: ParseTestCase[] = [ - ["./", { root: "", dir: "", base: ".", ext: "", name: "." }], - ["//", { root: "/", dir: "/", base: "", ext: "", name: "" }], - ["///", { root: "/", dir: "/", base: "", ext: "", name: "" }], - ["/foo///", { root: "/", dir: "/", base: "foo", ext: "", name: "foo" }], - [ - "/foo///bar.baz", - { root: "/", dir: "/foo//", base: "bar.baz", ext: ".baz", name: "bar" }, - ], -]; - -Deno.test("parseTrailingWin32", function () { - windowsTrailingTests.forEach(function (p) { - const actual = win32.parse(p[0]); - const expected = p[1]; - assertEquals(actual, expected); - }); -}); - -Deno.test("parseTrailing", function () { - posixTrailingTests.forEach(function (p) { - const actual = posix.parse(p[0]); - const expected = p[1]; - assertEquals(actual, expected); - }); -}); diff --git a/std/path/posix.ts b/std/path/posix.ts deleted file mode 100644 index ed3649bd35..0000000000 --- a/std/path/posix.ts +++ /dev/null @@ -1,504 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -// This module is browser compatible. - -import type { FormatInputPathObject, ParsedPath } from "./_interface.ts"; -import { CHAR_DOT, CHAR_FORWARD_SLASH } from "./_constants.ts"; - -import { - _format, - assertPath, - isPosixPathSeparator, - normalizeString, -} from "./_util.ts"; - -export const sep = "/"; -export const delimiter = ":"; - -// path.resolve([from ...], to) -/** - * Resolves `pathSegments` into an absolute path. - * @param pathSegments an array of path segments - */ -export function resolve(...pathSegments: string[]): string { - let resolvedPath = ""; - let resolvedAbsolute = false; - - for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) { - let path: string; - - if (i >= 0) path = pathSegments[i]; - else { - if (globalThis.Deno == null) { - throw new TypeError("Resolved a relative path without a CWD."); - } - path = Deno.cwd(); - } - - assertPath(path); - - // Skip empty entries - if (path.length === 0) { - continue; - } - - resolvedPath = `${path}/${resolvedPath}`; - resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; - } - - // At this point the path should be resolved to a full absolute path, but - // handle relative paths to be safe (might happen when process.cwd() fails) - - // Normalize the path - resolvedPath = normalizeString( - resolvedPath, - !resolvedAbsolute, - "/", - isPosixPathSeparator, - ); - - if (resolvedAbsolute) { - if (resolvedPath.length > 0) return `/${resolvedPath}`; - else return "/"; - } else if (resolvedPath.length > 0) return resolvedPath; - else return "."; -} - -/** - * Normalize the `path`, resolving `'..'` and `'.'` segments. - * @param path to be normalized - */ -export function normalize(path: string): string { - assertPath(path); - - if (path.length === 0) return "."; - - const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; - const trailingSeparator = - path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH; - - // Normalize the path - path = normalizeString(path, !isAbsolute, "/", isPosixPathSeparator); - - if (path.length === 0 && !isAbsolute) path = "."; - if (path.length > 0 && trailingSeparator) path += "/"; - - if (isAbsolute) return `/${path}`; - return path; -} - -/** - * Verifies whether provided path is absolute - * @param path to be verified as absolute - */ -export function isAbsolute(path: string): boolean { - assertPath(path); - return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH; -} - -/** - * Join all given a sequence of `paths`,then normalizes the resulting path. - * @param paths to be joined and normalized - */ -export function join(...paths: string[]): string { - if (paths.length === 0) return "."; - let joined: string | undefined; - for (let i = 0, len = paths.length; i < len; ++i) { - const path = paths[i]; - assertPath(path); - if (path.length > 0) { - if (!joined) joined = path; - else joined += `/${path}`; - } - } - if (!joined) return "."; - return normalize(joined); -} - -/** - * Return the relative path from `from` to `to` based on current working directory. - * @param from path in current working directory - * @param to path in current working directory - */ -export function relative(from: string, to: string): string { - assertPath(from); - assertPath(to); - - if (from === to) return ""; - - from = resolve(from); - to = resolve(to); - - if (from === to) return ""; - - // Trim any leading backslashes - let fromStart = 1; - const fromEnd = from.length; - for (; fromStart < fromEnd; ++fromStart) { - if (from.charCodeAt(fromStart) !== CHAR_FORWARD_SLASH) break; - } - const fromLen = fromEnd - fromStart; - - // Trim any leading backslashes - let toStart = 1; - const toEnd = to.length; - for (; toStart < toEnd; ++toStart) { - if (to.charCodeAt(toStart) !== CHAR_FORWARD_SLASH) break; - } - const toLen = toEnd - toStart; - - // Compare paths to find the longest common path from root - const length = fromLen < toLen ? fromLen : toLen; - let lastCommonSep = -1; - let i = 0; - for (; i <= length; ++i) { - if (i === length) { - if (toLen > length) { - if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { - // We get here if `from` is the exact base path for `to`. - // For example: from='/foo/bar'; to='/foo/bar/baz' - return to.slice(toStart + i + 1); - } else if (i === 0) { - // We get here if `from` is the root - // For example: from='/'; to='/foo' - return to.slice(toStart + i); - } - } else if (fromLen > length) { - if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { - // We get here if `to` is the exact base path for `from`. - // For example: from='/foo/bar/baz'; to='/foo/bar' - lastCommonSep = i; - } else if (i === 0) { - // We get here if `to` is the root. - // For example: from='/foo'; to='/' - lastCommonSep = 0; - } - } - break; - } - const fromCode = from.charCodeAt(fromStart + i); - const toCode = to.charCodeAt(toStart + i); - if (fromCode !== toCode) break; - else if (fromCode === CHAR_FORWARD_SLASH) lastCommonSep = i; - } - - let out = ""; - // Generate the relative path based on the path difference between `to` - // and `from` - for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { - if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) { - if (out.length === 0) out += ".."; - else out += "/.."; - } - } - - // Lastly, append the rest of the destination (`to`) path that comes after - // the common path parts - if (out.length > 0) return out + to.slice(toStart + lastCommonSep); - else { - toStart += lastCommonSep; - if (to.charCodeAt(toStart) === CHAR_FORWARD_SLASH) ++toStart; - return to.slice(toStart); - } -} - -/** - * Resolves path to a namespace path - * @param path to resolve to namespace - */ -export function toNamespacedPath(path: string): string { - // Non-op on posix systems - return path; -} - -/** - * Return the directory name of a `path`. - * @param path to determine name for - */ -export function dirname(path: string): string { - assertPath(path); - if (path.length === 0) return "."; - const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH; - let end = -1; - let matchedSlash = true; - for (let i = path.length - 1; i >= 1; --i) { - if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { - if (!matchedSlash) { - end = i; - break; - } - } else { - // We saw the first non-path separator - matchedSlash = false; - } - } - - if (end === -1) return hasRoot ? "/" : "."; - if (hasRoot && end === 1) return "//"; - return path.slice(0, end); -} - -/** - * Return the last portion of a `path`. Trailing directory separators are ignored. - * @param path to process - * @param ext of path directory - */ -export function basename(path: string, ext = ""): string { - if (ext !== undefined && typeof ext !== "string") { - throw new TypeError('"ext" argument must be a string'); - } - assertPath(path); - - let start = 0; - let end = -1; - let matchedSlash = true; - let i: number; - - if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { - if (ext.length === path.length && ext === path) return ""; - let extIdx = ext.length - 1; - let firstNonSlashEnd = -1; - for (i = path.length - 1; i >= 0; --i) { - const code = path.charCodeAt(i); - if (code === CHAR_FORWARD_SLASH) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - start = i + 1; - break; - } - } else { - if (firstNonSlashEnd === -1) { - // We saw the first non-path separator, remember this index in case - // we need it if the extension ends up not matching - matchedSlash = false; - firstNonSlashEnd = i + 1; - } - if (extIdx >= 0) { - // Try to match the explicit extension - if (code === ext.charCodeAt(extIdx)) { - if (--extIdx === -1) { - // We matched the extension, so mark this as the end of our path - // component - end = i; - } - } else { - // Extension does not match, so our result is the entire path - // component - extIdx = -1; - end = firstNonSlashEnd; - } - } - } - } - - if (start === end) end = firstNonSlashEnd; - else if (end === -1) end = path.length; - return path.slice(start, end); - } else { - for (i = path.length - 1; i >= 0; --i) { - if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - start = i + 1; - break; - } - } else if (end === -1) { - // We saw the first non-path separator, mark this as the end of our - // path component - matchedSlash = false; - end = i + 1; - } - } - - if (end === -1) return ""; - return path.slice(start, end); - } -} - -/** - * Return the extension of the `path`. - * @param path with extension - */ -export function extname(path: string): string { - assertPath(path); - let startDot = -1; - let startPart = 0; - let end = -1; - let matchedSlash = true; - // Track the state of characters (if any) we see before our first dot and - // after any path separator we find - let preDotState = 0; - for (let i = path.length - 1; i >= 0; --i) { - const code = path.charCodeAt(i); - if (code === CHAR_FORWARD_SLASH) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - startPart = i + 1; - break; - } - continue; - } - if (end === -1) { - // We saw the first non-path separator, mark this as the end of our - // extension - matchedSlash = false; - end = i + 1; - } - if (code === CHAR_DOT) { - // If this is our first dot, mark it as the start of our extension - if (startDot === -1) startDot = i; - else if (preDotState !== 1) preDotState = 1; - } else if (startDot !== -1) { - // We saw a non-dot and non-path separator before our dot, so we should - // have a good chance at having a non-empty extension - preDotState = -1; - } - } - - if ( - startDot === -1 || - end === -1 || - // We saw a non-dot character immediately before the dot - preDotState === 0 || - // The (right-most) trimmed path component is exactly '..' - (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) - ) { - return ""; - } - return path.slice(startDot, end); -} - -/** - * Generate a path from `FormatInputPathObject` object. - * @param pathObject with path - */ -export function format(pathObject: FormatInputPathObject): string { - if (pathObject === null || typeof pathObject !== "object") { - throw new TypeError( - `The "pathObject" argument must be of type Object. Received type ${typeof pathObject}`, - ); - } - return _format("/", pathObject); -} - -/** - * Return a `ParsedPath` object of the `path`. - * @param path to process - */ -export function parse(path: string): ParsedPath { - assertPath(path); - - const ret: ParsedPath = { root: "", dir: "", base: "", ext: "", name: "" }; - if (path.length === 0) return ret; - const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; - let start: number; - if (isAbsolute) { - ret.root = "/"; - start = 1; - } else { - start = 0; - } - let startDot = -1; - let startPart = 0; - let end = -1; - let matchedSlash = true; - let i = path.length - 1; - - // Track the state of characters (if any) we see before our first dot and - // after any path separator we find - let preDotState = 0; - - // Get non-dir info - for (; i >= start; --i) { - const code = path.charCodeAt(i); - if (code === CHAR_FORWARD_SLASH) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - startPart = i + 1; - break; - } - continue; - } - if (end === -1) { - // We saw the first non-path separator, mark this as the end of our - // extension - matchedSlash = false; - end = i + 1; - } - if (code === CHAR_DOT) { - // If this is our first dot, mark it as the start of our extension - if (startDot === -1) startDot = i; - else if (preDotState !== 1) preDotState = 1; - } else if (startDot !== -1) { - // We saw a non-dot and non-path separator before our dot, so we should - // have a good chance at having a non-empty extension - preDotState = -1; - } - } - - if ( - startDot === -1 || - end === -1 || - // We saw a non-dot character immediately before the dot - preDotState === 0 || - // The (right-most) trimmed path component is exactly '..' - (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) - ) { - if (end !== -1) { - if (startPart === 0 && isAbsolute) { - ret.base = ret.name = path.slice(1, end); - } else { - ret.base = ret.name = path.slice(startPart, end); - } - } - } else { - if (startPart === 0 && isAbsolute) { - ret.name = path.slice(1, startDot); - ret.base = path.slice(1, end); - } else { - ret.name = path.slice(startPart, startDot); - ret.base = path.slice(startPart, end); - } - ret.ext = path.slice(startDot, end); - } - - if (startPart > 0) ret.dir = path.slice(0, startPart - 1); - else if (isAbsolute) ret.dir = "/"; - - return ret; -} - -/** - * Converts a file URL to a path string. - * - * fromFileUrl("file:///home/foo"); // "/home/foo" - * @param url of a file URL - */ -export function fromFileUrl(url: string | URL): string { - url = url instanceof URL ? url : new URL(url); - if (url.protocol != "file:") { - throw new TypeError("Must be a file URL."); - } - return decodeURIComponent( - url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25"), - ); -} - -/** - * Converts a path string to a file URL. - * - * toFileUrl("/home/foo"); // new URL("file:///home/foo") - * @param path to convert to file URL - */ -export function toFileUrl(path: string): URL { - if (!isAbsolute(path)) { - throw new TypeError("Must be an absolute path."); - } - const url = new URL("file:///"); - url.pathname = path.replace(/%/g, "%25").replace(/\\/g, "%5C"); - return url; -} diff --git a/std/path/relative_test.ts b/std/path/relative_test.ts deleted file mode 100644 index e00e16d732..0000000000 --- a/std/path/relative_test.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -const relativeTests = { - // arguments result - win32: [ - ["c:/blah\\blah", "d:/games", "d:\\games"], - ["c:/aaaa/bbbb", "c:/aaaa", ".."], - ["c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"], - ["c:/aaaa/bbbb", "c:/aaaa/bbbb", ""], - ["c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"], - ["c:/aaaa/", "c:/aaaa/cccc", "cccc"], - ["c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"], - ["c:/aaaa/bbbb", "d:\\", "d:\\"], - ["c:/AaAa/bbbb", "c:/aaaa/bbbb", ""], - ["c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"], - ["C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."], - ["C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"], - ["C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"], - ["C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"], - ["\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"], - ["\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."], - ["\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"], - ["\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"], - ["C:\\baz-quux", "C:\\baz", "..\\baz"], - ["C:\\baz", "C:\\baz-quux", "..\\baz-quux"], - ["\\\\foo\\baz-quux", "\\\\foo\\baz", "..\\baz"], - ["\\\\foo\\baz", "\\\\foo\\baz-quux", "..\\baz-quux"], - ["C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"], - ["\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"], - ], - // arguments result - posix: [ - ["/var/lib", "/var", ".."], - ["/var/lib", "/bin", "../../bin"], - ["/var/lib", "/var/lib", ""], - ["/var/lib", "/var/apache", "../apache"], - ["/var/", "/var/lib", "lib"], - ["/", "/var/lib", "var/lib"], - ["/foo/test", "/foo/test/bar/package.json", "bar/package.json"], - ["/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."], - ["/foo/bar/baz-quux", "/foo/bar/baz", "../baz"], - ["/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"], - ["/baz-quux", "/baz", "../baz"], - ["/baz", "/baz-quux", "../baz-quux"], - ], -}; - -Deno.test("relative", function () { - relativeTests.posix.forEach(function (p) { - const expected = p[2]; - const actual = path.posix.relative(p[0], p[1]); - assertEquals(actual, expected); - }); -}); - -Deno.test("relativeWin32", function () { - relativeTests.win32.forEach(function (p) { - const expected = p[2]; - const actual = path.win32.relative(p[0], p[1]); - assertEquals(actual, expected); - }); -}); diff --git a/std/path/resolve_test.ts b/std/path/resolve_test.ts deleted file mode 100644 index dec032f477..0000000000 --- a/std/path/resolve_test.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -const windowsTests = - // arguments result - [ - [["c:/blah\\blah", "d:/games", "c:../a"], "c:\\blah\\a"], - [["c:/ignore", "d:\\a/b\\c/d", "\\e.exe"], "d:\\e.exe"], - [["c:/ignore", "c:/some/file"], "c:\\some\\file"], - [["d:/ignore", "d:some/dir//"], "d:\\ignore\\some\\dir"], - [["//server/share", "..", "relative\\"], "\\\\server\\share\\relative"], - [["c:/", "//"], "c:\\"], - [["c:/", "//dir"], "c:\\dir"], - [["c:/", "//server/share"], "\\\\server\\share\\"], - [["c:/", "//server//share"], "\\\\server\\share\\"], - [["c:/", "///some//dir"], "c:\\some\\dir"], - [ - ["C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js"], - "C:\\foo\\tmp.3\\cycles\\root.js", - ], - ]; -const posixTests = - // arguments result - [ - [["/var/lib", "../", "file/"], "/var/file"], - [["/var/lib", "/../", "file/"], "/file"], - [["a/b/c/", "../../.."], Deno.cwd()], - [["."], Deno.cwd()], - [["/some/dir", ".", "/absolute/"], "/absolute"], - [["/foo/tmp.3/", "../tmp.3/cycles/root.js"], "/foo/tmp.3/cycles/root.js"], - ]; - -Deno.test("resolve", function () { - posixTests.forEach(function (p) { - const _p = p[0] as string[]; - const actual = path.posix.resolve.apply(null, _p); - assertEquals(actual, p[1]); - }); -}); - -Deno.test("resolveWin32", function () { - windowsTests.forEach(function (p) { - const _p = p[0] as string[]; - const actual = path.win32.resolve.apply(null, _p); - assertEquals(actual, p[1]); - }); -}); diff --git a/std/path/separator.ts b/std/path/separator.ts deleted file mode 100644 index d897e07e68..0000000000 --- a/std/path/separator.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. - -import { isWindows } from "../_util/os.ts"; - -export const SEP = isWindows ? "\\" : "/"; -export const SEP_PATTERN = isWindows ? /[\\/]+/ : /\/+/; diff --git a/std/path/test.ts b/std/path/test.ts deleted file mode 100644 index 590417055f..0000000000 --- a/std/path/test.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import "./mod.ts"; diff --git a/std/path/to_file_url_test.ts b/std/path/to_file_url_test.ts deleted file mode 100644 index c4ee1a2368..0000000000 --- a/std/path/to_file_url_test.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { posix, win32 } from "./mod.ts"; -import { assertEquals, assertThrows } from "../testing/asserts.ts"; - -Deno.test("[path] toFileUrl", function () { - assertEquals(posix.toFileUrl("/home/foo").href, "file:///home/foo"); - assertEquals(posix.toFileUrl("/home/ ").href, "file:///home/%20"); - assertEquals(posix.toFileUrl("/home/%20").href, "file:///home/%2520"); - assertEquals(posix.toFileUrl("/home\\foo").href, "file:///home%5Cfoo"); - assertThrows( - () => posix.toFileUrl("foo").href, - TypeError, - "Must be an absolute path.", - ); - assertThrows( - () => posix.toFileUrl("C:/"), - TypeError, - "Must be an absolute path.", - ); - assertEquals( - posix.toFileUrl("//localhost/home/foo").href, - "file:////localhost/home/foo", - ); - assertEquals(posix.toFileUrl("//localhost/").href, "file:////localhost/"); - assertEquals(posix.toFileUrl("//:/home/foo").href, "file:////:/home/foo"); -}); - -Deno.test("[path] toFileUrl (win32)", function () { - assertEquals(win32.toFileUrl("/home/foo").href, "file:///home/foo"); - assertEquals(win32.toFileUrl("/home/ ").href, "file:///home/%20"); - assertEquals(win32.toFileUrl("/home/%20").href, "file:///home/%2520"); - assertEquals(win32.toFileUrl("/home\\foo").href, "file:///home/foo"); - assertThrows( - () => win32.toFileUrl("foo").href, - TypeError, - "Must be an absolute path.", - ); - assertEquals(win32.toFileUrl("C:/").href, "file:///C:/"); - assertEquals( - win32.toFileUrl("//localhost/home/foo").href, - "file://localhost/home/foo", - ); - assertEquals(win32.toFileUrl("//localhost/").href, "file:////localhost/"); - assertThrows( - () => win32.toFileUrl("//:/home/foo").href, - TypeError, - "Invalid hostname.", - ); -}); diff --git a/std/path/win32.ts b/std/path/win32.ts deleted file mode 100644 index d456c68e44..0000000000 --- a/std/path/win32.ts +++ /dev/null @@ -1,1001 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -// This module is browser compatible. - -import type { FormatInputPathObject, ParsedPath } from "./_interface.ts"; -import { - CHAR_BACKWARD_SLASH, - CHAR_COLON, - CHAR_DOT, - CHAR_QUESTION_MARK, -} from "./_constants.ts"; - -import { - _format, - assertPath, - isPathSeparator, - isWindowsDeviceRoot, - normalizeString, -} from "./_util.ts"; -import { assert } from "../_util/assert.ts"; - -export const sep = "\\"; -export const delimiter = ";"; - -/** - * Resolves path segments into a `path` - * @param pathSegments to process to path - */ -export function resolve(...pathSegments: string[]): string { - let resolvedDevice = ""; - let resolvedTail = ""; - let resolvedAbsolute = false; - - for (let i = pathSegments.length - 1; i >= -1; i--) { - let path: string; - if (i >= 0) { - path = pathSegments[i]; - } else if (!resolvedDevice) { - if (globalThis.Deno == null) { - throw new TypeError("Resolved a drive-letter-less path without a CWD."); - } - path = Deno.cwd(); - } else { - if (globalThis.Deno == null) { - throw new TypeError("Resolved a relative path without a CWD."); - } - // Windows has the concept of drive-specific current working - // directories. If we've resolved a drive letter but not yet an - // absolute path, get cwd for that drive, or the process cwd if - // the drive cwd is not available. We're sure the device is not - // a UNC path at this points, because UNC paths are always absolute. - path = Deno.env.get(`=${resolvedDevice}`) || Deno.cwd(); - - // Verify that a cwd was found and that it actually points - // to our drive. If not, default to the drive's root. - if ( - path === undefined || - path.slice(0, 3).toLowerCase() !== `${resolvedDevice.toLowerCase()}\\` - ) { - path = `${resolvedDevice}\\`; - } - } - - assertPath(path); - - const len = path.length; - - // Skip empty entries - if (len === 0) continue; - - let rootEnd = 0; - let device = ""; - let isAbsolute = false; - const code = path.charCodeAt(0); - - // Try to match a root - if (len > 1) { - if (isPathSeparator(code)) { - // Possible UNC root - - // If we started with a separator, we know we at least have an - // absolute path of some kind (UNC or otherwise) - isAbsolute = true; - - if (isPathSeparator(path.charCodeAt(1))) { - // Matched double path separator at beginning - let j = 2; - let last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - const firstPart = path.slice(last, j); - // Matched! - last = j; - // Match 1 or more path separators - for (; j < len; ++j) { - if (!isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j === len) { - // We matched a UNC root only - device = `\\\\${firstPart}\\${path.slice(last)}`; - rootEnd = j; - } else if (j !== last) { - // We matched a UNC root with leftovers - - device = `\\\\${firstPart}\\${path.slice(last, j)}`; - rootEnd = j; - } - } - } - } else { - rootEnd = 1; - } - } else if (isWindowsDeviceRoot(code)) { - // Possible device root - - if (path.charCodeAt(1) === CHAR_COLON) { - device = path.slice(0, 2); - rootEnd = 2; - if (len > 2) { - if (isPathSeparator(path.charCodeAt(2))) { - // Treat separator following drive name as an absolute path - // indicator - isAbsolute = true; - rootEnd = 3; - } - } - } - } - } else if (isPathSeparator(code)) { - // `path` contains just a path separator - rootEnd = 1; - isAbsolute = true; - } - - if ( - device.length > 0 && - resolvedDevice.length > 0 && - device.toLowerCase() !== resolvedDevice.toLowerCase() - ) { - // This path points to another device so it is not applicable - continue; - } - - if (resolvedDevice.length === 0 && device.length > 0) { - resolvedDevice = device; - } - if (!resolvedAbsolute) { - resolvedTail = `${path.slice(rootEnd)}\\${resolvedTail}`; - resolvedAbsolute = isAbsolute; - } - - if (resolvedAbsolute && resolvedDevice.length > 0) break; - } - - // At this point the path should be resolved to a full absolute path, - // but handle relative paths to be safe (might happen when process.cwd() - // fails) - - // Normalize the tail path - resolvedTail = normalizeString( - resolvedTail, - !resolvedAbsolute, - "\\", - isPathSeparator, - ); - - return resolvedDevice + (resolvedAbsolute ? "\\" : "") + resolvedTail || "."; -} - -/** - * Normalizes a `path` - * @param path to normalize - */ -export function normalize(path: string): string { - assertPath(path); - const len = path.length; - if (len === 0) return "."; - let rootEnd = 0; - let device: string | undefined; - let isAbsolute = false; - const code = path.charCodeAt(0); - - // Try to match a root - if (len > 1) { - if (isPathSeparator(code)) { - // Possible UNC root - - // If we started with a separator, we know we at least have an absolute - // path of some kind (UNC or otherwise) - isAbsolute = true; - - if (isPathSeparator(path.charCodeAt(1))) { - // Matched double path separator at beginning - let j = 2; - let last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - const firstPart = path.slice(last, j); - // Matched! - last = j; - // Match 1 or more path separators - for (; j < len; ++j) { - if (!isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j === len) { - // We matched a UNC root only - // Return the normalized version of the UNC root since there - // is nothing left to process - - return `\\\\${firstPart}\\${path.slice(last)}\\`; - } else if (j !== last) { - // We matched a UNC root with leftovers - - device = `\\\\${firstPart}\\${path.slice(last, j)}`; - rootEnd = j; - } - } - } - } else { - rootEnd = 1; - } - } else if (isWindowsDeviceRoot(code)) { - // Possible device root - - if (path.charCodeAt(1) === CHAR_COLON) { - device = path.slice(0, 2); - rootEnd = 2; - if (len > 2) { - if (isPathSeparator(path.charCodeAt(2))) { - // Treat separator following drive name as an absolute path - // indicator - isAbsolute = true; - rootEnd = 3; - } - } - } - } - } else if (isPathSeparator(code)) { - // `path` contains just a path separator, exit early to avoid unnecessary - // work - return "\\"; - } - - let tail: string; - if (rootEnd < len) { - tail = normalizeString( - path.slice(rootEnd), - !isAbsolute, - "\\", - isPathSeparator, - ); - } else { - tail = ""; - } - if (tail.length === 0 && !isAbsolute) tail = "."; - if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) { - tail += "\\"; - } - if (device === undefined) { - if (isAbsolute) { - if (tail.length > 0) return `\\${tail}`; - else return "\\"; - } else if (tail.length > 0) { - return tail; - } else { - return ""; - } - } else if (isAbsolute) { - if (tail.length > 0) return `${device}\\${tail}`; - else return `${device}\\`; - } else if (tail.length > 0) { - return device + tail; - } else { - return device; - } -} - -/** - * Verifies whether path is absolute - * @param path to verify - */ -export function isAbsolute(path: string): boolean { - assertPath(path); - const len = path.length; - if (len === 0) return false; - - const code = path.charCodeAt(0); - if (isPathSeparator(code)) { - return true; - } else if (isWindowsDeviceRoot(code)) { - // Possible device root - - if (len > 2 && path.charCodeAt(1) === CHAR_COLON) { - if (isPathSeparator(path.charCodeAt(2))) return true; - } - } - return false; -} - -/** - * Join all given a sequence of `paths`,then normalizes the resulting path. - * @param paths to be joined and normalized - */ -export function join(...paths: string[]): string { - const pathsCount = paths.length; - if (pathsCount === 0) return "."; - - let joined: string | undefined; - let firstPart: string | null = null; - for (let i = 0; i < pathsCount; ++i) { - const path = paths[i]; - assertPath(path); - if (path.length > 0) { - if (joined === undefined) joined = firstPart = path; - else joined += `\\${path}`; - } - } - - if (joined === undefined) return "."; - - // Make sure that the joined path doesn't start with two slashes, because - // normalize() will mistake it for an UNC path then. - // - // This step is skipped when it is very clear that the user actually - // intended to point at an UNC path. This is assumed when the first - // non-empty string arguments starts with exactly two slashes followed by - // at least one more non-slash character. - // - // Note that for normalize() to treat a path as an UNC path it needs to - // have at least 2 components, so we don't filter for that here. - // This means that the user can use join to construct UNC paths from - // a server name and a share name; for example: - // path.join('//server', 'share') -> '\\\\server\\share\\') - let needsReplace = true; - let slashCount = 0; - assert(firstPart != null); - if (isPathSeparator(firstPart.charCodeAt(0))) { - ++slashCount; - const firstLen = firstPart.length; - if (firstLen > 1) { - if (isPathSeparator(firstPart.charCodeAt(1))) { - ++slashCount; - if (firstLen > 2) { - if (isPathSeparator(firstPart.charCodeAt(2))) ++slashCount; - else { - // We matched a UNC path in the first part - needsReplace = false; - } - } - } - } - } - if (needsReplace) { - // Find any more consecutive slashes we need to replace - for (; slashCount < joined.length; ++slashCount) { - if (!isPathSeparator(joined.charCodeAt(slashCount))) break; - } - - // Replace the slashes if needed - if (slashCount >= 2) joined = `\\${joined.slice(slashCount)}`; - } - - return normalize(joined); -} - -/** - * It will solve the relative path from `from` to `to`, for instance: - * from = 'C:\\orandea\\test\\aaa' - * to = 'C:\\orandea\\impl\\bbb' - * The output of the function should be: '..\\..\\impl\\bbb' - * @param from relative path - * @param to relative path - */ -export function relative(from: string, to: string): string { - assertPath(from); - assertPath(to); - - if (from === to) return ""; - - const fromOrig = resolve(from); - const toOrig = resolve(to); - - if (fromOrig === toOrig) return ""; - - from = fromOrig.toLowerCase(); - to = toOrig.toLowerCase(); - - if (from === to) return ""; - - // Trim any leading backslashes - let fromStart = 0; - let fromEnd = from.length; - for (; fromStart < fromEnd; ++fromStart) { - if (from.charCodeAt(fromStart) !== CHAR_BACKWARD_SLASH) break; - } - // Trim trailing backslashes (applicable to UNC paths only) - for (; fromEnd - 1 > fromStart; --fromEnd) { - if (from.charCodeAt(fromEnd - 1) !== CHAR_BACKWARD_SLASH) break; - } - const fromLen = fromEnd - fromStart; - - // Trim any leading backslashes - let toStart = 0; - let toEnd = to.length; - for (; toStart < toEnd; ++toStart) { - if (to.charCodeAt(toStart) !== CHAR_BACKWARD_SLASH) break; - } - // Trim trailing backslashes (applicable to UNC paths only) - for (; toEnd - 1 > toStart; --toEnd) { - if (to.charCodeAt(toEnd - 1) !== CHAR_BACKWARD_SLASH) break; - } - const toLen = toEnd - toStart; - - // Compare paths to find the longest common path from root - const length = fromLen < toLen ? fromLen : toLen; - let lastCommonSep = -1; - let i = 0; - for (; i <= length; ++i) { - if (i === length) { - if (toLen > length) { - if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) { - // We get here if `from` is the exact base path for `to`. - // For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz' - return toOrig.slice(toStart + i + 1); - } else if (i === 2) { - // We get here if `from` is the device root. - // For example: from='C:\\'; to='C:\\foo' - return toOrig.slice(toStart + i); - } - } - if (fromLen > length) { - if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) { - // We get here if `to` is the exact base path for `from`. - // For example: from='C:\\foo\\bar'; to='C:\\foo' - lastCommonSep = i; - } else if (i === 2) { - // We get here if `to` is the device root. - // For example: from='C:\\foo\\bar'; to='C:\\' - lastCommonSep = 3; - } - } - break; - } - const fromCode = from.charCodeAt(fromStart + i); - const toCode = to.charCodeAt(toStart + i); - if (fromCode !== toCode) break; - else if (fromCode === CHAR_BACKWARD_SLASH) lastCommonSep = i; - } - - // We found a mismatch before the first common path separator was seen, so - // return the original `to`. - if (i !== length && lastCommonSep === -1) { - return toOrig; - } - - let out = ""; - if (lastCommonSep === -1) lastCommonSep = 0; - // Generate the relative path based on the path difference between `to` and - // `from` - for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { - if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) { - if (out.length === 0) out += ".."; - else out += "\\.."; - } - } - - // Lastly, append the rest of the destination (`to`) path that comes after - // the common path parts - if (out.length > 0) { - return out + toOrig.slice(toStart + lastCommonSep, toEnd); - } else { - toStart += lastCommonSep; - if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) ++toStart; - return toOrig.slice(toStart, toEnd); - } -} - -/** - * Resolves path to a namespace path - * @param path to resolve to namespace - */ -export function toNamespacedPath(path: string): string { - // Note: this will *probably* throw somewhere. - if (typeof path !== "string") return path; - if (path.length === 0) return ""; - - const resolvedPath = resolve(path); - - if (resolvedPath.length >= 3) { - if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) { - // Possible UNC root - - if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) { - const code = resolvedPath.charCodeAt(2); - if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) { - // Matched non-long UNC root, convert the path to a long UNC path - return `\\\\?\\UNC\\${resolvedPath.slice(2)}`; - } - } - } else if (isWindowsDeviceRoot(resolvedPath.charCodeAt(0))) { - // Possible device root - - if ( - resolvedPath.charCodeAt(1) === CHAR_COLON && - resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH - ) { - // Matched device root, convert the path to a long UNC path - return `\\\\?\\${resolvedPath}`; - } - } - } - - return path; -} - -/** - * Return the directory name of a `path`. - * @param path to determine name for - */ -export function dirname(path: string): string { - assertPath(path); - const len = path.length; - if (len === 0) return "."; - let rootEnd = -1; - let end = -1; - let matchedSlash = true; - let offset = 0; - const code = path.charCodeAt(0); - - // Try to match a root - if (len > 1) { - if (isPathSeparator(code)) { - // Possible UNC root - - rootEnd = offset = 1; - - if (isPathSeparator(path.charCodeAt(1))) { - // Matched double path separator at beginning - let j = 2; - let last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more path separators - for (; j < len; ++j) { - if (!isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j === len) { - // We matched a UNC root only - return path; - } - if (j !== last) { - // We matched a UNC root with leftovers - - // Offset by 1 to include the separator after the UNC root to - // treat it as a "normal root" on top of a (UNC) root - rootEnd = offset = j + 1; - } - } - } - } - } else if (isWindowsDeviceRoot(code)) { - // Possible device root - - if (path.charCodeAt(1) === CHAR_COLON) { - rootEnd = offset = 2; - if (len > 2) { - if (isPathSeparator(path.charCodeAt(2))) rootEnd = offset = 3; - } - } - } - } else if (isPathSeparator(code)) { - // `path` contains just a path separator, exit early to avoid - // unnecessary work - return path; - } - - for (let i = len - 1; i >= offset; --i) { - if (isPathSeparator(path.charCodeAt(i))) { - if (!matchedSlash) { - end = i; - break; - } - } else { - // We saw the first non-path separator - matchedSlash = false; - } - } - - if (end === -1) { - if (rootEnd === -1) return "."; - else end = rootEnd; - } - return path.slice(0, end); -} - -/** - * Return the last portion of a `path`. Trailing directory separators are ignored. - * @param path to process - * @param ext of path directory - */ -export function basename(path: string, ext = ""): string { - if (ext !== undefined && typeof ext !== "string") { - throw new TypeError('"ext" argument must be a string'); - } - - assertPath(path); - - let start = 0; - let end = -1; - let matchedSlash = true; - let i: number; - - // Check for a drive letter prefix so as not to mistake the following - // path separator as an extra separator at the end of the path that can be - // disregarded - if (path.length >= 2) { - const drive = path.charCodeAt(0); - if (isWindowsDeviceRoot(drive)) { - if (path.charCodeAt(1) === CHAR_COLON) start = 2; - } - } - - if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { - if (ext.length === path.length && ext === path) return ""; - let extIdx = ext.length - 1; - let firstNonSlashEnd = -1; - for (i = path.length - 1; i >= start; --i) { - const code = path.charCodeAt(i); - if (isPathSeparator(code)) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - start = i + 1; - break; - } - } else { - if (firstNonSlashEnd === -1) { - // We saw the first non-path separator, remember this index in case - // we need it if the extension ends up not matching - matchedSlash = false; - firstNonSlashEnd = i + 1; - } - if (extIdx >= 0) { - // Try to match the explicit extension - if (code === ext.charCodeAt(extIdx)) { - if (--extIdx === -1) { - // We matched the extension, so mark this as the end of our path - // component - end = i; - } - } else { - // Extension does not match, so our result is the entire path - // component - extIdx = -1; - end = firstNonSlashEnd; - } - } - } - } - - if (start === end) end = firstNonSlashEnd; - else if (end === -1) end = path.length; - return path.slice(start, end); - } else { - for (i = path.length - 1; i >= start; --i) { - if (isPathSeparator(path.charCodeAt(i))) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - start = i + 1; - break; - } - } else if (end === -1) { - // We saw the first non-path separator, mark this as the end of our - // path component - matchedSlash = false; - end = i + 1; - } - } - - if (end === -1) return ""; - return path.slice(start, end); - } -} - -/** - * Return the extension of the `path`. - * @param path with extension - */ -export function extname(path: string): string { - assertPath(path); - let start = 0; - let startDot = -1; - let startPart = 0; - let end = -1; - let matchedSlash = true; - // Track the state of characters (if any) we see before our first dot and - // after any path separator we find - let preDotState = 0; - - // Check for a drive letter prefix so as not to mistake the following - // path separator as an extra separator at the end of the path that can be - // disregarded - - if ( - path.length >= 2 && - path.charCodeAt(1) === CHAR_COLON && - isWindowsDeviceRoot(path.charCodeAt(0)) - ) { - start = startPart = 2; - } - - for (let i = path.length - 1; i >= start; --i) { - const code = path.charCodeAt(i); - if (isPathSeparator(code)) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - startPart = i + 1; - break; - } - continue; - } - if (end === -1) { - // We saw the first non-path separator, mark this as the end of our - // extension - matchedSlash = false; - end = i + 1; - } - if (code === CHAR_DOT) { - // If this is our first dot, mark it as the start of our extension - if (startDot === -1) startDot = i; - else if (preDotState !== 1) preDotState = 1; - } else if (startDot !== -1) { - // We saw a non-dot and non-path separator before our dot, so we should - // have a good chance at having a non-empty extension - preDotState = -1; - } - } - - if ( - startDot === -1 || - end === -1 || - // We saw a non-dot character immediately before the dot - preDotState === 0 || - // The (right-most) trimmed path component is exactly '..' - (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) - ) { - return ""; - } - return path.slice(startDot, end); -} - -/** - * Generate a path from `FormatInputPathObject` object. - * @param pathObject with path - */ -export function format(pathObject: FormatInputPathObject): string { - if (pathObject === null || typeof pathObject !== "object") { - throw new TypeError( - `The "pathObject" argument must be of type Object. Received type ${typeof pathObject}`, - ); - } - return _format("\\", pathObject); -} - -/** - * Return a `ParsedPath` object of the `path`. - * @param path to process - */ -export function parse(path: string): ParsedPath { - assertPath(path); - - const ret: ParsedPath = { root: "", dir: "", base: "", ext: "", name: "" }; - - const len = path.length; - if (len === 0) return ret; - - let rootEnd = 0; - let code = path.charCodeAt(0); - - // Try to match a root - if (len > 1) { - if (isPathSeparator(code)) { - // Possible UNC root - - rootEnd = 1; - if (isPathSeparator(path.charCodeAt(1))) { - // Matched double path separator at beginning - let j = 2; - let last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more path separators - for (; j < len; ++j) { - if (!isPathSeparator(path.charCodeAt(j))) break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - if (isPathSeparator(path.charCodeAt(j))) break; - } - if (j === len) { - // We matched a UNC root only - - rootEnd = j; - } else if (j !== last) { - // We matched a UNC root with leftovers - - rootEnd = j + 1; - } - } - } - } - } else if (isWindowsDeviceRoot(code)) { - // Possible device root - - if (path.charCodeAt(1) === CHAR_COLON) { - rootEnd = 2; - if (len > 2) { - if (isPathSeparator(path.charCodeAt(2))) { - if (len === 3) { - // `path` contains just a drive root, exit early to avoid - // unnecessary work - ret.root = ret.dir = path; - return ret; - } - rootEnd = 3; - } - } else { - // `path` contains just a drive root, exit early to avoid - // unnecessary work - ret.root = ret.dir = path; - return ret; - } - } - } - } else if (isPathSeparator(code)) { - // `path` contains just a path separator, exit early to avoid - // unnecessary work - ret.root = ret.dir = path; - return ret; - } - - if (rootEnd > 0) ret.root = path.slice(0, rootEnd); - - let startDot = -1; - let startPart = rootEnd; - let end = -1; - let matchedSlash = true; - let i = path.length - 1; - - // Track the state of characters (if any) we see before our first dot and - // after any path separator we find - let preDotState = 0; - - // Get non-dir info - for (; i >= rootEnd; --i) { - code = path.charCodeAt(i); - if (isPathSeparator(code)) { - // If we reached a path separator that was not part of a set of path - // separators at the end of the string, stop now - if (!matchedSlash) { - startPart = i + 1; - break; - } - continue; - } - if (end === -1) { - // We saw the first non-path separator, mark this as the end of our - // extension - matchedSlash = false; - end = i + 1; - } - if (code === CHAR_DOT) { - // If this is our first dot, mark it as the start of our extension - if (startDot === -1) startDot = i; - else if (preDotState !== 1) preDotState = 1; - } else if (startDot !== -1) { - // We saw a non-dot and non-path separator before our dot, so we should - // have a good chance at having a non-empty extension - preDotState = -1; - } - } - - if ( - startDot === -1 || - end === -1 || - // We saw a non-dot character immediately before the dot - preDotState === 0 || - // The (right-most) trimmed path component is exactly '..' - (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) - ) { - if (end !== -1) { - ret.base = ret.name = path.slice(startPart, end); - } - } else { - ret.name = path.slice(startPart, startDot); - ret.base = path.slice(startPart, end); - ret.ext = path.slice(startDot, end); - } - - // If the directory is the root, use the entire root as the `dir` including - // the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the - // trailing slash (`C:\abc\def` -> `C:\abc`). - if (startPart > 0 && startPart !== rootEnd) { - ret.dir = path.slice(0, startPart - 1); - } else ret.dir = ret.root; - - return ret; -} - -/** - * Converts a file URL to a path string. - * - * fromFileUrl("file:///home/foo"); // "\\home\\foo" - * fromFileUrl("file:///C:/Users/foo"); // "C:\\Users\\foo" - * fromFileUrl("file://localhost/home/foo"); // "\\\\localhost\\home\\foo" - * @param url of a file URL - */ -export function fromFileUrl(url: string | URL): string { - url = url instanceof URL ? url : new URL(url); - if (url.protocol != "file:") { - throw new TypeError("Must be a file URL."); - } - let path = decodeURIComponent( - url.pathname.replace(/\//g, "\\").replace(/%(?![0-9A-Fa-f]{2})/g, "%25"), - ).replace(/^\\*([A-Za-z]:)(\\|$)/, "$1\\"); - if (url.hostname != "") { - // Note: The `URL` implementation guarantees that the drive letter and - // hostname are mutually exclusive. Otherwise it would not have been valid - // to append the hostname and path like this. - path = `\\\\${url.hostname}${path}`; - } - return path; -} - -/** - * Converts a path string to a file URL. - * - * toFileUrl("\\home\\foo"); // new URL("file:///home/foo") - * toFileUrl("C:\\Users\\foo"); // new URL("file:///C:/Users/foo") - * toFileUrl("\\\\localhost\\home\\foo"); // new URL("file://localhost/home/foo") - * @param path to convert to file URL - */ -export function toFileUrl(path: string): URL { - if (!isAbsolute(path)) { - throw new TypeError("Must be an absolute path."); - } - const [, hostname, pathname] = path.match( - /^(?:[/\\]{2}([^/\\]+)(?=[/\\][^/\\]))?(.*)/, - )!; - const url = new URL("file:///"); - url.pathname = pathname.replace(/%/g, "%25"); - if (hostname != null) { - url.hostname = hostname; - if (!url.hostname) { - throw new TypeError("Invalid hostname."); - } - } - return url; -} diff --git a/std/path/zero_length_strings_test.ts b/std/path/zero_length_strings_test.ts deleted file mode 100644 index e2ec466a5c..0000000000 --- a/std/path/zero_length_strings_test.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright the Browserify authors. MIT License. -// Ported from https://github.com/browserify/path-browserify/ -import { assertEquals } from "../testing/asserts.ts"; -import * as path from "./mod.ts"; - -const pwd = Deno.cwd(); - -Deno.test("joinZeroLength", function () { - // join will internally ignore all the zero-length strings and it will return - // '.' if the joined string is a zero-length string. - assertEquals(path.posix.join(""), "."); - assertEquals(path.posix.join("", ""), "."); - if (path.win32) assertEquals(path.win32.join(""), "."); - if (path.win32) assertEquals(path.win32.join("", ""), "."); - assertEquals(path.join(pwd), pwd); - assertEquals(path.join(pwd, ""), pwd); -}); - -Deno.test("normalizeZeroLength", function () { - // normalize will return '.' if the input is a zero-length string - assertEquals(path.posix.normalize(""), "."); - if (path.win32) assertEquals(path.win32.normalize(""), "."); - assertEquals(path.normalize(pwd), pwd); -}); - -Deno.test("isAbsoluteZeroLength", function () { - // Since '' is not a valid path in any of the common environments, - // return false - assertEquals(path.posix.isAbsolute(""), false); - if (path.win32) assertEquals(path.win32.isAbsolute(""), false); -}); - -Deno.test("resolveZeroLength", function () { - // resolve, internally ignores all the zero-length strings and returns the - // current working directory - assertEquals(path.resolve(""), pwd); - assertEquals(path.resolve("", ""), pwd); -}); - -Deno.test("relativeZeroLength", function () { - // relative, internally calls resolve. So, '' is actually the current - // directory - assertEquals(path.relative("", pwd), ""); - assertEquals(path.relative(pwd, ""), ""); - assertEquals(path.relative(pwd, pwd), ""); -}); diff --git a/std/permissions/mod.ts b/std/permissions/mod.ts deleted file mode 100644 index 3134589d22..0000000000 --- a/std/permissions/mod.ts +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -const { PermissionDenied } = Deno.errors; - -function getPermissionString(descriptors: Deno.PermissionDescriptor[]): string { - return descriptors.length - ? ` ${ - descriptors - .map((pd) => { - switch (pd.name) { - case "read": - case "write": - return pd.path - ? `--allow-${pd.name}=${pd.path}` - : `--allow-${pd.name}`; - case "net": - return pd.host - ? `--allow-${pd.name}=${pd.host}` - : `--allow-${pd.name}`; - default: - return `--allow-${pd.name}`; - } - }) - .join("\n ") - }` - : ""; -} - -/** Attempts to grant a set of permissions, resolving with the descriptors of - * the permissions that are granted. - * - * const perms = await grant({ name: "net" }, { name: "read" }); - * if (perms && perms.length === 2) { - * // do something cool that connects to the net and reads files - * } else { - * // notify user of missing permissions - * } - * - * If one of the permissions requires a prompt, the function will attempt to - * prompt for it. The function resolves with all of the granted permissions. */ -export async function grant( - ...descriptors: Deno.PermissionDescriptor[] -): Promise; -/** Attempts to grant a set of permissions, resolving with the descriptors of - * the permissions that are granted. - * - * const perms = await grant([{ name: "net" }, { name: "read" }]); - * if (perms && perms.length === 2) { - * // do something cool that connects to the net and reads files - * } else { - * // notify user of missing permissions - * } - * - * If one of the permissions requires a prompt, the function will attempt to - * prompt for it. The function resolves with all of the granted permissions. */ -export async function grant( - descriptors: Deno.PermissionDescriptor[], -): Promise; -export async function grant( - descriptor: Deno.PermissionDescriptor[] | Deno.PermissionDescriptor, - ...descriptors: Deno.PermissionDescriptor[] -): Promise { - const result: Deno.PermissionDescriptor[] = []; - descriptors = Array.isArray(descriptor) - ? descriptor - : [descriptor, ...descriptors]; - for (const descriptor of descriptors) { - let state = (await Deno.permissions.query(descriptor)).state; - if (state === "prompt") { - state = (await Deno.permissions.request(descriptor)).state; - } - if (state === "granted") { - result.push(descriptor); - } - } - return result.length ? result : undefined; -} - -/** Attempts to grant a set of permissions or rejects. - * - * await grantOrThrow({ name: "env" }, { name: "net" }); - * - * If the permission can be prompted for, the function will attempt to prompt. - * If any of the permissions are denied, the function will reject for the first - * permission that is denied. If all permissions are granted, the function - * will resolve. */ -export async function grantOrThrow( - ...descriptors: Deno.PermissionDescriptor[] -): Promise; -/** Attempts to grant a set of permissions or rejects. - * - * await grantOrThrow([{ name: "env" }, { name: "net" }]); - * - * If the permission can be prompted for, the function will attempt to prompt. - * If any of the permissions are denied, the function will reject mentioning the - * the denied permissions. If all permissions are granted, the function will - * resolve. */ -export async function grantOrThrow( - descriptors: Deno.PermissionDescriptor[], -): Promise; -export async function grantOrThrow( - descriptor: Deno.PermissionDescriptor[] | Deno.PermissionDescriptor, - ...descriptors: Deno.PermissionDescriptor[] -): Promise { - const denied: Deno.PermissionDescriptor[] = []; - descriptors = Array.isArray(descriptor) - ? descriptor - : [descriptor, ...descriptors]; - for (const descriptor of descriptors) { - const { state } = await Deno.permissions.request(descriptor); - if (state !== "granted") { - denied.push(descriptor); - } - } - if (denied.length) { - throw new PermissionDenied( - `The following permissions have not been granted:\n${ - getPermissionString( - denied, - ) - }`, - ); - } -} diff --git a/std/permissions/test.ts b/std/permissions/test.ts deleted file mode 100644 index 622f9f1b68..0000000000 --- a/std/permissions/test.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { grant, grantOrThrow } from "./mod.ts"; -import { assert, assertEquals } from "../testing/asserts.ts"; - -Deno.test({ - name: "grant basic", - async fn() { - assertEquals(await grant({ name: "net" }, { name: "env" }), [ - { name: "net" }, - { name: "env" }, - ]); - }, -}); - -Deno.test({ - name: "grant array", - async fn() { - assertEquals(await grant([{ name: "net" }, { name: "env" }]), [ - { name: "net" }, - { name: "env" }, - ]); - }, -}); - -Deno.test({ - name: "grant logic", - async fn() { - assert(await grant({ name: "net" })); - }, -}); - -Deno.test({ - name: "grantOrThrow basic", - async fn() { - await grantOrThrow({ name: "net" }, { name: "env" }); - }, -}); - -Deno.test({ - name: "grantOrThrow array", - async fn() { - await grantOrThrow([{ name: "net" }, { name: "env" }]); - }, -}); diff --git a/std/signal/README.md b/std/signal/README.md deleted file mode 100644 index 7a152cdca7..0000000000 --- a/std/signal/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# signal - -signal is a module used to capture and monitor OS signals. - -# usage - -The following functions are exposed in `mod.ts`: - -## signal - -Generates an AsyncIterable which can be awaited on for one or more signals. -`dispose()` can be called when you are finished waiting on the events. - -```typescript -import { signal } from "https://deno.land/std/signal/mod.ts"; -const sig = signal(Deno.Signal.SIGUSR1, Deno.Signal.SIGINT); -setTimeout(() => {}, 5000); // Prevents exiting immediately. - -for await (const _ of sig) { - // .. -} - -// At some other point in your code when finished listening: -sig.dispose(); -``` - -## onSignal - -Registers a callback function to be called on triggering of a signal event. - -```typescript -import { onSignal } from "https://deno.land/std/signal/mod.ts"; - -const handle = onSignal(Deno.Signal.SIGINT, () => { - // ... - handle.dispose(); // de-register from receiving further events. -}); -``` diff --git a/std/signal/mod.ts b/std/signal/mod.ts deleted file mode 100644 index c28cf5e99c..0000000000 --- a/std/signal/mod.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { MuxAsyncIterator } from "../async/mux_async_iterator.ts"; - -export type Disposable = { dispose: () => void }; - -/** - * Generates an AsyncIterable which can be awaited on for one or more signals. - * `dispose()` can be called when you are finished waiting on the events. - * - * Example: - * - * const sig = signal(Deno.Signal.SIGUSR1, Deno.Signal.SIGINT); - * setTimeout(() => {}, 5000); // Prevents exiting immediately - * - * for await (const _ of sig) { - * console.log("interrupt or usr1 signal received"); - * } - * - * // At some other point in your code when finished listening: - * sig.dispose(); - * - * @param signos - one or more `Deno.Signal`s to await on - */ -export function signal( - ...signos: [number, ...number[]] -): AsyncIterable & Disposable { - const mux = new MuxAsyncIterator(); - - if (signos.length < 1) { - throw new Error( - "No signals are given. You need to specify at least one signal to create a signal stream.", - ); - } - - const streams = signos.map(Deno.signal); - - streams.forEach((stream) => { - mux.add(stream); - }); - - // Create dispose method for the muxer of signal streams. - const dispose = (): void => { - streams.forEach((stream) => { - stream.dispose(); - }); - }; - - return Object.assign(mux, { dispose }); -} - -/** - * Registers a callback function to be called on triggering of a signal event. - * - * const handle = onSignal(Deno.Signal.SIGINT, () => { - * console.log('Received SIGINT'); - * handle.dispose(); // de-register from receiving further events - * }); - * - * @param signo One of Deno.Signal (e.g. Deno.Signal.SIGINT) - * @param callback Callback function triggered upon signal event - */ -export function onSignal(signo: number, callback: () => void): Disposable { - const sig = signal(signo); - - // allows `sig` to be returned before blocking on the await - (async (): Promise => { - for await (const _ of sig) { - callback(); - } - })(); - - return sig; -} diff --git a/std/signal/test.ts b/std/signal/test.ts deleted file mode 100644 index d56b7920d8..0000000000 --- a/std/signal/test.ts +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assertEquals, assertThrows } from "../testing/asserts.ts"; -import { delay } from "../async/delay.ts"; -import { onSignal, signal } from "./mod.ts"; - -Deno.test({ - name: "signal() throws when called with empty signals", - ignore: Deno.build.os === "windows", - fn() { - assertThrows( - () => { - // deno-lint-ignore no-explicit-any - (signal as any)(); - }, - Error, - "No signals are given. You need to specify at least one signal to create a signal stream.", - ); - }, -}); - -Deno.test({ - name: "signal() iterates for multiple signals", - ignore: Deno.build.os === "windows", - fn: async (): Promise => { - // This prevents the program from exiting. - const t = setInterval(() => {}, 1000); - - let c = 0; - const sig = signal( - Deno.Signal.SIGUSR1, - Deno.Signal.SIGUSR2, - Deno.Signal.SIGINT, - ); - - setTimeout(async () => { - await delay(20); - Deno.kill(Deno.pid, Deno.Signal.SIGINT); - await delay(20); - Deno.kill(Deno.pid, Deno.Signal.SIGUSR2); - await delay(20); - Deno.kill(Deno.pid, Deno.Signal.SIGUSR1); - await delay(20); - Deno.kill(Deno.pid, Deno.Signal.SIGUSR2); - await delay(20); - Deno.kill(Deno.pid, Deno.Signal.SIGUSR1); - await delay(20); - Deno.kill(Deno.pid, Deno.Signal.SIGINT); - await delay(20); - sig.dispose(); - }); - - for await (const _ of sig) { - c += 1; - } - - assertEquals(c, 6); - - clearTimeout(t); - }, -}); - -Deno.test({ - name: "onSignal() registers and disposes of event handler", - ignore: Deno.build.os === "windows", - async fn() { - // This prevents the program from exiting. - const t = setInterval(() => {}, 1000); - - let calledCount = 0; - const handle = onSignal(Deno.Signal.SIGINT, () => { - calledCount++; - }); - - await delay(20); - Deno.kill(Deno.pid, Deno.Signal.SIGINT); - await delay(20); - Deno.kill(Deno.pid, Deno.Signal.SIGINT); - await delay(20); - Deno.kill(Deno.pid, Deno.Signal.SIGUSR2); - await delay(20); - handle.dispose(); // stop monitoring SIGINT - await delay(20); - Deno.kill(Deno.pid, Deno.Signal.SIGUSR1); - await delay(20); - Deno.kill(Deno.pid, Deno.Signal.SIGINT); - await delay(20); - assertEquals(calledCount, 2); - - clearTimeout(t); - }, -}); diff --git a/std/testing/README.md b/std/testing/README.md deleted file mode 100644 index 2cff96a6d6..0000000000 --- a/std/testing/README.md +++ /dev/null @@ -1,248 +0,0 @@ -# Testing - -This module provides a few basic utilities to make testing easier and consistent -in Deno. - -## Usage - -`testing/asserts.ts` module provides range of assertion helpers. If the -assertion is false an `AssertionError` will be thrown which will result in -pretty-printed diff of failing assertion. - -- `equal()` - Deep comparison function, where `actual` and `expected` are - compared deeply, and if they vary, `equal` returns `false`. -- `assert()` - Expects a boolean value, throws if the value is `false`. -- `assertEquals()` - Uses the `equal` comparison and throws if the `actual` and - `expected` are not equal. -- `assertNotEquals()` - Uses the `equal` comparison and throws if the `actual` - and `expected` are equal. -- `assertStrictEquals()` - Compares `actual` and `expected` strictly, therefore - for non-primitives the values must reference the same instance. -- `assertStringIncludes()` - Make an assertion that `actual` includes - `expected`. -- `assertMatch()` - Make an assertion that `actual` match RegExp `expected`. -- `assertNotMatch()` - Make an assertion that `actual` not match RegExp - `expected`. -- `assertArrayIncludes()` - Make an assertion that `actual` array includes the - `expected` values. -- `assertObjectMatch()` - Make an assertion that `actual` object match - `expected` subset object -- `assertThrows()` - Expects the passed `fn` to throw. If `fn` does not throw, - this function does. Also compares any errors thrown to an optional expected - `Error` class and checks that the error `.message` includes an optional - string. -- `assertThrowsAsync()` - Expects the passed `fn` to be async and throw (or - return a `Promise` that rejects). If the `fn` does not throw or reject, this - function will throw asynchronously. Also compares any errors thrown to an - optional expected `Error` class and checks that the error `.message` includes - an optional string. -- `unimplemented()` - Use this to stub out methods that will throw when invoked. -- `unreachable()` - Used to assert unreachable code. - -Basic usage: - -```ts -import { assertEquals } from "https://deno.land/std@$STD_VERSION/testing/asserts.ts"; - -Deno.test({ - name: "testing example", - fn(): void { - assertEquals("world", "world"); - assertEquals({ hello: "world" }, { hello: "world" }); - }, -}); -``` - -Short syntax (named function instead of object): - -```ts -Deno.test("example", function (): void { - assertEquals("world", "world"); - assertEquals({ hello: "world" }, { hello: "world" }); -}); -``` - -Using `assertStrictEquals()`: - -```ts -Deno.test("isStrictlyEqual", function (): void { - const a = {}; - const b = a; - assertStrictEquals(a, b); -}); - -// This test fails -Deno.test("isNotStrictlyEqual", function (): void { - const a = {}; - const b = {}; - assertStrictEquals(a, b); -}); -``` - -Using `assertThrows()`: - -```ts -Deno.test("doesThrow", function (): void { - assertThrows((): void => { - throw new TypeError("hello world!"); - }); - assertThrows((): void => { - throw new TypeError("hello world!"); - }, TypeError); - assertThrows( - (): void => { - throw new TypeError("hello world!"); - }, - TypeError, - "hello", - ); -}); - -// This test will not pass. -Deno.test("fails", function (): void { - assertThrows((): void => { - console.log("Hello world"); - }); -}); -``` - -Using `assertThrowsAsync()`: - -```ts -Deno.test("doesThrow", async function (): Promise { - await assertThrowsAsync( - async (): Promise => { - throw new TypeError("hello world!"); - }, - ); - await assertThrowsAsync(async (): Promise => { - throw new TypeError("hello world!"); - }, TypeError); - await assertThrowsAsync( - async (): Promise => { - throw new TypeError("hello world!"); - }, - TypeError, - "hello", - ); - await assertThrowsAsync( - async (): Promise => { - return Promise.reject(new Error()); - }, - ); -}); - -// This test will not pass. -Deno.test("fails", async function (): Promise { - await assertThrowsAsync( - async (): Promise => { - console.log("Hello world"); - }, - ); -}); -``` - -## Benching - -With this module you can benchmark your code and get information on how is it -performing. - -### Basic usage: - -Benchmarks can be registered using the `bench` function, where you can define a -code, that should be benchmarked. `b.start()` has to be called at the start of -the part you want to benchmark and `b.stop()` at the end of it, otherwise an -error will be thrown. - -After that simply calling `runBenchmarks()` will benchmark all registered -benchmarks and log the results in the commandline. - -```ts -import { - bench, - runBenchmarks, -} from "https://deno.land/std@$STD_VERSION/testing/bench.ts"; - -bench(function forIncrementX1e9(b): void { - b.start(); - for (let i = 0; i < 1e9; i++); - b.stop(); -}); - -runBenchmarks(); -``` - -Averaging execution time over multiple runs: - -```ts -bench({ - name: "runs100ForIncrementX1e6", - runs: 100, - func(b): void { - b.start(); - for (let i = 0; i < 1e6; i++); - b.stop(); - }, -}); -``` - -Running specific benchmarks using regular expressions: - -```ts -runBenchmarks({ only: /desired/, skip: /exceptions/ }); -``` - -### Processing benchmark results - -`runBenchmarks()` returns a `Promise`, so you can process -the benchmarking results yourself. It contains detailed results of each -benchmark's run as `BenchmarkResult` s. - -```ts -runBenchmarks() - .then((results: BenchmarkRunResult) => { - console.log(results); - }) - .catch((error: Error) => { - // ... errors if benchmark was badly constructed. - }); -``` - -### Processing benchmarking progress - -`runBenchmarks()` accepts an optional progress handler callback function, so you -can get information on the progress of the running benchmarking. - -Using `{ silent: true }` means you wont see the default progression logs in the -commandline. - -```ts -runBenchmarks({ silent: true }, (p: BenchmarkRunProgress) => { - // initial progress data. - if (p.state === ProgressState.BenchmarkingStart) { - console.log( - `Starting benchmarking. Queued: ${p.queued.length}, filtered: ${p.filtered}`, - ); - } - // ... -}); -``` - -#### Benching API - -##### `bench(benchmark: BenchmarkDefinition | BenchmarkFunction): void` - -Registers a benchmark that will be run once `runBenchmarks` is called. - -##### `runBenchmarks(opts?: BenchmarkRunOptions, progressCb?: (p: BenchmarkRunProgress) => void | Promise): Promise` - -Runs all registered benchmarks serially. Filtering can be applied by setting -`BenchmarkRunOptions.only` and/or `BenchmarkRunOptions.skip` to regular -expressions matching benchmark names. Default progression logs can be turned off -with the `BenchmarkRunOptions.silent` flag. - -##### `clearBenchmarks(opts?: BenchmarkClearOptions): void` - -Clears all registered benchmarks, so calling `runBenchmarks()` after it wont run -them. Filtering can be applied by setting `BenchmarkRunOptions.only` and/or -`BenchmarkRunOptions.skip` to regular expressions matching benchmark names. diff --git a/std/testing/_diff.ts b/std/testing/_diff.ts deleted file mode 100644 index 7d659acd26..0000000000 --- a/std/testing/_diff.ts +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. - -interface FarthestPoint { - y: number; - id: number; -} - -export enum DiffType { - removed = "removed", - common = "common", - added = "added", -} - -export interface DiffResult { - type: DiffType; - value: T; -} - -const REMOVED = 1; -const COMMON = 2; -const ADDED = 3; - -function createCommon(A: T[], B: T[], reverse?: boolean): T[] { - const common = []; - if (A.length === 0 || B.length === 0) return []; - for (let i = 0; i < Math.min(A.length, B.length); i += 1) { - if ( - A[reverse ? A.length - i - 1 : i] === B[reverse ? B.length - i - 1 : i] - ) { - common.push(A[reverse ? A.length - i - 1 : i]); - } else { - return common; - } - } - return common; -} - -/** - * Renders the differences between the actual and expected values - * @param A Actual value - * @param B Expected value - */ -export function diff(A: T[], B: T[]): Array> { - const prefixCommon = createCommon(A, B); - const suffixCommon = createCommon( - A.slice(prefixCommon.length), - B.slice(prefixCommon.length), - true, - ).reverse(); - A = suffixCommon.length - ? A.slice(prefixCommon.length, -suffixCommon.length) - : A.slice(prefixCommon.length); - B = suffixCommon.length - ? B.slice(prefixCommon.length, -suffixCommon.length) - : B.slice(prefixCommon.length); - const swapped = B.length > A.length; - [A, B] = swapped ? [B, A] : [A, B]; - const M = A.length; - const N = B.length; - if (!M && !N && !suffixCommon.length && !prefixCommon.length) return []; - if (!N) { - return [ - ...prefixCommon.map( - (c): DiffResult => ({ type: DiffType.common, value: c }), - ), - ...A.map( - (a): DiffResult => ({ - type: swapped ? DiffType.added : DiffType.removed, - value: a, - }), - ), - ...suffixCommon.map( - (c): DiffResult => ({ type: DiffType.common, value: c }), - ), - ]; - } - const offset = N; - const delta = M - N; - const size = M + N + 1; - const fp = new Array(size).fill({ y: -1 }); - /** - * INFO: - * This buffer is used to save memory and improve performance. - * The first half is used to save route and last half is used to save diff - * type. - * This is because, when I kept new uint8array area to save type,performance - * worsened. - */ - const routes = new Uint32Array((M * N + size + 1) * 2); - const diffTypesPtrOffset = routes.length / 2; - let ptr = 0; - let p = -1; - - function backTrace( - A: T[], - B: T[], - current: FarthestPoint, - swapped: boolean, - ): Array<{ - type: DiffType; - value: T; - }> { - const M = A.length; - const N = B.length; - const result = []; - let a = M - 1; - let b = N - 1; - let j = routes[current.id]; - let type = routes[current.id + diffTypesPtrOffset]; - while (true) { - if (!j && !type) break; - const prev = j; - if (type === REMOVED) { - result.unshift({ - type: swapped ? DiffType.removed : DiffType.added, - value: B[b], - }); - b -= 1; - } else if (type === ADDED) { - result.unshift({ - type: swapped ? DiffType.added : DiffType.removed, - value: A[a], - }); - a -= 1; - } else { - result.unshift({ type: DiffType.common, value: A[a] }); - a -= 1; - b -= 1; - } - j = routes[prev]; - type = routes[prev + diffTypesPtrOffset]; - } - return result; - } - - function createFP( - slide: FarthestPoint, - down: FarthestPoint, - k: number, - M: number, - ): FarthestPoint { - if (slide && slide.y === -1 && down && down.y === -1) { - return { y: 0, id: 0 }; - } - if ( - (down && down.y === -1) || - k === M || - (slide && slide.y) > (down && down.y) + 1 - ) { - const prev = slide.id; - ptr++; - routes[ptr] = prev; - routes[ptr + diffTypesPtrOffset] = ADDED; - return { y: slide.y, id: ptr }; - } else { - const prev = down.id; - ptr++; - routes[ptr] = prev; - routes[ptr + diffTypesPtrOffset] = REMOVED; - return { y: down.y + 1, id: ptr }; - } - } - - function snake( - k: number, - slide: FarthestPoint, - down: FarthestPoint, - _offset: number, - A: T[], - B: T[], - ): FarthestPoint { - const M = A.length; - const N = B.length; - if (k < -N || M < k) return { y: -1, id: -1 }; - const fp = createFP(slide, down, k, M); - while (fp.y + k < M && fp.y < N && A[fp.y + k] === B[fp.y]) { - const prev = fp.id; - ptr++; - fp.id = ptr; - fp.y += 1; - routes[ptr] = prev; - routes[ptr + diffTypesPtrOffset] = COMMON; - } - return fp; - } - - while (fp[delta + offset].y < N) { - p = p + 1; - for (let k = -p; k < delta; ++k) { - fp[k + offset] = snake( - k, - fp[k - 1 + offset], - fp[k + 1 + offset], - offset, - A, - B, - ); - } - for (let k = delta + p; k > delta; --k) { - fp[k + offset] = snake( - k, - fp[k - 1 + offset], - fp[k + 1 + offset], - offset, - A, - B, - ); - } - fp[delta + offset] = snake( - delta, - fp[delta - 1 + offset], - fp[delta + 1 + offset], - offset, - A, - B, - ); - } - return [ - ...prefixCommon.map( - (c): DiffResult => ({ type: DiffType.common, value: c }), - ), - ...backTrace(A, B, fp[delta + offset], swapped), - ...suffixCommon.map( - (c): DiffResult => ({ type: DiffType.common, value: c }), - ), - ]; -} diff --git a/std/testing/_diff_test.ts b/std/testing/_diff_test.ts deleted file mode 100644 index ec40c191c2..0000000000 --- a/std/testing/_diff_test.ts +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { diff } from "./_diff.ts"; -import { assertEquals } from "../testing/asserts.ts"; - -Deno.test({ - name: "empty", - fn(): void { - assertEquals(diff([], []), []); - }, -}); - -Deno.test({ - name: '"a" vs "b"', - fn(): void { - assertEquals(diff(["a"], ["b"]), [ - { type: "removed", value: "a" }, - { type: "added", value: "b" }, - ]); - }, -}); - -Deno.test({ - name: '"a" vs "a"', - fn(): void { - assertEquals(diff(["a"], ["a"]), [{ type: "common", value: "a" }]); - }, -}); - -Deno.test({ - name: '"a" vs ""', - fn(): void { - assertEquals(diff(["a"], []), [{ type: "removed", value: "a" }]); - }, -}); - -Deno.test({ - name: '"" vs "a"', - fn(): void { - assertEquals(diff([], ["a"]), [{ type: "added", value: "a" }]); - }, -}); - -Deno.test({ - name: '"a" vs "a, b"', - fn(): void { - assertEquals(diff(["a"], ["a", "b"]), [ - { type: "common", value: "a" }, - { type: "added", value: "b" }, - ]); - }, -}); - -Deno.test({ - name: '"strength" vs "string"', - fn(): void { - assertEquals(diff(Array.from("strength"), Array.from("string")), [ - { type: "common", value: "s" }, - { type: "common", value: "t" }, - { type: "common", value: "r" }, - { type: "removed", value: "e" }, - { type: "added", value: "i" }, - { type: "common", value: "n" }, - { type: "common", value: "g" }, - { type: "removed", value: "t" }, - { type: "removed", value: "h" }, - ]); - }, -}); - -Deno.test({ - name: '"strength" vs ""', - fn(): void { - assertEquals(diff(Array.from("strength"), Array.from("")), [ - { type: "removed", value: "s" }, - { type: "removed", value: "t" }, - { type: "removed", value: "r" }, - { type: "removed", value: "e" }, - { type: "removed", value: "n" }, - { type: "removed", value: "g" }, - { type: "removed", value: "t" }, - { type: "removed", value: "h" }, - ]); - }, -}); - -Deno.test({ - name: '"" vs "strength"', - fn(): void { - assertEquals(diff(Array.from(""), Array.from("strength")), [ - { type: "added", value: "s" }, - { type: "added", value: "t" }, - { type: "added", value: "r" }, - { type: "added", value: "e" }, - { type: "added", value: "n" }, - { type: "added", value: "g" }, - { type: "added", value: "t" }, - { type: "added", value: "h" }, - ]); - }, -}); - -Deno.test({ - name: '"abc", "c" vs "abc", "bcd", "c"', - fn(): void { - assertEquals(diff(["abc", "c"], ["abc", "bcd", "c"]), [ - { type: "common", value: "abc" }, - { type: "added", value: "bcd" }, - { type: "common", value: "c" }, - ]); - }, -}); diff --git a/std/testing/asserts.ts b/std/testing/asserts.ts deleted file mode 100644 index 3daf0d83f6..0000000000 --- a/std/testing/asserts.ts +++ /dev/null @@ -1,617 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// This module is browser compatible. Do not rely on good formatting of values -// for AssertionError messages in browsers. - -import { bold, gray, green, red, stripColor, white } from "../fmt/colors.ts"; -import { diff, DiffResult, DiffType } from "./_diff.ts"; - -const CAN_NOT_DISPLAY = "[Cannot display]"; - -interface Constructor { - // deno-lint-ignore no-explicit-any - new (...args: any[]): any; -} - -export class AssertionError extends Error { - constructor(message: string) { - super(message); - this.name = "AssertionError"; - } -} - -/** - * Converts the input into a string. Objects, Sets and Maps are sorted so as to - * make tests less flaky - * @param v Value to be formatted - */ -export function _format(v: unknown): string { - return globalThis.Deno - ? Deno.inspect(v, { - depth: Infinity, - sorted: true, - trailingComma: true, - compact: false, - iterableLimit: Infinity, - }) - : `"${String(v).replace(/(?=["\\])/g, "\\")}"`; -} - -/** - * Colors the output of assertion diffs - * @param diffType Difference type, either added or removed - */ -function createColor(diffType: DiffType): (s: string) => string { - switch (diffType) { - case DiffType.added: - return (s: string): string => green(bold(s)); - case DiffType.removed: - return (s: string): string => red(bold(s)); - default: - return white; - } -} - -/** - * Prefixes `+` or `-` in diff output - * @param diffType Difference type, either added or removed - */ -function createSign(diffType: DiffType): string { - switch (diffType) { - case DiffType.added: - return "+ "; - case DiffType.removed: - return "- "; - default: - return " "; - } -} - -function buildMessage(diffResult: ReadonlyArray>): string[] { - const messages: string[] = []; - messages.push(""); - messages.push(""); - messages.push( - ` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${ - green(bold("Expected")) - }`, - ); - messages.push(""); - messages.push(""); - diffResult.forEach((result: DiffResult): void => { - const c = createColor(result.type); - messages.push(c(`${createSign(result.type)}${result.value}`)); - }); - messages.push(""); - - return messages; -} - -function isKeyedCollection(x: unknown): x is Set { - return [Symbol.iterator, "size"].every((k) => k in (x as Set)); -} - -/** - * Deep equality comparison used in assertions - * @param c actual value - * @param d expected value - */ -export function equal(c: unknown, d: unknown): boolean { - const seen = new Map(); - return (function compare(a: unknown, b: unknown): boolean { - // Have to render RegExp & Date for string comparison - // unless it's mistreated as object - if ( - a && - b && - ((a instanceof RegExp && b instanceof RegExp) || - (a instanceof URL && b instanceof URL)) - ) { - return String(a) === String(b); - } - if (a instanceof Date && b instanceof Date) { - const aTime = a.getTime(); - const bTime = b.getTime(); - // Check for NaN equality manually since NaN is not - // equal to itself. - if (Number.isNaN(aTime) && Number.isNaN(bTime)) { - return true; - } - return a.getTime() === b.getTime(); - } - if (Object.is(a, b)) { - return true; - } - if (a && typeof a === "object" && b && typeof b === "object") { - if (seen.get(a) === b) { - return true; - } - if (Object.keys(a || {}).length !== Object.keys(b || {}).length) { - return false; - } - if (isKeyedCollection(a) && isKeyedCollection(b)) { - if (a.size !== b.size) { - return false; - } - - let unmatchedEntries = a.size; - - for (const [aKey, aValue] of a.entries()) { - for (const [bKey, bValue] of b.entries()) { - /* Given that Map keys can be references, we need - * to ensure that they are also deeply equal */ - if ( - (aKey === aValue && bKey === bValue && compare(aKey, bKey)) || - (compare(aKey, bKey) && compare(aValue, bValue)) - ) { - unmatchedEntries--; - } - } - } - - return unmatchedEntries === 0; - } - const merged = { ...a, ...b }; - for (const key in merged) { - type Key = keyof typeof merged; - if (!compare(a && a[key as Key], b && b[key as Key])) { - return false; - } - } - seen.set(a, b); - return true; - } - return false; - })(c, d); -} - -/** Make an assertion, error will be thrown if `expr` does not have truthy value. */ -export function assert(expr: unknown, msg = ""): asserts expr { - if (!expr) { - throw new AssertionError(msg); - } -} - -/** - * Make an assertion that `actual` and `expected` are equal, deeply. If not - * deeply equal, then throw. - * - * Type parameter can be specified to ensure values under comparison have the same type. - * For example: - *```ts - *assertEquals(1, 2) - *``` - */ -export function assertEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void; -export function assertEquals(actual: T, expected: T, msg?: string): void; -export function assertEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void { - if (equal(actual, expected)) { - return; - } - let message = ""; - const actualString = _format(actual); - const expectedString = _format(expected); - try { - const diffResult = diff( - actualString.split("\n"), - expectedString.split("\n"), - ); - const diffMsg = buildMessage(diffResult).join("\n"); - message = `Values are not equal:\n${diffMsg}`; - } catch (e) { - message = `\n${red(CAN_NOT_DISPLAY)} + \n\n`; - } - if (msg) { - message = msg; - } - throw new AssertionError(message); -} - -/** - * Make an assertion that `actual` and `expected` are not equal, deeply. - * If not then throw. - * - * Type parameter can be specified to ensure values under comparison have the same type. - * For example: - *```ts - *assertNotEquals(1, 2) - *``` - */ -export function assertNotEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void; -export function assertNotEquals(actual: T, expected: T, msg?: string): void; -export function assertNotEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void { - if (!equal(actual, expected)) { - return; - } - let actualString: string; - let expectedString: string; - try { - actualString = String(actual); - } catch (e) { - actualString = "[Cannot display]"; - } - try { - expectedString = String(expected); - } catch (e) { - expectedString = "[Cannot display]"; - } - if (!msg) { - msg = `actual: ${actualString} expected: ${expectedString}`; - } - throw new AssertionError(msg); -} - -/** - * Make an assertion that `actual` and `expected` are strictly equal. If - * not then throw. - * ```ts - * assertStrictEquals(1, 2) - * ``` - */ -export function assertStrictEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void; -export function assertStrictEquals( - actual: T, - expected: T, - msg?: string, -): void; -export function assertStrictEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void { - if (actual === expected) { - return; - } - - let message: string; - - if (msg) { - message = msg; - } else { - const actualString = _format(actual); - const expectedString = _format(expected); - - if (actualString === expectedString) { - const withOffset = actualString - .split("\n") - .map((l) => ` ${l}`) - .join("\n"); - message = - `Values have the same structure but are not reference-equal:\n\n${ - red(withOffset) - }\n`; - } else { - try { - const diffResult = diff( - actualString.split("\n"), - expectedString.split("\n"), - ); - const diffMsg = buildMessage(diffResult).join("\n"); - message = `Values are not strictly equal:\n${diffMsg}`; - } catch (e) { - message = `\n${red(CAN_NOT_DISPLAY)} + \n\n`; - } - } - } - - throw new AssertionError(message); -} - -/** - * Make an assertion that `actual` and `expected` are not strictly equal. - * If the values are strictly equal then throw. - * ```ts - * assertNotStrictEquals(1, 1) - * ``` - */ -export function assertNotStrictEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void; -export function assertNotStrictEquals( - actual: T, - expected: T, - msg?: string, -): void; -export function assertNotStrictEquals( - actual: unknown, - expected: unknown, - msg?: string, -): void { - if (actual !== expected) { - return; - } - - throw new AssertionError( - msg ?? `Expected "actual" to be strictly unequal to: ${_format(actual)}\n`, - ); -} - -/** - * Make an assertion that actual is not null or undefined. If not - * then thrown. - */ -export function assertExists( - actual: unknown, - msg?: string, -): void { - if (actual === undefined || actual === null) { - if (!msg) { - msg = - `actual: "${actual}" expected to match anything but null or undefined`; - } - throw new AssertionError(msg); - } -} - -/** - * Make an assertion that actual includes expected. If not - * then thrown. - */ -export function assertStringIncludes( - actual: string, - expected: string, - msg?: string, -): void { - if (!actual.includes(expected)) { - if (!msg) { - msg = `actual: "${actual}" expected to contain: "${expected}"`; - } - throw new AssertionError(msg); - } -} - -/** - * Make an assertion that `actual` includes the `expected` values. - * If not then an error will be thrown. - * - * Type parameter can be specified to ensure values under comparison have the same type. - * For example: - *```ts - *assertArrayIncludes([1, 2], [2]) - *``` - */ -export function assertArrayIncludes( - actual: ArrayLike, - expected: ArrayLike, - msg?: string, -): void; -export function assertArrayIncludes( - actual: ArrayLike, - expected: ArrayLike, - msg?: string, -): void; -export function assertArrayIncludes( - actual: ArrayLike, - expected: ArrayLike, - msg?: string, -): void { - const missing: unknown[] = []; - for (let i = 0; i < expected.length; i++) { - let found = false; - for (let j = 0; j < actual.length; j++) { - if (equal(expected[i], actual[j])) { - found = true; - break; - } - } - if (!found) { - missing.push(expected[i]); - } - } - if (missing.length === 0) { - return; - } - if (!msg) { - msg = `actual: "${_format(actual)}" expected to include: "${ - _format(expected) - }"\nmissing: ${_format(missing)}`; - } - throw new AssertionError(msg); -} - -/** - * Make an assertion that `actual` match RegExp `expected`. If not - * then thrown - */ -export function assertMatch( - actual: string, - expected: RegExp, - msg?: string, -): void { - if (!expected.test(actual)) { - if (!msg) { - msg = `actual: "${actual}" expected to match: "${expected}"`; - } - throw new AssertionError(msg); - } -} - -/** - * Make an assertion that `actual` not match RegExp `expected`. If match - * then thrown - */ -export function assertNotMatch( - actual: string, - expected: RegExp, - msg?: string, -): void { - if (expected.test(actual)) { - if (!msg) { - msg = `actual: "${actual}" expected to not match: "${expected}"`; - } - throw new AssertionError(msg); - } -} - -/** - * Make an assertion that `actual` object is a subset of `expected` object, deeply. - * If not, then throw. - */ -export function assertObjectMatch( - actual: Record, - expected: Record, -): void { - type loose = Record; - const seen = new WeakMap(); - return assertEquals( - (function filter(a: loose, b: loose): loose { - // Prevent infinite loop with circular references with same filter - if ((seen.has(a)) && (seen.get(a) === b)) { - return a; - } - seen.set(a, b); - // Filter keys and symbols which are present in both actual and expected - const filtered = {} as loose; - const entries = [ - ...Object.getOwnPropertyNames(a), - ...Object.getOwnPropertySymbols(a), - ] - .filter((key) => key in b) - .map((key) => [key, a[key as string]]) as Array<[string, unknown]>; - // Build filtered object and filter recursively on nested objects references - for (const [key, value] of entries) { - if (typeof value === "object") { - const subset = (b as loose)[key]; - if ((typeof subset === "object") && (subset)) { - filtered[key] = filter(value as loose, subset as loose); - continue; - } - } - filtered[key] = value; - } - return filtered; - })(actual, expected), - expected, - ); -} - -/** - * Forcefully throws a failed assertion - */ -export function fail(msg?: string): void { - // eslint-disable-next-line @typescript-eslint/no-use-before-define - assert(false, `Failed assertion${msg ? `: ${msg}` : "."}`); -} - -/** - * Executes a function, expecting it to throw. If it does not, then it - * throws. An error class and a string that should be included in the - * error message can also be asserted. - */ -export function assertThrows( - fn: () => T, - ErrorClass?: Constructor, - msgIncludes = "", - msg?: string, -): Error { - let doesThrow = false; - let error = null; - try { - fn(); - } catch (e) { - if (e instanceof Error === false) { - throw new AssertionError("A non-Error object was thrown."); - } - if (ErrorClass && !(e instanceof ErrorClass)) { - msg = - `Expected error to be instance of "${ErrorClass.name}", but was "${e.constructor.name}"${ - msg ? `: ${msg}` : "." - }`; - throw new AssertionError(msg); - } - if ( - msgIncludes && - !stripColor(e.message).includes(stripColor(msgIncludes)) - ) { - msg = - `Expected error message to include "${msgIncludes}", but got "${e.message}"${ - msg ? `: ${msg}` : "." - }`; - throw new AssertionError(msg); - } - doesThrow = true; - error = e; - } - if (!doesThrow) { - msg = `Expected function to throw${msg ? `: ${msg}` : "."}`; - throw new AssertionError(msg); - } - return error; -} - -/** - * Executes a function which returns a promise, expecting it to throw or reject. - * If it does not, then it throws. An error class and a string that should be - * included in the error message can also be asserted. - */ -export async function assertThrowsAsync( - fn: () => Promise, - ErrorClass?: Constructor, - msgIncludes = "", - msg?: string, -): Promise { - let doesThrow = false; - let error = null; - try { - await fn(); - } catch (e) { - if (e instanceof Error === false) { - throw new AssertionError("A non-Error object was thrown or rejected."); - } - if (ErrorClass && !(e instanceof ErrorClass)) { - msg = - `Expected error to be instance of "${ErrorClass.name}", but got "${e.name}"${ - msg ? `: ${msg}` : "." - }`; - throw new AssertionError(msg); - } - if ( - msgIncludes && - !stripColor(e.message).includes(stripColor(msgIncludes)) - ) { - msg = - `Expected error message to include "${msgIncludes}", but got "${e.message}"${ - msg ? `: ${msg}` : "." - }`; - throw new AssertionError(msg); - } - doesThrow = true; - error = e; - } - if (!doesThrow) { - msg = `Expected function to throw${msg ? `: ${msg}` : "."}`; - throw new AssertionError(msg); - } - return error; -} - -/** Use this to stub out methods that will throw when invoked. */ -export function unimplemented(msg?: string): never { - throw new AssertionError(msg || "unimplemented"); -} - -/** Use this to assert unreachable code. */ -export function unreachable(): never { - throw new AssertionError("unreachable"); -} diff --git a/std/testing/asserts_test.ts b/std/testing/asserts_test.ts deleted file mode 100644 index 14ffd29624..0000000000 --- a/std/testing/asserts_test.ts +++ /dev/null @@ -1,927 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - _format, - assert, - assertArrayIncludes, - assertEquals, - assertExists, - AssertionError, - assertMatch, - assertNotEquals, - assertNotMatch, - assertNotStrictEquals, - assertObjectMatch, - assertStrictEquals, - assertStringIncludes, - assertThrows, - assertThrowsAsync, - equal, - fail, - unimplemented, - unreachable, -} from "./asserts.ts"; -import { bold, gray, green, red, stripColor, yellow } from "../fmt/colors.ts"; - -Deno.test("testingEqual", function (): void { - assert(equal("world", "world")); - assert(!equal("hello", "world")); - assert(equal(5, 5)); - assert(!equal(5, 6)); - assert(equal(NaN, NaN)); - assert(equal({ hello: "world" }, { hello: "world" })); - assert(!equal({ world: "hello" }, { hello: "world" })); - assert( - equal( - { hello: "world", hi: { there: "everyone" } }, - { hello: "world", hi: { there: "everyone" } }, - ), - ); - assert( - !equal( - { hello: "world", hi: { there: "everyone" } }, - { hello: "world", hi: { there: "everyone else" } }, - ), - ); - assert(equal(/deno/, /deno/)); - assert(!equal(/deno/, /node/)); - assert(equal(new Date(2019, 0, 3), new Date(2019, 0, 3))); - assert(!equal(new Date(2019, 0, 3), new Date(2019, 1, 3))); - assert( - !equal( - new Date(2019, 0, 3, 4, 20, 1, 10), - new Date(2019, 0, 3, 4, 20, 1, 20), - ), - ); - assert(equal(new Date("Invalid"), new Date("Invalid"))); - assert(!equal(new Date("Invalid"), new Date(2019, 0, 3))); - assert(!equal(new Date("Invalid"), new Date(2019, 0, 3, 4, 20, 1, 10))); - assert(equal(new Set([1]), new Set([1]))); - assert(!equal(new Set([1]), new Set([2]))); - assert(equal(new Set([1, 2, 3]), new Set([3, 2, 1]))); - assert(equal(new Set([1, new Set([2, 3])]), new Set([new Set([3, 2]), 1]))); - assert(!equal(new Set([1, 2]), new Set([3, 2, 1]))); - assert(!equal(new Set([1, 2, 3]), new Set([4, 5, 6]))); - assert(equal(new Set("denosaurus"), new Set("denosaurussss"))); - assert(equal(new Map(), new Map())); - assert( - equal( - new Map([ - ["foo", "bar"], - ["baz", "baz"], - ]), - new Map([ - ["foo", "bar"], - ["baz", "baz"], - ]), - ), - ); - assert( - equal( - new Map([["foo", new Map([["bar", "baz"]])]]), - new Map([["foo", new Map([["bar", "baz"]])]]), - ), - ); - assert( - equal( - new Map([["foo", { bar: "baz" }]]), - new Map([["foo", { bar: "baz" }]]), - ), - ); - assert( - equal( - new Map([ - ["foo", "bar"], - ["baz", "qux"], - ]), - new Map([ - ["baz", "qux"], - ["foo", "bar"], - ]), - ), - ); - assert(equal(new Map([["foo", ["bar"]]]), new Map([["foo", ["bar"]]]))); - assert(!equal(new Map([["foo", "bar"]]), new Map([["bar", "baz"]]))); - assert( - !equal( - new Map([["foo", "bar"]]), - new Map([ - ["foo", "bar"], - ["bar", "baz"], - ]), - ), - ); - assert( - !equal( - new Map([["foo", new Map([["bar", "baz"]])]]), - new Map([["foo", new Map([["bar", "qux"]])]]), - ), - ); - assert(equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 1 }, true]]))); - assert(!equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 1 }, false]]))); - assert(!equal(new Map([[{ x: 1 }, true]]), new Map([[{ x: 2 }, true]]))); - assert(equal([1, 2, 3], [1, 2, 3])); - assert(equal([1, [2, 3]], [1, [2, 3]])); - assert(!equal([1, 2, 3, 4], [1, 2, 3])); - assert(!equal([1, 2, 3, 4], [1, 2, 3])); - assert(!equal([1, 2, 3, 4], [1, 4, 2, 3])); - assert(equal(new Uint8Array([1, 2, 3, 4]), new Uint8Array([1, 2, 3, 4]))); - assert(!equal(new Uint8Array([1, 2, 3, 4]), new Uint8Array([2, 1, 4, 3]))); - assert( - equal(new URL("https://example.test"), new URL("https://example.test")), - ); - assert( - !equal( - new URL("https://example.test"), - new URL("https://example.test/with-path"), - ), - ); -}); - -Deno.test("testingNotEquals", function (): void { - const a = { foo: "bar" }; - const b = { bar: "foo" }; - assertNotEquals(a, b); - assertNotEquals("Denosaurus", "Tyrannosaurus"); - assertNotEquals( - new Date(2019, 0, 3, 4, 20, 1, 10), - new Date(2019, 0, 3, 4, 20, 1, 20), - ); - assertNotEquals( - new Date("invalid"), - new Date(2019, 0, 3, 4, 20, 1, 20), - ); - let didThrow; - try { - assertNotEquals("Raptor", "Raptor"); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); -}); - -Deno.test("testingAssertExists", function (): void { - assertExists("Denosaurus"); - assertExists(false); - assertExists(0); - assertExists(""); - assertExists(-0); - assertExists(0); - assertExists(NaN); - let didThrow; - try { - assertExists(undefined); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - didThrow = false; - try { - assertExists(null); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); -}); - -Deno.test("testingAssertStringContains", function (): void { - assertStringIncludes("Denosaurus", "saur"); - assertStringIncludes("Denosaurus", "Deno"); - assertStringIncludes("Denosaurus", "rus"); - let didThrow; - try { - assertStringIncludes("Denosaurus", "Raptor"); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); -}); - -Deno.test("testingArrayContains", function (): void { - const fixture = ["deno", "iz", "luv"]; - const fixtureObject = [{ deno: "luv" }, { deno: "Js" }]; - assertArrayIncludes(fixture, ["deno"]); - assertArrayIncludes(fixtureObject, [{ deno: "luv" }]); - assertArrayIncludes( - Uint8Array.from([1, 2, 3, 4]), - Uint8Array.from([1, 2, 3]), - ); - assertThrows( - (): void => assertArrayIncludes(fixtureObject, [{ deno: "node" }]), - AssertionError, - `actual: "[ - { - deno: "luv", - }, - { - deno: "Js", - }, -]" expected to include: "[ - { - deno: "node", - }, -]" -missing: [ - { - deno: "node", - }, -]`, - ); -}); - -Deno.test("testingAssertStringContainsThrow", function (): void { - let didThrow = false; - try { - assertStringIncludes("Denosaurus from Jurassic", "Raptor"); - } catch (e) { - assert( - e.message === - `actual: "Denosaurus from Jurassic" expected to contain: "Raptor"`, - ); - assert(e instanceof AssertionError); - didThrow = true; - } - assert(didThrow); -}); - -Deno.test("testingAssertStringMatching", function (): void { - assertMatch("foobar@deno.com", RegExp(/[a-zA-Z]+@[a-zA-Z]+.com/)); -}); - -Deno.test("testingAssertStringMatchingThrows", function (): void { - let didThrow = false; - try { - assertMatch("Denosaurus from Jurassic", RegExp(/Raptor/)); - } catch (e) { - assert( - e.message === - `actual: "Denosaurus from Jurassic" expected to match: "/Raptor/"`, - ); - assert(e instanceof AssertionError); - didThrow = true; - } - assert(didThrow); -}); - -Deno.test("testingAssertStringNotMatching", function (): void { - assertNotMatch("foobar.deno.com", RegExp(/[a-zA-Z]+@[a-zA-Z]+.com/)); -}); - -Deno.test("testingAssertStringNotMatchingThrows", function (): void { - let didThrow = false; - try { - assertNotMatch("Denosaurus from Jurassic", RegExp(/from/)); - } catch (e) { - assert( - e.message === - `actual: "Denosaurus from Jurassic" expected to not match: "/from/"`, - ); - assert(e instanceof AssertionError); - didThrow = true; - } - assert(didThrow); -}); - -Deno.test("testingAssertObjectMatching", function (): void { - const sym = Symbol("foo"); - const a = { foo: true, bar: false }; - const b = { ...a, baz: a }; - const c = { ...b, qux: b }; - const d = { corge: c, grault: c }; - const e = { foo: true } as { [key: string]: unknown }; - e.bar = e; - const f = { [sym]: true, bar: false }; - // Simple subset - assertObjectMatch(a, { - foo: true, - }); - // Subset with another subset - assertObjectMatch(b, { - foo: true, - baz: { bar: false }, - }); - // Subset with multiple subsets - assertObjectMatch(c, { - foo: true, - baz: { bar: false }, - qux: { - baz: { foo: true }, - }, - }); - // Subset with same object reference as subset - assertObjectMatch(d, { - corge: { - foo: true, - qux: { bar: false }, - }, - grault: { - bar: false, - qux: { foo: true }, - }, - }); - // Subset with circular reference - assertObjectMatch(e, { - foo: true, - bar: { - bar: { - bar: { - foo: true, - }, - }, - }, - }); - // Subset with same symbol - assertObjectMatch(f, { - [sym]: true, - }); - // Missing key - { - let didThrow; - try { - assertObjectMatch({ - foo: true, - }, { - foo: true, - bar: false, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } - // Simple subset - { - let didThrow; - try { - assertObjectMatch(a, { - foo: false, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } - // Subset with another subset - { - let didThrow; - try { - assertObjectMatch(b, { - foo: true, - baz: { bar: true }, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } - // Subset with multiple subsets - { - let didThrow; - try { - assertObjectMatch(c, { - foo: true, - baz: { bar: false }, - qux: { - baz: { foo: false }, - }, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } - // Subset with same object reference as subset - { - let didThrow; - try { - assertObjectMatch(d, { - corge: { - foo: true, - qux: { bar: true }, - }, - grault: { - bar: false, - qux: { foo: false }, - }, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } - // Subset with circular reference - { - let didThrow; - try { - assertObjectMatch(e, { - foo: true, - bar: { - bar: { - bar: { - foo: false, - }, - }, - }, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } - // Subset with symbol key but with string key subset - { - let didThrow; - try { - assertObjectMatch(f, { - foo: true, - }); - didThrow = false; - } catch (e) { - assert(e instanceof AssertionError); - didThrow = true; - } - assertEquals(didThrow, true); - } -}); - -Deno.test("testingAssertsUnimplemented", function (): void { - let didThrow = false; - try { - unimplemented(); - } catch (e) { - assert(e.message === "unimplemented"); - assert(e instanceof AssertionError); - didThrow = true; - } - assert(didThrow); -}); - -Deno.test("testingAssertsUnreachable", function (): void { - let didThrow = false; - try { - unreachable(); - } catch (e) { - assert(e.message === "unreachable"); - assert(e instanceof AssertionError); - didThrow = true; - } - assert(didThrow); -}); - -Deno.test("testingAssertFail", function (): void { - assertThrows(fail, AssertionError, "Failed assertion."); - assertThrows( - (): void => { - fail("foo"); - }, - AssertionError, - "Failed assertion: foo", - ); -}); - -Deno.test("testingAssertFailWithWrongErrorClass", function (): void { - assertThrows( - (): void => { - //This next assertThrows will throw an AssertionError due to the wrong - //expected error class - assertThrows( - (): void => { - fail("foo"); - }, - TypeError, - "Failed assertion: foo", - ); - }, - AssertionError, - `Expected error to be instance of "TypeError", but was "AssertionError"`, - ); -}); - -Deno.test("testingAssertThrowsWithReturnType", () => { - assertThrows(() => { - throw new Error(); - }); -}); - -Deno.test("testingAssertThrowsAsyncWithReturnType", () => { - assertThrowsAsync(() => { - throw new Error(); - }); -}); - -const createHeader = (): string[] => [ - "", - "", - ` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${ - green( - bold("Expected"), - ) - }`, - "", - "", -]; - -const added: (s: string) => string = (s: string): string => - green(bold(stripColor(s))); -const removed: (s: string) => string = (s: string): string => - red(bold(stripColor(s))); - -Deno.test({ - name: "pass case", - fn(): void { - assertEquals({ a: 10 }, { a: 10 }); - assertEquals(true, true); - assertEquals(10, 10); - assertEquals("abc", "abc"); - assertEquals({ a: 10, b: { c: "1" } }, { a: 10, b: { c: "1" } }); - assertEquals(new Date("invalid"), new Date("invalid")); - }, -}); - -Deno.test({ - name: "failed with number", - fn(): void { - assertThrows( - (): void => assertEquals(1, 2), - AssertionError, - [ - "Values are not equal:", - ...createHeader(), - removed(`- ${yellow("1")}`), - added(`+ ${yellow("2")}`), - "", - ].join("\n"), - ); - }, -}); - -Deno.test({ - name: "failed with number vs string", - fn(): void { - assertThrows( - (): void => assertEquals(1, "1"), - AssertionError, - [ - "Values are not equal:", - ...createHeader(), - removed(`- ${yellow("1")}`), - added(`+ "1"`), - ].join("\n"), - ); - }, -}); - -Deno.test({ - name: "failed with array", - fn(): void { - assertThrows( - (): void => assertEquals([1, "2", 3], ["1", "2", 3]), - AssertionError, - ` - [ -- 1, -+ "1", - "2", - 3, - ]`, - ); - }, -}); - -Deno.test({ - name: "failed with object", - fn(): void { - assertThrows( - (): void => assertEquals({ a: 1, b: "2", c: 3 }, { a: 1, b: 2, c: [3] }), - AssertionError, - ` - { - a: 1, -+ b: 2, -+ c: [ -+ 3, -+ ], -- b: "2", -- c: 3, - }`, - ); - }, -}); - -Deno.test({ - name: "failed with date", - fn(): void { - assertThrows( - (): void => - assertEquals( - new Date(2019, 0, 3, 4, 20, 1, 10), - new Date(2019, 0, 3, 4, 20, 1, 20), - ), - AssertionError, - [ - "Values are not equal:", - ...createHeader(), - removed(`- ${new Date(2019, 0, 3, 4, 20, 1, 10).toISOString()}`), - added(`+ ${new Date(2019, 0, 3, 4, 20, 1, 20).toISOString()}`), - "", - ].join("\n"), - ); - assertThrows( - (): void => - assertEquals( - new Date("invalid"), - new Date(2019, 0, 3, 4, 20, 1, 20), - ), - AssertionError, - [ - "Values are not equal:", - ...createHeader(), - removed(`- ${new Date("invalid")}`), - added(`+ ${new Date(2019, 0, 3, 4, 20, 1, 20).toISOString()}`), - "", - ].join("\n"), - ); - }, -}); - -Deno.test({ - name: "strict pass case", - fn(): void { - assertStrictEquals(true, true); - assertStrictEquals(10, 10); - assertStrictEquals("abc", "abc"); - - const xs = [1, false, "foo"]; - const ys = xs; - assertStrictEquals(xs, ys); - - const x = { a: 1 }; - const y = x; - assertStrictEquals(x, y); - }, -}); - -Deno.test({ - name: "strict failed with structure diff", - fn(): void { - assertThrows( - (): void => assertStrictEquals({ a: 1, b: 2 }, { a: 1, c: [3] }), - AssertionError, - ` - { - a: 1, -+ c: [ -+ 3, -+ ], -- b: 2, - }`, - ); - }, -}); - -Deno.test({ - name: "strict failed with reference diff", - fn(): void { - assertThrows( - (): void => assertStrictEquals({ a: 1, b: 2 }, { a: 1, b: 2 }), - AssertionError, - `Values have the same structure but are not reference-equal: - - { - a: 1, - b: 2, - }`, - ); - }, -}); - -Deno.test({ - name: "strictly unequal pass case", - fn(): void { - assertNotStrictEquals(true, false); - assertNotStrictEquals(10, 11); - assertNotStrictEquals("abc", "xyz"); - assertNotStrictEquals(1, "1"); - - const xs = [1, false, "foo"]; - const ys = [1, true, "bar"]; - assertNotStrictEquals(xs, ys); - - const x = { a: 1 }; - const y = { a: 2 }; - assertNotStrictEquals(x, y); - }, -}); - -Deno.test({ - name: "strictly unequal fail case", - fn(): void { - assertThrows(() => assertNotStrictEquals(1, 1), AssertionError); - }, -}); - -Deno.test({ - name: "assert* functions with specified type parameter", - fn(): void { - assertEquals("hello", "hello"); - assertNotEquals(1, 2); - assertArrayIncludes([true, false], [true]); - const value = { x: 1 }; - assertStrictEquals(value, value); - // deno-lint-ignore ban-types - assertNotStrictEquals(value, { x: 1 }); - }, -}); - -Deno.test("Assert Throws Non-Error Fail", () => { - assertThrows( - () => { - assertThrows( - () => { - throw "Panic!"; - }, - String, - "Panic!", - ); - }, - AssertionError, - "A non-Error object was thrown.", - ); - - assertThrows( - () => { - assertThrows(() => { - throw null; - }); - }, - AssertionError, - "A non-Error object was thrown.", - ); - - assertThrows( - () => { - assertThrows(() => { - throw undefined; - }); - }, - AssertionError, - "A non-Error object was thrown.", - ); -}); - -Deno.test("Assert Throws Async Non-Error Fail", () => { - assertThrowsAsync( - () => { - return assertThrowsAsync( - () => { - return Promise.reject("Panic!"); - }, - String, - "Panic!", - ); - }, - AssertionError, - "A non-Error object was thrown or rejected.", - ); - - assertThrowsAsync( - () => { - return assertThrowsAsync(() => { - return Promise.reject(null); - }); - }, - AssertionError, - "A non-Error object was thrown or rejected.", - ); - - assertThrowsAsync( - () => { - return assertThrowsAsync(() => { - return Promise.reject(undefined); - }); - }, - AssertionError, - "A non-Error object was thrown or rejected.", - ); - - assertThrowsAsync( - () => { - return assertThrowsAsync(() => { - throw undefined; - }); - }, - AssertionError, - "A non-Error object was thrown or rejected.", - ); -}); - -Deno.test("assertEquals diff for differently ordered objects", () => { - assertThrows( - () => { - assertEquals( - { - aaaaaaaaaaaaaaaaaaaaaaaa: 0, - bbbbbbbbbbbbbbbbbbbbbbbb: 0, - ccccccccccccccccccccccc: 0, - }, - { - ccccccccccccccccccccccc: 1, - aaaaaaaaaaaaaaaaaaaaaaaa: 0, - bbbbbbbbbbbbbbbbbbbbbbbb: 0, - }, - ); - }, - AssertionError, - ` - { - aaaaaaaaaaaaaaaaaaaaaaaa: 0, - bbbbbbbbbbbbbbbbbbbbbbbb: 0, -- ccccccccccccccccccccccc: 0, -+ ccccccccccccccccccccccc: 1, - }`, - ); -}); - -// Check that the diff formatter overrides some default behaviours of -// `Deno.inspect()` which are problematic for diffing. -Deno.test("assert diff formatting", () => { - // Wraps objects into multiple lines even when they are small. Prints trailing - // commas. - assertEquals( - stripColor(_format({ a: 1, b: 2 })), - `{ - a: 1, - b: 2, -}`, - ); - - // Same for nested small objects. - assertEquals( - stripColor(_format([{ x: { a: 1, b: 2 }, y: ["a", "b"] }])), - `[ - { - x: { - a: 1, - b: 2, - }, - y: [ - "a", - "b", - ], - }, -]`, - ); - - // Grouping is disabled. - assertEquals( - stripColor(_format(["i", "i", "i", "i", "i", "i", "i"])), - `[ - "i", - "i", - "i", - "i", - "i", - "i", - "i", -]`, - ); -}); - -Deno.test("Assert Throws Parent Error", () => { - assertThrows( - () => { - throw new AssertionError("Fail!"); - }, - Error, - "Fail!", - ); -}); - -Deno.test("Assert Throws Async Parent Error", () => { - assertThrowsAsync( - () => { - throw new AssertionError("Fail!"); - }, - Error, - "Fail!", - ); -}); diff --git a/std/testing/bench.ts b/std/testing/bench.ts deleted file mode 100644 index 159a70cf74..0000000000 --- a/std/testing/bench.ts +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert } from "../_util/assert.ts"; -import { deepAssign } from "../_util/deep_assign.ts"; - -interface BenchmarkClock { - start: number; - stop: number; - for?: string; -} - -/** Provides methods for starting and stopping a benchmark clock. */ -export interface BenchmarkTimer { - start: () => void; - stop: () => void; -} - -/** Defines a benchmark through a named function. */ -export interface BenchmarkFunction { - (b: BenchmarkTimer): void | Promise; - name: string; -} - -/** Defines a benchmark definition with configurable runs. */ -export interface BenchmarkDefinition { - func: BenchmarkFunction; - name: string; - /** Defines how many times the provided `func` should be benchmarked in succession */ - runs?: number; -} - -/** Defines runBenchmark's run constraints by matching benchmark names. */ -export interface BenchmarkRunOptions { - /** Only benchmarks which name match this regexp will be run*/ - only?: RegExp; - /** Benchmarks which name match this regexp will be skipped */ - skip?: RegExp; - /** Setting it to true prevents default benchmarking progress logs to the commandline*/ - silent?: boolean; -} - -/** Defines clearBenchmark's constraints by matching benchmark names. */ -export interface BenchmarkClearOptions { - /** Only benchmarks which name match this regexp will be removed */ - only?: RegExp; - /** Benchmarks which name match this regexp will be kept */ - skip?: RegExp; -} - -/** Defines the result of a single benchmark */ -export interface BenchmarkResult { - /** The name of the benchmark */ - name: string; - /** The total time it took to run a given bechmark */ - totalMs: number; - /** Times the benchmark was run in succession. */ - runsCount: number; - /** The average time of running the benchmark in milliseconds. */ - measuredRunsAvgMs: number; - /** The individual measurements in milliseconds it took to run the benchmark.*/ - measuredRunsMs: number[]; -} - -/** Defines the result of a `runBenchmarks` call */ -export interface BenchmarkRunResult { - /** How many benchmark were ignored by the provided `only` and `skip` */ - filtered: number; - /** The individual results for each benchmark that was run */ - results: BenchmarkResult[]; -} - -/** Defines the current progress during the run of `runBenchmarks` */ -export interface BenchmarkRunProgress extends BenchmarkRunResult { - /** List of the queued benchmarks to run with their name and their run count */ - queued?: Array<{ name: string; runsCount: number }>; - /** The currently running benchmark with its name, run count and the already finished measurements in milliseconds */ - running?: { name: string; runsCount: number; measuredRunsMs: number[] }; - /** Indicates in which state benchmarking currently is */ - state?: ProgressState; -} - -/** Defines the states `BenchmarkRunProgress` can be in */ -export enum ProgressState { - BenchmarkingStart = "benchmarking_start", - BenchStart = "bench_start", - BenchPartialResult = "bench_partial_result", - BenchResult = "bench_result", - BenchmarkingEnd = "benchmarking_end", -} - -export class BenchmarkRunError extends Error { - benchmarkName?: string; - constructor(msg: string, benchmarkName?: string) { - super(msg); - this.name = "BenchmarkRunError"; - this.benchmarkName = benchmarkName; - } -} - -function red(text: string): string { - return Deno.noColor ? text : `\x1b[31m${text}\x1b[0m`; -} - -function blue(text: string): string { - return Deno.noColor ? text : `\x1b[34m${text}\x1b[0m`; -} - -function verifyOr1Run(runs?: number): number { - return runs && runs >= 1 && runs !== Infinity ? Math.floor(runs) : 1; -} - -function assertTiming(clock: BenchmarkClock): void { - // NaN indicates that a benchmark has not been timed properly - if (!clock.stop) { - throw new BenchmarkRunError( - `Running benchmarks FAILED during benchmark named [${clock.for}]. The benchmark timer's stop method must be called`, - clock.for, - ); - } else if (!clock.start) { - throw new BenchmarkRunError( - `Running benchmarks FAILED during benchmark named [${clock.for}]. The benchmark timer's start method must be called`, - clock.for, - ); - } else if (clock.start > clock.stop) { - throw new BenchmarkRunError( - `Running benchmarks FAILED during benchmark named [${clock.for}]. The benchmark timer's start method must be called before its stop method`, - clock.for, - ); - } -} - -function createBenchmarkTimer(clock: BenchmarkClock): BenchmarkTimer { - return { - start(): void { - clock.start = performance.now(); - }, - stop(): void { - if (isNaN(clock.start)) { - throw new BenchmarkRunError( - `Running benchmarks FAILED during benchmark named [${clock.for}]. The benchmark timer's start method must be called before its stop method`, - clock.for, - ); - } - clock.stop = performance.now(); - }, - }; -} - -const candidates: BenchmarkDefinition[] = []; - -/** Registers a benchmark as a candidate for the runBenchmarks executor. */ -export function bench( - benchmark: BenchmarkDefinition | BenchmarkFunction, -): void { - if (!benchmark.name) { - throw new Error("The benchmark function must not be anonymous"); - } - if (typeof benchmark === "function") { - candidates.push({ name: benchmark.name, runs: 1, func: benchmark }); - } else { - candidates.push({ - name: benchmark.name, - runs: verifyOr1Run(benchmark.runs), - func: benchmark.func, - }); - } -} - -/** Clears benchmark candidates which name matches `only` and doesn't match `skip`. - * Removes all candidates if options were not provided */ -export function clearBenchmarks({ - only = /[^\s]/, - skip = /$^/, -}: BenchmarkClearOptions = {}): void { - const keep = candidates.filter( - ({ name }): boolean => !only.test(name) || skip.test(name), - ); - candidates.splice(0, candidates.length); - candidates.push(...keep); -} - -/** - * Runs all registered and non-skipped benchmarks serially. - * - * @param [progressCb] provides the possibility to get updates of the current progress during the run of the benchmarking - * @returns results of the benchmarking - */ -export async function runBenchmarks( - { only = /[^\s]/, skip = /^\s*$/, silent }: BenchmarkRunOptions = {}, - progressCb?: (progress: BenchmarkRunProgress) => void | Promise, -): Promise { - // Filtering candidates by the "only" and "skip" constraint - const benchmarks: BenchmarkDefinition[] = candidates.filter( - ({ name }): boolean => only.test(name) && !skip.test(name), - ); - // Init main counters and error flag - const filtered = candidates.length - benchmarks.length; - let failError: Error | undefined = undefined; - // Setting up a shared benchmark clock and timer - const clock: BenchmarkClock = { start: NaN, stop: NaN }; - const b = createBenchmarkTimer(clock); - - // Init progress data - const progress: BenchmarkRunProgress = { - // bench.run is already ensured with verifyOr1Run on register - queued: benchmarks.map((bench) => ({ - name: bench.name, - runsCount: bench.runs!, - })), - results: [], - filtered, - state: ProgressState.BenchmarkingStart, - }; - - // Publish initial progress data - await publishProgress(progress, ProgressState.BenchmarkingStart, progressCb); - - if (!silent) { - console.log( - "running", - benchmarks.length, - `benchmark${benchmarks.length === 1 ? " ..." : "s ..."}`, - ); - } - - // Iterating given benchmark definitions (await-in-loop) - for (const { name, runs = 0, func } of benchmarks) { - if (!silent) { - // See https://github.com/denoland/deno/pull/1452 about groupCollapsed - console.groupCollapsed(`benchmark ${name} ... `); - } - - // Provide the benchmark name for clock assertions - clock.for = name; - - // Remove benchmark from queued - assert(progress.queued); - const queueIndex = progress.queued.findIndex( - (queued) => queued.name === name && queued.runsCount === runs, - ); - if (queueIndex != -1) { - progress.queued.splice(queueIndex, 1); - } - // Init the progress of the running benchmark - progress.running = { name, runsCount: runs, measuredRunsMs: [] }; - // Publish starting of a benchmark - await publishProgress(progress, ProgressState.BenchStart, progressCb); - - // Trying benchmark.func - let result = ""; - try { - // Averaging runs - let pendingRuns = runs; - let totalMs = 0; - - // Would be better 2 not run these serially - while (true) { - // b is a benchmark timer interfacing an unset (NaN) benchmark clock - await func(b); - // Making sure the benchmark was started/stopped properly - assertTiming(clock); - - // Calculate length of run - const measuredMs = clock.stop - clock.start; - - // Summing up - totalMs += measuredMs; - // Adding partial result - progress.running.measuredRunsMs.push(measuredMs); - // Publish partial benchmark results - await publishProgress( - progress, - ProgressState.BenchPartialResult, - progressCb, - ); - - // Resetting the benchmark clock - clock.start = clock.stop = NaN; - // Once all ran - if (!--pendingRuns) { - result = runs == 1 - ? `${totalMs}ms` - : `${runs} runs avg: ${totalMs / runs}ms`; - // Adding results - progress.results.push({ - name, - totalMs, - runsCount: runs, - measuredRunsAvgMs: totalMs / runs, - measuredRunsMs: progress.running.measuredRunsMs, - }); - // Clear currently running - delete progress.running; - // Publish results of the benchmark - await publishProgress( - progress, - ProgressState.BenchResult, - progressCb, - ); - break; - } - } - } catch (err) { - failError = err; - - if (!silent) { - console.groupEnd(); - console.error(red(err.stack)); - } - - break; - } - - if (!silent) { - // Reporting - console.log(blue(result)); - console.groupEnd(); - } - - // Resetting the benchmark clock - clock.start = clock.stop = NaN; - delete clock.for; - } - - // Indicate finished running - delete progress.queued; - // Publish final result in Cb too - await publishProgress(progress, ProgressState.BenchmarkingEnd, progressCb); - - if (!silent) { - // Closing results - console.log( - `benchmark result: ${failError ? red("FAIL") : blue("DONE")}. ` + - `${progress.results.length} measured; ${filtered} filtered`, - ); - } - - // Throw error if there was a failing benchmark - if (failError) { - throw failError; - } - - const benchmarkRunResult = { - filtered, - results: progress.results, - }; - - return benchmarkRunResult; -} - -async function publishProgress( - progress: BenchmarkRunProgress, - state: ProgressState, - progressCb?: (progress: BenchmarkRunProgress) => void | Promise, -): Promise { - progressCb && (await progressCb(cloneProgressWithState(progress, state))); -} - -function cloneProgressWithState( - progress: BenchmarkRunProgress, - state: ProgressState, -): BenchmarkRunProgress { - return deepAssign({}, progress, { state }) as BenchmarkRunProgress; -} diff --git a/std/testing/bench_example.ts b/std/testing/bench_example.ts deleted file mode 100644 index 5af277f1c9..0000000000 --- a/std/testing/bench_example.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// https://deno.land/std/testing/bench.ts -import { bench, BenchmarkTimer, runBenchmarks } from "./bench.ts"; - -// Basic -bench(function forIncrementX1e9(b: BenchmarkTimer): void { - b.start(); - for (let i = 0; i < 1e9; i++); - b.stop(); -}); - -// Reporting average measured time for $runs runs of func -bench({ - name: "runs100ForIncrementX1e6", - runs: 100, - func(b): void { - b.start(); - for (let i = 0; i < 1e6; i++); - b.stop(); - }, -}); - -// Itsabug -bench(function throwing(b): void { - b.start(); - // Throws bc the timer's stop method is never called -}); - -// Bench control -if (import.meta.main) { - runBenchmarks({ skip: /throw/ }); -} diff --git a/std/testing/bench_test.ts b/std/testing/bench_test.ts deleted file mode 100644 index 6ce7796cac..0000000000 --- a/std/testing/bench_test.ts +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - bench, - BenchmarkRunError, - BenchmarkRunProgress, - clearBenchmarks, - ProgressState, - runBenchmarks, -} from "./bench.ts"; -import { - assert, - assertEquals, - assertThrows, - assertThrowsAsync, -} from "./asserts.ts"; - -Deno.test({ - name: "benching", - - fn: async function (): Promise { - bench(function forIncrementX1e3(b): void { - b.start(); - for (let i = 0; i < 1e3; i++); - b.stop(); - }); - - bench(function forDecrementX1e3(b): void { - b.start(); - for (let i = 1e3; i > 0; i--); - b.stop(); - }); - - bench(async function forAwaitFetchDenolandX10(b): Promise { - b.start(); - for (let i = 0; i < 10; i++) { - const r = await fetch("https://deno.land/"); - await r.text(); - } - b.stop(); - }); - - bench(async function promiseAllFetchDenolandX10(b): Promise { - const urls = new Array(10).fill("https://deno.land/"); - b.start(); - await Promise.all( - urls.map( - async (denoland: string): Promise => { - const r = await fetch(denoland); - await r.text(); - }, - ), - ); - b.stop(); - }); - - bench({ - name: "runs100ForIncrementX1e6", - runs: 100, - func(b): void { - b.start(); - for (let i = 0; i < 1e6; i++); - b.stop(); - }, - }); - - bench(function throwing(b): void { - b.start(); - // Throws bc the timer's stop method is never called - }); - - const benchResult = await runBenchmarks({ skip: /throw/ }); - - assertEquals(benchResult.filtered, 1); - assertEquals(benchResult.results.length, 5); - - const resultWithSingleRunsFiltered = benchResult.results.filter( - ({ name }) => name === "forDecrementX1e3", - ); - assertEquals(resultWithSingleRunsFiltered.length, 1); - - const resultWithSingleRuns = resultWithSingleRunsFiltered[0]; - assert(!!resultWithSingleRuns.runsCount); - assert(!!resultWithSingleRuns.measuredRunsAvgMs); - assert(!!resultWithSingleRuns.measuredRunsMs); - assertEquals(resultWithSingleRuns.runsCount, 1); - assertEquals(resultWithSingleRuns.measuredRunsMs.length, 1); - - const resultWithMultipleRunsFiltered = benchResult.results.filter( - ({ name }) => name === "runs100ForIncrementX1e6", - ); - assertEquals(resultWithMultipleRunsFiltered.length, 1); - - const resultWithMultipleRuns = resultWithMultipleRunsFiltered[0]; - assert(!!resultWithMultipleRuns.runsCount); - assert(!!resultWithMultipleRuns.measuredRunsAvgMs); - assert(!!resultWithMultipleRuns.measuredRunsMs); - assertEquals(resultWithMultipleRuns.runsCount, 100); - assertEquals(resultWithMultipleRuns.measuredRunsMs.length, 100); - - clearBenchmarks(); - }, -}); - -Deno.test({ - name: "Bench without name should throw", - fn() { - assertThrows( - (): void => { - bench(() => {}); - }, - Error, - "The benchmark function must not be anonymous", - ); - }, -}); - -Deno.test({ - name: "Bench without stop should throw", - fn: async function (): Promise { - await assertThrowsAsync( - async (): Promise => { - bench(function benchWithoutStop(b): void { - b.start(); - // Throws bc the timer's stop method is never called - }); - await runBenchmarks({ only: /benchWithoutStop/, silent: true }); - }, - BenchmarkRunError, - "The benchmark timer's stop method must be called", - ); - }, -}); - -Deno.test({ - name: "Bench without start should throw", - fn: async function (): Promise { - await assertThrowsAsync( - async (): Promise => { - bench(function benchWithoutStart(b): void { - b.stop(); - // Throws bc the timer's start method is never called - }); - await runBenchmarks({ only: /benchWithoutStart/, silent: true }); - }, - BenchmarkRunError, - "The benchmark timer's start method must be called", - ); - }, -}); - -Deno.test({ - name: "Bench with stop before start should throw", - fn: async function (): Promise { - await assertThrowsAsync( - async (): Promise => { - bench(function benchStopBeforeStart(b): void { - b.stop(); - b.start(); - // Throws bc the timer's stop is called before start - }); - await runBenchmarks({ only: /benchStopBeforeStart/, silent: true }); - }, - BenchmarkRunError, - "The benchmark timer's start method must be called before its stop method", - ); - }, -}); - -Deno.test({ - name: "clearBenchmarks should clear all candidates", - fn: async function (): Promise { - dummyBench("test"); - - clearBenchmarks(); - const benchingResults = await runBenchmarks({ silent: true }); - - assertEquals(benchingResults.filtered, 0); - assertEquals(benchingResults.results.length, 0); - }, -}); - -Deno.test({ - name: "clearBenchmarks with only as option", - fn: async function (): Promise { - // to reset candidates - clearBenchmarks(); - - dummyBench("test"); - dummyBench("onlyclear"); - - clearBenchmarks({ only: /only/ }); - const benchingResults = await runBenchmarks({ silent: true }); - - assertEquals(benchingResults.filtered, 0); - assertEquals(benchingResults.results.length, 1); - assertEquals(benchingResults.results[0].name, "test"); - }, -}); - -Deno.test({ - name: "clearBenchmarks with skip as option", - fn: async function (): Promise { - // to reset candidates - clearBenchmarks(); - - dummyBench("test"); - dummyBench("skipclear"); - - clearBenchmarks({ skip: /skip/ }); - const benchingResults = await runBenchmarks({ silent: true }); - - assertEquals(benchingResults.filtered, 0); - assertEquals(benchingResults.results.length, 1); - assertEquals(benchingResults.results[0].name, "skipclear"); - }, -}); - -Deno.test({ - name: "clearBenchmarks with only and skip as option", - fn: async function (): Promise { - // to reset candidates - clearBenchmarks(); - - dummyBench("test"); - dummyBench("clearonly"); - dummyBench("clearskip"); - dummyBench("clearonly"); - - clearBenchmarks({ only: /clear/, skip: /skip/ }); - const benchingResults = await runBenchmarks({ silent: true }); - - assertEquals(benchingResults.filtered, 0); - assertEquals(benchingResults.results.length, 2); - assert(!!benchingResults.results.find(({ name }) => name === "test")); - assert(!!benchingResults.results.find(({ name }) => name === "clearskip")); - }, -}); - -Deno.test({ - name: "progressCallback of runBenchmarks", - fn: async function (): Promise { - clearBenchmarks(); - dummyBench("skip"); - dummyBench("single"); - dummyBench("multiple", 2); - - const progressCallbacks: BenchmarkRunProgress[] = []; - - const benchingResults = await runBenchmarks( - { skip: /skip/, silent: true }, - (progress) => { - progressCallbacks.push(progress); - }, - ); - - let pc = 0; - // Assert initial progress before running - let progress = progressCallbacks[pc++]; - assert(progress.queued); - assertEquals(progress.state, ProgressState.BenchmarkingStart); - assertEquals(progress.filtered, 1); - assertEquals(progress.queued.length, 2); - assertEquals(progress.running, undefined); - assertEquals(progress.results, []); - - // Assert start of bench "single" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchStart); - assertEquals(progress.filtered, 1); - assert(progress.queued); - assertEquals(progress.queued.length, 1); - assert(!!progress.queued.find(({ name }) => name == "multiple")); - assertEquals(progress.running, { - name: "single", - runsCount: 1, - measuredRunsMs: [], - }); - assertEquals(progress.results, []); - - // Assert running result of bench "single" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchPartialResult); - assert(progress.queued); - assertEquals(progress.queued.length, 1); - assertEquals(progress.running!.measuredRunsMs.length, 1); - assertEquals(progress.results.length, 0); - - // Assert result of bench "single" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchResult); - assert(progress.queued); - assertEquals(progress.queued.length, 1); - assertEquals(progress.running, undefined); - assertEquals(progress.results.length, 1); - assert(!!progress.results.find(({ name }) => name == "single")); - - // Assert start of bench "multiple" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchStart); - assert(progress.queued); - assertEquals(progress.queued.length, 0); - assertEquals(progress.running, { - name: "multiple", - runsCount: 2, - measuredRunsMs: [], - }); - assertEquals(progress.results.length, 1); - - // Assert first result of bench "multiple" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchPartialResult); - assert(progress.queued); - assertEquals(progress.queued.length, 0); - assertEquals(progress.running!.measuredRunsMs.length, 1); - assertEquals(progress.results.length, 1); - - // Assert second result of bench "multiple" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchPartialResult); - assert(progress.queued); - assertEquals(progress.queued.length, 0); - assertEquals(progress.running!.measuredRunsMs.length, 2); - assertEquals(progress.results.length, 1); - - // Assert finish of bench "multiple" - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchResult); - assert(progress.queued); - assertEquals(progress.queued.length, 0); - assertEquals(progress.running, undefined); - assertEquals(progress.results.length, 2); - assert(!!progress.results.find(({ name }) => name == "single")); - const resultOfMultiple = progress.results.filter( - ({ name }) => name == "multiple", - ); - assertEquals(resultOfMultiple.length, 1); - assert(!!resultOfMultiple[0].measuredRunsMs); - assert(!isNaN(resultOfMultiple[0].measuredRunsAvgMs)); - assertEquals(resultOfMultiple[0].measuredRunsMs.length, 2); - - // The last progress should equal the final result from promise except the state property - progress = progressCallbacks[pc++]; - assertEquals(progress.state, ProgressState.BenchmarkingEnd); - delete progress.state; - assertEquals(progress, benchingResults); - }, -}); - -Deno.test({ - name: "async progressCallback", - fn: async function (): Promise { - clearBenchmarks(); - dummyBench("single"); - - const asyncCallbacks = []; - - await runBenchmarks({ silent: true }, (progress) => { - return new Promise((resolve) => { - queueMicrotask(() => { - asyncCallbacks.push(progress); - resolve(); - }); - }); - }); - - assertEquals(asyncCallbacks.length, 5); - }, -}); - -function dummyBench(name: string, runs = 1): void { - bench({ - name, - runs, - func(b) { - b.start(); - b.stop(); - }, - }); -} diff --git a/std/textproto/mod.ts b/std/textproto/mod.ts deleted file mode 100644 index 8162756622..0000000000 --- a/std/textproto/mod.ts +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// Based on https://github.com/golang/go/tree/master/src/net/textproto -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -import type { BufReader } from "../io/bufio.ts"; -import { concat } from "../bytes/mod.ts"; -import { decode } from "../encoding/utf8.ts"; - -// FROM https://github.com/denoland/deno/blob/b34628a26ab0187a827aa4ebe256e23178e25d39/cli/js/web/headers.ts#L9 -const invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/g; - -function str(buf: Uint8Array | null | undefined): string { - if (buf == null) { - return ""; - } else { - return decode(buf); - } -} - -function charCode(s: string): number { - return s.charCodeAt(0); -} - -export class TextProtoReader { - constructor(readonly r: BufReader) {} - - /** readLine() reads a single line from the TextProtoReader, - * eliding the final \n or \r\n from the returned string. - */ - async readLine(): Promise { - const s = await this.readLineSlice(); - if (s === null) return null; - return str(s); - } - - /** ReadMIMEHeader reads a MIME-style header from r. - * The header is a sequence of possibly continued Key: Value lines - * ending in a blank line. - * The returned map m maps CanonicalMIMEHeaderKey(key) to a - * sequence of values in the same order encountered in the input. - * - * For example, consider this input: - * - * My-Key: Value 1 - * Long-Key: Even - * Longer Value - * My-Key: Value 2 - * - * Given that input, ReadMIMEHeader returns the map: - * - * map[string][]string{ - * "My-Key": {"Value 1", "Value 2"}, - * "Long-Key": {"Even Longer Value"}, - * } - */ - async readMIMEHeader(): Promise { - const m = new Headers(); - let line: Uint8Array | undefined; - - // The first line cannot start with a leading space. - let buf = await this.r.peek(1); - if (buf === null) { - return null; - } else if (buf[0] == charCode(" ") || buf[0] == charCode("\t")) { - line = (await this.readLineSlice()) as Uint8Array; - } - - buf = await this.r.peek(1); - if (buf === null) { - throw new Deno.errors.UnexpectedEof(); - } else if (buf[0] == charCode(" ") || buf[0] == charCode("\t")) { - throw new Deno.errors.InvalidData( - `malformed MIME header initial line: ${str(line)}`, - ); - } - - while (true) { - const kv = await this.readLineSlice(); // readContinuedLineSlice - if (kv === null) throw new Deno.errors.UnexpectedEof(); - if (kv.byteLength === 0) return m; - - // Key ends at first colon - let i = kv.indexOf(charCode(":")); - if (i < 0) { - throw new Deno.errors.InvalidData( - `malformed MIME header line: ${str(kv)}`, - ); - } - - //let key = canonicalMIMEHeaderKey(kv.subarray(0, endKey)); - const key = str(kv.subarray(0, i)); - - // As per RFC 7230 field-name is a token, - // tokens consist of one or more chars. - // We could throw `Deno.errors.InvalidData` here, - // but better to be liberal in what we - // accept, so if we get an empty key, skip it. - if (key == "") { - continue; - } - - // Skip initial spaces in value. - i++; // skip colon - while ( - i < kv.byteLength && - (kv[i] == charCode(" ") || kv[i] == charCode("\t")) - ) { - i++; - } - const value = str(kv.subarray(i)).replace( - invalidHeaderCharRegex, - encodeURI, - ); - - // In case of invalid header we swallow the error - // example: "Audio Mode" => invalid due to space in the key - try { - m.append(key, value); - } catch { - // Pass - } - } - } - - async readLineSlice(): Promise { - // this.closeDot(); - let line: Uint8Array | undefined; - while (true) { - const r = await this.r.readLine(); - if (r === null) return null; - const { line: l, more } = r; - - // Avoid the copy if the first call produced a full line. - if (!line && !more) { - // TODO(ry): - // This skipSpace() is definitely misplaced, but I don't know where it - // comes from nor how to fix it. - if (this.skipSpace(l) === 0) { - return new Uint8Array(0); - } - return l; - } - line = line ? concat(line, l) : l; - if (!more) { - break; - } - } - return line; - } - - skipSpace(l: Uint8Array): number { - let n = 0; - for (let i = 0; i < l.length; i++) { - if (l[i] === charCode(" ") || l[i] === charCode("\t")) { - continue; - } - n++; - } - return n; - } -} diff --git a/std/textproto/test.ts b/std/textproto/test.ts deleted file mode 100644 index ca7a17ce41..0000000000 --- a/std/textproto/test.ts +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -// Based on https://github.com/golang/go/blob/master/src/net/textproto/reader_test.go -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -import { BufReader } from "../io/bufio.ts"; -import { TextProtoReader } from "./mod.ts"; -import { StringReader } from "../io/readers.ts"; -import { assert, assertEquals, assertThrows } from "../testing/asserts.ts"; - -function reader(s: string): TextProtoReader { - return new TextProtoReader(new BufReader(new StringReader(s))); -} - -Deno.test({ - ignore: true, - name: "[textproto] Reader : DotBytes", - fn(): Promise { - const _input = - "dotlines\r\n.foo\r\n..bar\n...baz\nquux\r\n\r\n.\r\nanot.her\r\n"; - return Promise.resolve(); - }, -}); - -Deno.test("[textproto] ReadEmpty", async () => { - const r = reader(""); - const m = await r.readMIMEHeader(); - assertEquals(m, null); -}); - -Deno.test("[textproto] Reader", async () => { - const r = reader("line1\nline2\n"); - let s = await r.readLine(); - assertEquals(s, "line1"); - - s = await r.readLine(); - assertEquals(s, "line2"); - - s = await r.readLine(); - assert(s === null); -}); - -Deno.test({ - name: "[textproto] Reader : MIME Header", - async fn(): Promise { - const input = - "my-key: Value 1 \r\nLong-key: Even Longer Value\r\nmy-Key: " + - "Value 2\r\n\n"; - const r = reader(input); - const m = await r.readMIMEHeader(); - assert(m !== null); - assertEquals(m.get("My-Key"), "Value 1, Value 2"); - assertEquals(m.get("Long-key"), "Even Longer Value"); - }, -}); - -Deno.test({ - name: "[textproto] Reader : MIME Header Single", - async fn(): Promise { - const input = "Foo: bar\n\n"; - const r = reader(input); - const m = await r.readMIMEHeader(); - assert(m !== null); - assertEquals(m.get("Foo"), "bar"); - }, -}); - -Deno.test({ - name: "[textproto] Reader : MIME Header No Key", - async fn(): Promise { - const input = ": bar\ntest-1: 1\n\n"; - const r = reader(input); - const m = await r.readMIMEHeader(); - assert(m !== null); - assertEquals(m.get("Test-1"), "1"); - }, -}); - -Deno.test({ - name: "[textproto] Reader : Large MIME Header", - async fn(): Promise { - const data: string[] = []; - // Go test is 16*1024. But seems it can't handle more - for (let i = 0; i < 1024; i++) { - data.push("x"); - } - const sdata = data.join(""); - const r = reader(`Cookie: ${sdata}\r\n\r\n`); - const m = await r.readMIMEHeader(); - assert(m !== null); - assertEquals(m.get("Cookie"), sdata); - }, -}); - -// Test that we don't read MIME headers seen in the wild, -// with spaces before colons, and spaces in keys. -Deno.test({ - name: "[textproto] Reader : MIME Header Non compliant", - async fn(): Promise { - const input = "Foo: bar\r\n" + - "Content-Language: en\r\n" + - "SID : 0\r\n" + - "Audio Mode : None\r\n" + - "Privilege : 127\r\n\r\n"; - const r = reader(input); - const m = await r.readMIMEHeader(); - assert(m !== null); - assertEquals(m.get("Foo"), "bar"); - assertEquals(m.get("Content-Language"), "en"); - // Make sure we drop headers with trailing whitespace - assertEquals(m.get("SID"), null); - assertEquals(m.get("Privilege"), null); - // Not legal http header - assertThrows((): void => { - assertEquals(m.get("Audio Mode"), "None"); - }); - }, -}); - -Deno.test({ - name: "[textproto] Reader : MIME Header Malformed", - async fn(): Promise { - const input = [ - "No colon first line\r\nFoo: foo\r\n\r\n", - " No colon first line with leading space\r\nFoo: foo\r\n\r\n", - "\tNo colon first line with leading tab\r\nFoo: foo\r\n\r\n", - " First: line with leading space\r\nFoo: foo\r\n\r\n", - "\tFirst: line with leading tab\r\nFoo: foo\r\n\r\n", - "Foo: foo\r\nNo colon second line\r\n\r\n", - ]; - const r = reader(input.join("")); - - let err; - try { - await r.readMIMEHeader(); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.InvalidData); - }, -}); - -Deno.test({ - name: "[textproto] Reader : MIME Header Trim Continued", - async fn(): Promise { - const input = "a:\n" + - " 0 \r\n" + - "b:1 \t\r\n" + - "c: 2\r\n" + - " 3\t\n" + - " \t 4 \r\n\n"; - const r = reader(input); - let err; - try { - await r.readMIMEHeader(); - } catch (e) { - err = e; - } - assert(err instanceof Deno.errors.InvalidData); - }, -}); - -Deno.test({ - name: "[textproto] #409 issue : multipart form boundary", - async fn(): Promise { - const input = [ - "Accept: */*\r\n", - 'Content-Disposition: form-data; name="test"\r\n', - " \r\n", - "------WebKitFormBoundaryimeZ2Le9LjohiUiG--\r\n\n", - ]; - const r = reader(input.join("")); - const m = await r.readMIMEHeader(); - assert(m !== null); - assertEquals(m.get("Accept"), "*/*"); - assertEquals(m.get("Content-Disposition"), 'form-data; name="test"'); - }, -}); - -Deno.test({ - name: "[textproto] #4521 issue", - async fn() { - const input = "abcdefghijklmnopqrstuvwxyz"; - const bufSize = 25; - const tp = new TextProtoReader( - new BufReader(new StringReader(input), bufSize), - ); - const line = await tp.readLine(); - assertEquals(line, input); - }, -}); diff --git a/std/uuid/README.md b/std/uuid/README.md deleted file mode 100644 index 846b782637..0000000000 --- a/std/uuid/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# UUID - -Support for version 1, 4, and 5 UUIDs. - -## Usage - -```ts -import { v4 } from "https://deno.land/std@$STD_VERSION/uuid/mod.ts"; - -// Generate a v4 uuid. -const myUUID = v4.generate(); - -// Validate a v4 uuid. -const isValid = v4.validate(myUUID); -``` diff --git a/std/uuid/_common.ts b/std/uuid/_common.ts deleted file mode 100644 index 1f3228aea8..0000000000 --- a/std/uuid/_common.ts +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -/** - * Converts the byte array to a UUID string - * @param bytes Used to convert Byte to Hex - */ -export function bytesToUuid(bytes: number[] | Uint8Array): string { - const bits: string[] = [...bytes].map((bit): string => { - const s: string = bit.toString(16); - return bit < 0x10 ? "0" + s : s; - }); - return [ - ...bits.slice(0, 4), - "-", - ...bits.slice(4, 6), - "-", - ...bits.slice(6, 8), - "-", - ...bits.slice(8, 10), - "-", - ...bits.slice(10, 16), - ].join(""); -} - -/** - * Converts a string to a byte array by converting the hex value to a number - * @param uuid Value that gets converted - */ -export function uuidToBytes(uuid: string): number[] { - const bytes: number[] = []; - - uuid.replace(/[a-fA-F0-9]{2}/g, (hex: string): string => { - bytes.push(parseInt(hex, 16)); - return ""; - }); - - return bytes; -} - -/** - * Converts a string to a byte array using the char code - * @param str Value that gets converted - */ -export function stringToBytes(str: string): number[] { - str = unescape(encodeURIComponent(str)); - const bytes = new Array(str.length); - for (let i = 0; i < str.length; i++) { - bytes[i] = str.charCodeAt(i); - } - return bytes; -} - -/** - * Creates a buffer for creating a SHA-1 hash - * @param content Buffer for SHA-1 hash - */ -export function createBuffer(content: number[]): ArrayBuffer { - const arrayBuffer = new ArrayBuffer(content.length); - const uint8Array = new Uint8Array(arrayBuffer); - for (let i = 0; i < content.length; i++) { - uint8Array[i] = content[i]; - } - return arrayBuffer; -} diff --git a/std/uuid/mod.ts b/std/uuid/mod.ts deleted file mode 100644 index b2580319c9..0000000000 --- a/std/uuid/mod.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Based on https://github.com/kelektiv/node-uuid -> https://www.ietf.org/rfc/rfc4122.txt -// Supporting Support for RFC4122 version 1, 4, and 5 UUIDs -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import * as v1 from "./v1.ts"; -import * as v4 from "./v4.ts"; -import * as v5 from "./v5.ts"; - -export const NIL_UUID = "00000000-0000-0000-0000-000000000000"; - -/** - * Checks if UUID is nil - * @param val UUID value - */ -export function isNil(val: string): boolean { - return val === NIL_UUID; -} - -export { v1, v4, v5 }; diff --git a/std/uuid/test.ts b/std/uuid/test.ts deleted file mode 100644 index 7d0feaf8da..0000000000 --- a/std/uuid/test.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert } from "../testing/asserts.ts"; -import { isNil, NIL_UUID } from "./mod.ts"; - -Deno.test({ - name: "[UUID] isNil", - fn(): void { - const nil = NIL_UUID; - const u = "582cbcff-dad6-4f28-888a-e062ae36bafc"; - assert(isNil(nil)); - assert(!isNil(u)); - }, -}); diff --git a/std/uuid/v1.ts b/std/uuid/v1.ts deleted file mode 100644 index b97c704e79..0000000000 --- a/std/uuid/v1.ts +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { bytesToUuid } from "./_common.ts"; - -const UUID_RE = new RegExp( - "^[0-9a-f]{8}-[0-9a-f]{4}-1[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", - "i", -); - -/** - * Validates the UUID v1 - * @param id UUID value - */ -export function validate(id: string): boolean { - return UUID_RE.test(id); -} - -let _nodeId: number[]; -let _clockseq: number; - -let _lastMSecs = 0; -let _lastNSecs = 0; - -type V1Options = { - node?: number[]; - clockseq?: number; - msecs?: number; - nsecs?: number; - random?: number[]; - rng?: () => number[]; -}; - -/** - * Generates a RFC4122 v1 UUID (time-based) - * @param options Can use RFC time sequence values as overwrites - * @param buf Can allow the UUID to be written in byte-form starting at the offset - * @param offset Index to start writing on the UUID bytes in buffer - */ -export function generate( - options?: V1Options | null, - buf?: number[], - offset?: number, -): string | number[] { - let i = (buf && offset) || 0; - const b = buf || []; - - options = options || {}; - let node = options.node || _nodeId; - let clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; - - if (node == null || clockseq == null) { - // deno-lint-ignore no-explicit-any - const seedBytes: any = options.random || - options.rng || - crypto.getRandomValues(new Uint8Array(16)); - if (node == null) { - node = _nodeId = [ - seedBytes[0] | 0x01, - seedBytes[1], - seedBytes[2], - seedBytes[3], - seedBytes[4], - seedBytes[5], - ]; - } - if (clockseq == null) { - clockseq = _clockseq = ((seedBytes[6] << 8) | seedBytes[7]) & 0x3fff; - } - } - let msecs = options.msecs !== undefined - ? options.msecs - : new Date().getTime(); - - let nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; - - const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; - - if (dt < 0 && options.clockseq === undefined) { - clockseq = (clockseq + 1) & 0x3fff; - } - - if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { - nsecs = 0; - } - - if (nsecs >= 10000) { - throw new Error("Can't create more than 10M uuids/sec"); - } - - _lastMSecs = msecs; - _lastNSecs = nsecs; - _clockseq = clockseq; - - msecs += 12219292800000; - - const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; - b[i++] = (tl >>> 24) & 0xff; - b[i++] = (tl >>> 16) & 0xff; - b[i++] = (tl >>> 8) & 0xff; - b[i++] = tl & 0xff; - - const tmh = ((msecs / 0x100000000) * 10000) & 0xfffffff; - b[i++] = (tmh >>> 8) & 0xff; - b[i++] = tmh & 0xff; - - b[i++] = ((tmh >>> 24) & 0xf) | 0x10; - b[i++] = (tmh >>> 16) & 0xff; - - b[i++] = (clockseq >>> 8) | 0x80; - - b[i++] = clockseq & 0xff; - - for (let n = 0; n < 6; ++n) { - b[i + n] = node[n]; - } - - return buf ? buf : bytesToUuid(b); -} diff --git a/std/uuid/v1_test.ts b/std/uuid/v1_test.ts deleted file mode 100644 index 5971d3f4ea..0000000000 --- a/std/uuid/v1_test.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "../testing/asserts.ts"; -import { generate, validate } from "./v1.ts"; - -Deno.test({ - name: "[UUID] is_valid_uuid_v1", - fn(): void { - const u = generate(); - const t = "63655efa-7ee6-11ea-bc55-0242ac130003"; - const n = "63655efa-7ee6-11eg-bc55-0242ac130003"; - - assert(validate(u as string), `generated ${u} should be valid`); - assert(validate(t), `${t} should be valid`); - assert(!validate(n), `${n} should not be valid`); - }, -}); - -Deno.test({ - name: "[UUID] test_uuid_v1", - fn(): void { - const u = generate(); - assertEquals(typeof u, "string", "returns a string"); - assert(u !== "", "return string is not empty"); - }, -}); - -Deno.test({ - name: "[UUID] test_uuid_v1_format", - fn(): void { - for (let i = 0; i < 10000; i++) { - const u = generate() as string; - assert(validate(u), `${u} is not a valid uuid v1`); - } - }, -}); - -Deno.test({ - name: "[UUID] test_uuid_v1_static", - fn(): void { - const v1options = { - node: [0x01, 0x23, 0x45, 0x67, 0x89, 0xab], - clockseq: 0x1234, - msecs: new Date("2011-11-01").getTime(), - nsecs: 5678, - }; - const u = generate(v1options); - assertEquals(u, "710b962e-041c-11e1-9234-0123456789ab"); - }, -}); diff --git a/std/uuid/v4.ts b/std/uuid/v4.ts deleted file mode 100644 index e059cd937c..0000000000 --- a/std/uuid/v4.ts +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { bytesToUuid } from "./_common.ts"; - -const UUID_RE = new RegExp( - "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", - "i", -); - -/** - * Validates the UUID v4 - * @param id UUID value - */ -export function validate(id: string): boolean { - return UUID_RE.test(id); -} - -/** Generates a RFC4122 v4 UUID (pseudo-randomly-based) */ -export function generate(): string { - const rnds = crypto.getRandomValues(new Uint8Array(16)); - - rnds[6] = (rnds[6] & 0x0f) | 0x40; // Version 4 - rnds[8] = (rnds[8] & 0x3f) | 0x80; // Variant 10 - - return bytesToUuid(rnds); -} diff --git a/std/uuid/v4_test.ts b/std/uuid/v4_test.ts deleted file mode 100644 index 6c9cb6b4a0..0000000000 --- a/std/uuid/v4_test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "../testing/asserts.ts"; -import { generate, validate } from "./v4.ts"; - -Deno.test({ - name: "[UUID] test_uuid_v4", - fn(): void { - const u = generate(); - assertEquals(typeof u, "string", "returns a string"); - assert(u !== "", "return string is not empty"); - }, -}); - -Deno.test({ - name: "[UUID] test_uuid_v4_format", - fn(): void { - for (let i = 0; i < 10000; i++) { - const u = generate() as string; - assert(validate(u), `${u} is not a valid uuid v4`); - } - }, -}); - -Deno.test({ - name: "[UUID] is_valid_uuid_v4", - fn(): void { - const u = generate(); - const t = "84fb7824-b951-490e-8afd-0c13228a8282"; - const n = "84fb7824-b951-490g-8afd-0c13228a8282"; - - assert(validate(u), `generated ${u} should be valid`); - assert(validate(t), `${t} should be valid`); - assert(!validate(n), `${n} should not be valid`); - }, -}); diff --git a/std/uuid/v5.ts b/std/uuid/v5.ts deleted file mode 100644 index 7f6a098e07..0000000000 --- a/std/uuid/v5.ts +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { - bytesToUuid, - createBuffer, - stringToBytes, - uuidToBytes, -} from "./_common.ts"; -import { Sha1 } from "../hash/sha1.ts"; -import { assert } from "../_util/assert.ts"; - -const UUID_RE = - /^[0-9a-f]{8}-[0-9a-f]{4}-[5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; - -/** - * Validates the UUID v5 - * @param id UUID value - */ -export function validate(id: string): boolean { - return UUID_RE.test(id); -} - -interface V5Options { - value: string | number[]; - namespace: string | number[]; -} - -/** - * Generates a RFC4122 v5 UUID (SHA-1 namespace-based) - * @param options Can use a namespace and value to create SHA-1 hash - * @param buf Can allow the UUID to be written in byte-form starting at the offset - * @param offset Index to start writing on the UUID bytes in buffer - */ -export function generate( - options: V5Options, - buf?: number[], - offset?: number, -): string | number[] { - const i = (buf && offset) || 0; - - let { value, namespace } = options; - if (typeof value == "string") { - value = stringToBytes(value as string); - } - - if (typeof namespace == "string") { - namespace = uuidToBytes(namespace as string); - } - - assert( - namespace.length === 16, - "namespace must be uuid string or an Array of 16 byte values", - ); - - const content = (namespace as number[]).concat(value as number[]); - const bytes = new Sha1().update(createBuffer(content)).digest(); - - bytes[6] = (bytes[6] & 0x0f) | 0x50; - bytes[8] = (bytes[8] & 0x3f) | 0x80; - - if (buf) { - for (let idx = 0; idx < 16; ++idx) { - buf[i + idx] = bytes[idx]; - } - } - - return buf || bytesToUuid(bytes); -} diff --git a/std/uuid/v5_test.ts b/std/uuid/v5_test.ts deleted file mode 100644 index 15da32aeea..0000000000 --- a/std/uuid/v5_test.ts +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "../testing/asserts.ts"; -import { generate, validate } from "./v5.ts"; - -const NAMESPACE = "1b671a64-40d5-491e-99b0-da01ff1f3341"; - -Deno.test({ - name: "[UUID] test_uuid_v5", - fn(): void { - const u = generate({ value: "", namespace: NAMESPACE }); - assertEquals(typeof u, "string", "returns a string"); - assert(u !== "", "return string is not empty"); - }, -}); - -Deno.test({ - name: "[UUID] test_uuid_v5_format", - fn(): void { - for (let i = 0; i < 10000; i++) { - const u = generate({ value: String(i), namespace: NAMESPACE }) as string; - assert(validate(u), `${u} is not a valid uuid v5`); - } - }, -}); - -Deno.test({ - name: "[UUID] test_uuid_v5_option", - fn(): void { - const v5Options = { - value: "Hello, World", - namespace: NAMESPACE, - }; - const u = generate(v5Options); - assertEquals(u, "4b4f2adc-5b27-57b5-8e3a-c4c4bcf94f05"); - }, -}); - -Deno.test({ - name: "[UUID] test_uuid_v5_buf_offset", - fn(): void { - const buf = [ - 75, - 79, - 42, - 220, - 91, - 39, - 87, - 181, - 142, - 58, - 196, - 196, - 188, - 249, - 79, - 5, - ]; - const origin = JSON.parse(JSON.stringify(buf)); - generate({ value: "Hello, World", namespace: NAMESPACE }, buf); - assertEquals(origin, buf); - - generate({ value: "Hello, World", namespace: NAMESPACE }, buf, 3); - assertEquals(origin.slice(0, 3), buf.slice(0, 3)); - assertEquals(origin, buf.slice(3)); - }, -}); - -Deno.test({ - name: "[UUID] is_valid_uuid_v5", - fn(): void { - const u = generate({ - value: "Hello, World", - namespace: "1b671a64-40d5-491e-99b0-da01ff1f3341", - }) as string; - const t = "4b4f2adc-5b27-57b5-8e3a-c4c4bcf94f05"; - const n = "4b4f2adc-5b27-17b5-8e3a-c4c4bcf94f05"; - - assert(validate(u), `generated ${u} should be valid`); - assert(validate(t), `${t} should be valid`); - assert(!validate(n), `${n} should not be valid`); - }, -}); diff --git a/std/version.ts b/std/version.ts deleted file mode 100644 index 434eaf8011..0000000000 --- a/std/version.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -/** Version of the Deno standard modules - * - * Deno std is versioned differently than Deno cli because it is still unstable; - * the cli's API is stable. In the future when std becomes stable, likely we - * will match versions with cli as we have in the past. - */ -export const VERSION = "0.85.0"; diff --git a/std/wasi/README.md b/std/wasi/README.md deleted file mode 100644 index 829b25174f..0000000000 --- a/std/wasi/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# wasi - -This module provides an implementation of the WebAssembly System Interface. - -## Supported Syscalls - -### wasi_snapshot_preview1 - -- [x] args_get -- [x] args_sizes_get -- [x] environ_get -- [x] environ_sizes_get -- [x] clock_res_get -- [x] clock_time_get -- [ ] fd_advise -- [ ] fd_allocate -- [x] fd_close -- [x] fd_datasync -- [x] fd_fdstat_get -- [ ] fd_fdstat_set_flags -- [ ] fd_fdstat_set_rights -- [x] fd_filestat_get -- [x] fd_filestat_set_size -- [x] fd_filestat_set_times -- [x] fd_pread -- [x] fd_prestat_get -- [x] fd_prestat_dir_name -- [x] fd_pwrite -- [x] fd_read -- [x] fd_readdir -- [x] fd_renumber -- [x] fd_seek -- [x] fd_sync -- [x] fd_tell -- [x] fd_write -- [x] path_create_directory -- [x] path_filestat_get -- [x] path_filestat_set_times -- [x] path_link -- [x] path_open -- [x] path_readlink -- [x] path_remove_directory -- [x] path_rename -- [x] path_symlink -- [x] path_unlink_file -- [x] poll_oneoff -- [x] proc_exit -- [ ] proc_raise -- [x] sched_yield -- [x] random_get -- [ ] sock_recv -- [ ] sock_send -- [ ] sock_shutdown - -## Usage - -```typescript -import Context from "https://deno.land/std@$STD_VERSION/wasi/snapshot_preview1.ts"; - -const context = new Context({ - args: Deno.args, - env: Deno.env.toObject(), -}); - -const binary = await Deno.readFile("path/to/your/module.wasm"); -const module = await WebAssembly.compile(binary); -const instance = await WebAssembly.instantiate(module, { - "wasi_snapshot_preview1": context.exports, -}); - -context.start(instance); -``` diff --git a/std/wasi/snapshot_preview1.ts b/std/wasi/snapshot_preview1.ts deleted file mode 100644 index 9a1796e2ee..0000000000 --- a/std/wasi/snapshot_preview1.ts +++ /dev/null @@ -1,1739 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { relative, resolve } from "../path/mod.ts"; - -const CLOCKID_REALTIME = 0; -const CLOCKID_MONOTONIC = 1; -const CLOCKID_PROCESS_CPUTIME_ID = 2; -const CLOCKID_THREAD_CPUTIME_ID = 3; - -const ERRNO_SUCCESS = 0; -const _ERRNO_2BIG = 1; -const ERRNO_ACCES = 2; -const ERRNO_ADDRINUSE = 3; -const ERRNO_ADDRNOTAVAIL = 4; -const _ERRNO_AFNOSUPPORT = 5; -const _ERRNO_AGAIN = 6; -const _ERRNO_ALREADY = 7; -const ERRNO_BADF = 8; -const _ERRNO_BADMSG = 9; -const ERRNO_BUSY = 10; -const _ERRNO_CANCELED = 11; -const _ERRNO_CHILD = 12; -const ERRNO_CONNABORTED = 13; -const ERRNO_CONNREFUSED = 14; -const ERRNO_CONNRESET = 15; -const _ERRNO_DEADLK = 16; -const _ERRNO_DESTADDRREQ = 17; -const _ERRNO_DOM = 18; -const _ERRNO_DQUOT = 19; -const _ERRNO_EXIST = 20; -const _ERRNO_FAULT = 21; -const _ERRNO_FBIG = 22; -const _ERRNO_HOSTUNREACH = 23; -const _ERRNO_IDRM = 24; -const _ERRNO_ILSEQ = 25; -const _ERRNO_INPROGRESS = 26; -const ERRNO_INTR = 27; -const ERRNO_INVAL = 28; -const _ERRNO_IO = 29; -const _ERRNO_ISCONN = 30; -const _ERRNO_ISDIR = 31; -const _ERRNO_LOOP = 32; -const _ERRNO_MFILE = 33; -const _ERRNO_MLINK = 34; -const _ERRNO_MSGSIZE = 35; -const _ERRNO_MULTIHOP = 36; -const _ERRNO_NAMETOOLONG = 37; -const _ERRNO_NETDOWN = 38; -const _ERRNO_NETRESET = 39; -const _ERRNO_NETUNREACH = 40; -const _ERRNO_NFILE = 41; -const _ERRNO_NOBUFS = 42; -const _ERRNO_NODEV = 43; -const ERRNO_NOENT = 44; -const _ERRNO_NOEXEC = 45; -const _ERRNO_NOLCK = 46; -const _ERRNO_NOLINK = 47; -const _ERRNO_NOMEM = 48; -const _ERRNO_NOMSG = 49; -const _ERRNO_NOPROTOOPT = 50; -const _ERRNO_NOSPC = 51; -const ERRNO_NOSYS = 52; -const ERRNO_NOTCONN = 53; -const ERRNO_NOTDIR = 54; -const _ERRNO_NOTEMPTY = 55; -const _ERRNO_NOTRECOVERABLE = 56; -const _ERRNO_NOTSOCK = 57; -const _ERRNO_NOTSUP = 58; -const _ERRNO_NOTTY = 59; -const _ERRNO_NXIO = 60; -const _ERRNO_OVERFLOW = 61; -const _ERRNO_OWNERDEAD = 62; -const _ERRNO_PERM = 63; -const ERRNO_PIPE = 64; -const _ERRNO_PROTO = 65; -const _ERRNO_PROTONOSUPPORT = 66; -const _ERRNO_PROTOTYPE = 67; -const _ERRNO_RANGE = 68; -const _ERRNO_ROFS = 69; -const _ERRNO_SPIPE = 70; -const _ERRNO_SRCH = 71; -const _ERRNO_STALE = 72; -const ERRNO_TIMEDOUT = 73; -const _ERRNO_TXTBSY = 74; -const _ERRNO_XDEV = 75; -const ERRNO_NOTCAPABLE = 76; - -const RIGHTS_FD_DATASYNC = 0x0000000000000001n; -const RIGHTS_FD_READ = 0x0000000000000002n; -const _RIGHTS_FD_SEEK = 0x0000000000000004n; -const _RIGHTS_FD_FDSTAT_SET_FLAGS = 0x0000000000000008n; -const _RIGHTS_FD_SYNC = 0x0000000000000010n; -const _RIGHTS_FD_TELL = 0x0000000000000020n; -const RIGHTS_FD_WRITE = 0x0000000000000040n; -const _RIGHTS_FD_ADVISE = 0x0000000000000080n; -const RIGHTS_FD_ALLOCATE = 0x0000000000000100n; -const _RIGHTS_PATH_CREATE_DIRECTORY = 0x0000000000000200n; -const _RIGHTS_PATH_CREATE_FILE = 0x0000000000000400n; -const _RIGHTS_PATH_LINK_SOURCE = 0x0000000000000800n; -const _RIGHTS_PATH_LINK_TARGET = 0x0000000000001000n; -const _RIGHTS_PATH_OPEN = 0x0000000000002000n; -const RIGHTS_FD_READDIR = 0x0000000000004000n; -const _RIGHTS_PATH_READLINK = 0x0000000000008000n; -const _RIGHTS_PATH_RENAME_SOURCE = 0x0000000000010000n; -const _RIGHTS_PATH_RENAME_TARGET = 0x0000000000020000n; -const _RIGHTS_PATH_FILESTAT_GET = 0x0000000000040000n; -const _RIGHTS_PATH_FILESTAT_SET_SIZE = 0x0000000000080000n; -const _RIGHTS_PATH_FILESTAT_SET_TIMES = 0x0000000000100000n; -const _RIGHTS_FD_FILESTAT_GET = 0x0000000000200000n; -const RIGHTS_FD_FILESTAT_SET_SIZE = 0x0000000000400000n; -const _RIGHTS_FD_FILESTAT_SET_TIMES = 0x0000000000800000n; -const _RIGHTS_PATH_SYMLINK = 0x0000000001000000n; -const _RIGHTS_PATH_REMOVE_DIRECTORY = 0x0000000002000000n; -const _RIGHTS_PATH_UNLINK_FILE = 0x0000000004000000n; -const _RIGHTS_POLL_FD_READWRITE = 0x0000000008000000n; -const _RIGHTS_SOCK_SHUTDOWN = 0x0000000010000000n; - -const _WHENCE_SET = 0; -const _WHENCE_CUR = 1; -const _WHENCE_END = 2; - -const FILETYPE_UNKNOWN = 0; -const _FILETYPE_BLOCK_DEVICE = 1; -const FILETYPE_CHARACTER_DEVICE = 2; -const FILETYPE_DIRECTORY = 3; -const FILETYPE_REGULAR_FILE = 4; -const _FILETYPE_SOCKET_DGRAM = 5; -const _FILETYPE_SOCKET_STREAM = 6; -const FILETYPE_SYMBOLIC_LINK = 7; - -const _ADVICE_NORMAL = 0; -const _ADVICE_SEQUENTIAL = 1; -const _ADVICE_RANDOM = 2; -const _ADVICE_WILLNEED = 3; -const _ADVICE_DONTNEED = 4; -const _ADVICE_NOREUSE = 5; - -const FDFLAGS_APPEND = 0x0001; -const FDFLAGS_DSYNC = 0x0002; -const FDFLAGS_NONBLOCK = 0x0004; -const FDFLAGS_RSYNC = 0x0008; -const FDFLAGS_SYNC = 0x0010; - -const _FSTFLAGS_ATIM = 0x0001; -const FSTFLAGS_ATIM_NOW = 0x0002; -const _FSTFLAGS_MTIM = 0x0004; -const FSTFLAGS_MTIM_NOW = 0x0008; - -const LOOKUPFLAGS_SYMLINK_FOLLOW = 0x0001; - -const OFLAGS_CREAT = 0x0001; -const OFLAGS_DIRECTORY = 0x0002; -const OFLAGS_EXCL = 0x0004; -const OFLAGS_TRUNC = 0x0008; - -const _EVENTTYPE_CLOCK = 0; -const _EVENTTYPE_FD_READ = 1; -const _EVENTTYPE_FD_WRITE = 2; - -const _EVENTRWFLAGS_FD_READWRITE_HANGUP = 1; -const _SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME = 1; - -const _SIGNAL_NONE = 0; -const _SIGNAL_HUP = 1; -const _SIGNAL_INT = 2; -const _SIGNAL_QUIT = 3; -const _SIGNAL_ILL = 4; -const _SIGNAL_TRAP = 5; -const _SIGNAL_ABRT = 6; -const _SIGNAL_BUS = 7; -const _SIGNAL_FPE = 8; -const _SIGNAL_KILL = 9; -const _SIGNAL_USR1 = 10; -const _SIGNAL_SEGV = 11; -const _SIGNAL_USR2 = 12; -const _SIGNAL_PIPE = 13; -const _SIGNAL_ALRM = 14; -const _SIGNAL_TERM = 15; -const _SIGNAL_CHLD = 16; -const _SIGNAL_CONT = 17; -const _SIGNAL_STOP = 18; -const _SIGNAL_TSTP = 19; -const _SIGNAL_TTIN = 20; -const _SIGNAL_TTOU = 21; -const _SIGNAL_URG = 22; -const _SIGNAL_XCPU = 23; -const _SIGNAL_XFSZ = 24; -const _SIGNAL_VTALRM = 25; -const _SIGNAL_PROF = 26; -const _SIGNAL_WINCH = 27; -const _SIGNAL_POLL = 28; -const _SIGNAL_PWR = 29; -const _SIGNAL_SYS = 30; - -const _RIFLAGS_RECV_PEEK = 0x0001; -const _RIFLAGS_RECV_WAITALL = 0x0002; - -const _ROFLAGS_RECV_DATA_TRUNCATED = 0x0001; - -const _SDFLAGS_RD = 0x0001; -const _SDFLAGS_WR = 0x0002; - -const PREOPENTYPE_DIR = 0; - -function syscall(target: T) { - return function (...args: unknown[]) { - try { - return target(...args); - } catch (err) { - if (err instanceof ExitStatus) { - throw err; - } - - switch (err.name) { - case "NotFound": - return ERRNO_NOENT; - - case "PermissionDenied": - return ERRNO_ACCES; - - case "ConnectionRefused": - return ERRNO_CONNREFUSED; - - case "ConnectionReset": - return ERRNO_CONNRESET; - - case "ConnectionAborted": - return ERRNO_CONNABORTED; - - case "NotConnected": - return ERRNO_NOTCONN; - - case "AddrInUse": - return ERRNO_ADDRINUSE; - - case "AddrNotAvailable": - return ERRNO_ADDRNOTAVAIL; - - case "BrokenPipe": - return ERRNO_PIPE; - - case "InvalidData": - return ERRNO_INVAL; - - case "TimedOut": - return ERRNO_TIMEDOUT; - - case "Interrupted": - return ERRNO_INTR; - - case "BadResource": - return ERRNO_BADF; - - case "Busy": - return ERRNO_BUSY; - - default: - return ERRNO_INVAL; - } - } - }; -} - -interface FileDescriptor { - rid?: number; - type?: number; - flags?: number; - path?: string; - vpath?: string; - entries?: Deno.DirEntry[]; -} - -class ExitStatus { - code: number; - - constructor(code: number) { - this.code = code; - } -} - -export interface ContextOptions { - /** - * An array of strings that the WebAssembly instance will see as command-line - * arguments. - * - * The first argument is the virtual path to the command itself. - */ - args?: string[]; - - /** - * An object of string keys mapped to string values that the WebAssembly module will see as its environment. - */ - env?: { [key: string]: string | undefined }; - - /** - * An object of string keys mapped to string values that the WebAssembly module will see as it's filesystem. - * - * The string keys of are treated as directories within the sandboxed - * filesystem, the values are the real paths to those directories on the host - * machine. - * - */ - preopens?: { [key: string]: string }; - - /** - * Determines if calls to exit from within the WebAssembly module will terminate the proess or return. - */ - exitOnReturn?: boolean; - - /** - * The resource descriptor used as standard input in the WebAssembly module. - */ - stdin?: number; - - /** - * The resource descriptor used as standard output in the WebAssembly module. - */ - stdout?: number; - - /** - * The resource descriptor used as standard error in the WebAssembly module. - */ - stderr?: number; -} - -/** - * The Context class provides the environment required to run WebAssembly - * modules compiled to run with the WebAssembly System Interface. - * - * Each context represents a distinct sandboxed environment and must have its - * command-line arguments, environment variables, and pre-opened directory - * structure configured explicitly. - */ -export default class Context { - #args: string[]; - #env: { [key: string]: string | undefined }; - #exitOnReturn: boolean; - #memory: WebAssembly.Memory; - #fds: FileDescriptor[]; - #started: boolean; - - exports: Record; - - constructor(options: ContextOptions) { - this.#args = options.args ?? []; - this.#env = options.env ?? {}; - this.#exitOnReturn = options.exitOnReturn ?? true; - this.#memory = null!; - - this.#fds = [ - { - rid: options.stdin ?? Deno.stdin.rid, - type: FILETYPE_CHARACTER_DEVICE, - flags: FDFLAGS_APPEND, - }, - { - rid: options.stdout ?? Deno.stdout.rid, - type: FILETYPE_CHARACTER_DEVICE, - flags: FDFLAGS_APPEND, - }, - { - rid: options.stderr ?? Deno.stderr.rid, - type: FILETYPE_CHARACTER_DEVICE, - flags: FDFLAGS_APPEND, - }, - ]; - - if (options.preopens) { - for (const [vpath, path] of Object.entries(options.preopens)) { - const type = FILETYPE_DIRECTORY; - const entries = Array.from(Deno.readDirSync(path)); - - const entry = { - type, - entries, - path, - vpath, - }; - - this.#fds.push(entry); - } - } - - this.exports = { - "args_get": syscall(( - argvOffset: number, - argvBufferOffset: number, - ): number => { - const args = this.#args; - const textEncoder = new TextEncoder(); - const memoryData = new Uint8Array(this.#memory.buffer); - const memoryView = new DataView(this.#memory.buffer); - - for (const arg of args) { - memoryView.setUint32(argvOffset, argvBufferOffset, true); - argvOffset += 4; - - const data = textEncoder.encode(`${arg}\0`); - memoryData.set(data, argvBufferOffset); - argvBufferOffset += data.length; - } - - return ERRNO_SUCCESS; - }), - - "args_sizes_get": syscall(( - argcOffset: number, - argvBufferSizeOffset: number, - ): number => { - const args = this.#args; - const textEncoder = new TextEncoder(); - const memoryView = new DataView(this.#memory.buffer); - - memoryView.setUint32(argcOffset, args.length, true); - memoryView.setUint32( - argvBufferSizeOffset, - args.reduce(function (acc, arg) { - return acc + textEncoder.encode(`${arg}\0`).length; - }, 0), - true, - ); - - return ERRNO_SUCCESS; - }), - - "environ_get": syscall(( - environOffset: number, - environBufferOffset: number, - ): number => { - const entries = Object.entries(this.#env); - const textEncoder = new TextEncoder(); - const memoryData = new Uint8Array(this.#memory.buffer); - const memoryView = new DataView(this.#memory.buffer); - - for (const [key, value] of entries) { - memoryView.setUint32(environOffset, environBufferOffset, true); - environOffset += 4; - - const data = textEncoder.encode(`${key}=${value}\0`); - memoryData.set(data, environBufferOffset); - environBufferOffset += data.length; - } - - return ERRNO_SUCCESS; - }), - - "environ_sizes_get": syscall(( - environcOffset: number, - environBufferSizeOffset: number, - ): number => { - const entries = Object.entries(this.#env); - const textEncoder = new TextEncoder(); - const memoryView = new DataView(this.#memory.buffer); - - memoryView.setUint32(environcOffset, entries.length, true); - memoryView.setUint32( - environBufferSizeOffset, - entries.reduce(function (acc, [key, value]) { - return acc + textEncoder.encode(`${key}=${value}\0`).length; - }, 0), - true, - ); - - return ERRNO_SUCCESS; - }), - - "clock_res_get": syscall(( - id: number, - resolutionOffset: number, - ): number => { - const memoryView = new DataView(this.#memory.buffer); - - switch (id) { - case CLOCKID_REALTIME: { - const resolution = BigInt(1e6); - - memoryView.setBigUint64( - resolutionOffset, - resolution, - true, - ); - break; - } - - case CLOCKID_MONOTONIC: - case CLOCKID_PROCESS_CPUTIME_ID: - case CLOCKID_THREAD_CPUTIME_ID: { - const resolution = BigInt(1e3); - memoryView.setBigUint64(resolutionOffset, resolution, true); - break; - } - - default: - return ERRNO_INVAL; - } - - return ERRNO_SUCCESS; - }), - - "clock_time_get": syscall(( - id: number, - precision: bigint, - timeOffset: number, - ): number => { - const memoryView = new DataView(this.#memory.buffer); - - switch (id) { - case CLOCKID_REALTIME: { - const time = BigInt(Date.now()) * BigInt(1e6); - memoryView.setBigUint64(timeOffset, time, true); - break; - } - - case CLOCKID_MONOTONIC: - case CLOCKID_PROCESS_CPUTIME_ID: - case CLOCKID_THREAD_CPUTIME_ID: { - const t = performance.now(); - const s = Math.trunc(t); - const ms = Math.floor((t - s) * 1e3); - - const time = BigInt(s) * BigInt(1e9) + BigInt(ms) * BigInt(1e6); - - memoryView.setBigUint64(timeOffset, time, true); - break; - } - - default: - return ERRNO_INVAL; - } - - return ERRNO_SUCCESS; - }), - - "fd_advise": syscall(( - _fd: number, - _offset: bigint, - _length: bigint, - _advice: number, - ): number => { - return ERRNO_NOSYS; - }), - - "fd_allocate": syscall(( - _fd: number, - _offset: bigint, - _length: bigint, - ): number => { - return ERRNO_NOSYS; - }), - - "fd_close": syscall(( - fd: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - if (entry.rid) { - Deno.close(entry.rid); - } - - delete this.#fds[fd]; - - return ERRNO_SUCCESS; - }), - - "fd_datasync": syscall(( - fd: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - Deno.fdatasyncSync(entry.rid!); - - return ERRNO_SUCCESS; - }), - - "fd_fdstat_get": syscall(( - fd: number, - offset: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - const memoryView = new DataView(this.#memory.buffer); - memoryView.setUint8(offset, entry.type!); - memoryView.setUint16(offset + 2, entry.flags!, true); - // TODO(bartlomieju) - memoryView.setBigUint64(offset + 8, 0n, true); - // TODO(bartlomieju) - memoryView.setBigUint64(offset + 16, 0n, true); - - return ERRNO_SUCCESS; - }), - - "fd_fdstat_set_flags": syscall(( - _fd: number, - _flags: number, - ): number => { - return ERRNO_NOSYS; - }), - - "fd_fdstat_set_rights": syscall(( - _fd: number, - _rightsBase: bigint, - _rightsInheriting: bigint, - ): number => { - return ERRNO_NOSYS; - }), - - "fd_filestat_get": syscall(( - fd: number, - offset: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - const memoryView = new DataView(this.#memory.buffer); - - const info = Deno.fstatSync(entry.rid!); - - if (entry.type === undefined) { - switch (true) { - case info.isFile: - entry.type = FILETYPE_REGULAR_FILE; - break; - - case info.isDirectory: - entry.type = FILETYPE_DIRECTORY; - break; - - case info.isSymlink: - entry.type = FILETYPE_SYMBOLIC_LINK; - break; - - default: - entry.type = FILETYPE_UNKNOWN; - break; - } - } - - memoryView.setBigUint64(offset, BigInt(info.dev ? info.dev : 0), true); - offset += 8; - - memoryView.setBigUint64(offset, BigInt(info.ino ? info.ino : 0), true); - offset += 8; - - memoryView.setUint8(offset, entry.type); - offset += 8; - - memoryView.setUint32(offset, Number(info.nlink), true); - offset += 8; - - memoryView.setBigUint64(offset, BigInt(info.size), true); - offset += 8; - - memoryView.setBigUint64( - offset, - BigInt(info.atime ? info.atime.getTime() * 1e6 : 0), - true, - ); - offset += 8; - - memoryView.setBigUint64( - offset, - BigInt(info.mtime ? info.mtime.getTime() * 1e6 : 0), - true, - ); - offset += 8; - - memoryView.setBigUint64( - offset, - BigInt(info.birthtime ? info.birthtime.getTime() * 1e6 : 0), - true, - ); - offset += 8; - - return ERRNO_SUCCESS; - }), - - "fd_filestat_set_size": syscall(( - fd: number, - size: bigint, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - Deno.ftruncateSync(entry.rid!, Number(size)); - - return ERRNO_SUCCESS; - }), - - "fd_filestat_set_times": syscall(( - fd: number, - atim: bigint, - mtim: bigint, - flags: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - if (!entry.path) { - return ERRNO_INVAL; - } - - if ((flags & FSTFLAGS_ATIM_NOW) == FSTFLAGS_ATIM_NOW) { - atim = BigInt(Date.now() * 1e6); - } - - if ((flags & FSTFLAGS_MTIM_NOW) == FSTFLAGS_MTIM_NOW) { - mtim = BigInt(Date.now() * 1e6); - } - - Deno.utimeSync(entry.path!, Number(atim), Number(mtim)); - - return ERRNO_SUCCESS; - }), - - "fd_pread": syscall(( - fd: number, - iovsOffset: number, - iovsLength: number, - offset: bigint, - nreadOffset: number, - ): number => { - const entry = this.#fds[fd]; - if (entry == null) { - return ERRNO_BADF; - } - - const seek = Deno.seekSync(entry.rid!, 0, Deno.SeekMode.Current); - const memoryView = new DataView(this.#memory.buffer); - - let nread = 0; - for (let i = 0; i < iovsLength; i++) { - const dataOffset = memoryView.getUint32(iovsOffset, true); - iovsOffset += 4; - - const dataLength = memoryView.getUint32(iovsOffset, true); - iovsOffset += 4; - - const data = new Uint8Array( - this.#memory.buffer, - dataOffset, - dataLength, - ); - nread += Deno.readSync(entry.rid!, data) as number; - } - - Deno.seekSync(entry.rid!, seek, Deno.SeekMode.Start); - memoryView.setUint32(nreadOffset, nread, true); - - return ERRNO_SUCCESS; - }), - - "fd_prestat_get": syscall(( - fd: number, - prestatOffset: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - if (!entry.vpath) { - return ERRNO_BADF; - } - - const memoryView = new DataView(this.#memory.buffer); - memoryView.setUint8(prestatOffset, PREOPENTYPE_DIR); - memoryView.setUint32( - prestatOffset + 4, - new TextEncoder().encode(entry.vpath).byteLength, - true, - ); - - return ERRNO_SUCCESS; - }), - - "fd_prestat_dir_name": syscall(( - fd: number, - pathOffset: number, - pathLength: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - if (!entry.vpath) { - return ERRNO_BADF; - } - - const data = new Uint8Array( - this.#memory.buffer, - pathOffset, - pathLength, - ); - data.set(new TextEncoder().encode(entry.vpath)); - - return ERRNO_SUCCESS; - }), - - "fd_pwrite": syscall(( - fd: number, - iovsOffset: number, - iovsLength: number, - offset: bigint, - nwrittenOffset: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - const seek = Deno.seekSync(entry.rid!, 0, Deno.SeekMode.Current); - const memoryView = new DataView(this.#memory.buffer); - - let nwritten = 0; - for (let i = 0; i < iovsLength; i++) { - const dataOffset = memoryView.getUint32(iovsOffset, true); - iovsOffset += 4; - - const dataLength = memoryView.getUint32(iovsOffset, true); - iovsOffset += 4; - - const data = new Uint8Array( - this.#memory.buffer, - dataOffset, - dataLength, - ); - nwritten += Deno.writeSync(entry.rid!, data) as number; - } - - Deno.seekSync(entry.rid!, seek, Deno.SeekMode.Start); - memoryView.setUint32(nwrittenOffset, nwritten, true); - - return ERRNO_SUCCESS; - }), - - "fd_read": syscall(( - fd: number, - iovsOffset: number, - iovsLength: number, - nreadOffset: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - const memoryView = new DataView(this.#memory.buffer); - - let nread = 0; - for (let i = 0; i < iovsLength; i++) { - const dataOffset = memoryView.getUint32(iovsOffset, true); - iovsOffset += 4; - - const dataLength = memoryView.getUint32(iovsOffset, true); - iovsOffset += 4; - - const data = new Uint8Array( - this.#memory.buffer, - dataOffset, - dataLength, - ); - nread += Deno.readSync(entry.rid!, data) as number; - } - - memoryView.setUint32(nreadOffset, nread, true); - - return ERRNO_SUCCESS; - }), - - "fd_readdir": syscall(( - fd: number, - bufferOffset: number, - bufferLength: number, - cookie: bigint, - bufferUsedOffset: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - const memoryData = new Uint8Array(this.#memory.buffer); - const memoryView = new DataView(this.#memory.buffer); - - let bufferUsed = 0; - - const entries = Array.from(Deno.readDirSync(entry.path!)); - for (let i = Number(cookie); i < entries.length; i++) { - const nameData = new TextEncoder().encode(entries[i].name); - - const entryInfo = Deno.statSync( - resolve(entry.path!, entries[i].name), - ); - const entryData = new Uint8Array(24 + nameData.byteLength); - const entryView = new DataView(entryData.buffer); - - entryView.setBigUint64(0, BigInt(i + 1), true); - entryView.setBigUint64( - 8, - BigInt(entryInfo.ino ? entryInfo.ino : 0), - true, - ); - entryView.setUint32(16, nameData.byteLength, true); - - let type: number; - switch (true) { - case entries[i].isFile: - type = FILETYPE_REGULAR_FILE; - break; - - case entries[i].isDirectory: - type = FILETYPE_REGULAR_FILE; - break; - - case entries[i].isSymlink: - type = FILETYPE_SYMBOLIC_LINK; - break; - - default: - type = FILETYPE_REGULAR_FILE; - break; - } - - entryView.setUint8(20, type); - entryData.set(nameData, 24); - - const data = entryData.slice( - 0, - Math.min(entryData.length, bufferLength - bufferUsed), - ); - memoryData.set(data, bufferOffset + bufferUsed); - bufferUsed += data.byteLength; - } - - memoryView.setUint32(bufferUsedOffset, bufferUsed, true); - - return ERRNO_SUCCESS; - }), - - "fd_renumber": syscall(( - fd: number, - to: number, - ): number => { - if (!this.#fds[fd]) { - return ERRNO_BADF; - } - - if (!this.#fds[to]) { - return ERRNO_BADF; - } - - if (this.#fds[to].rid) { - Deno.close(this.#fds[to].rid!); - } - - this.#fds[to] = this.#fds[fd]; - delete this.#fds[fd]; - - return ERRNO_SUCCESS; - }), - - "fd_seek": syscall(( - fd: number, - offset: bigint, - whence: number, - newOffsetOffset: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - const memoryView = new DataView(this.#memory.buffer); - - // FIXME Deno does not support seeking with big integers - const newOffset = Deno.seekSync(entry.rid!, Number(offset), whence); - memoryView.setBigUint64(newOffsetOffset, BigInt(newOffset), true); - - return ERRNO_SUCCESS; - }), - - "fd_sync": syscall(( - fd: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - Deno.fsyncSync(entry.rid!); - - return ERRNO_SUCCESS; - }), - - "fd_tell": syscall(( - fd: number, - offsetOffset: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - const memoryView = new DataView(this.#memory.buffer); - - const offset = Deno.seekSync(entry.rid!, 0, Deno.SeekMode.Current); - memoryView.setBigUint64(offsetOffset, BigInt(offset), true); - - return ERRNO_SUCCESS; - }), - - "fd_write": syscall(( - fd: number, - iovsOffset: number, - iovsLength: number, - nwrittenOffset: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - const memoryView = new DataView(this.#memory.buffer); - - let nwritten = 0; - for (let i = 0; i < iovsLength; i++) { - const dataOffset = memoryView.getUint32(iovsOffset, true); - iovsOffset += 4; - - const dataLength = memoryView.getUint32(iovsOffset, true); - iovsOffset += 4; - - const data = new Uint8Array( - this.#memory.buffer, - dataOffset, - dataLength, - ); - nwritten += Deno.writeSync(entry.rid!, data) as number; - } - - memoryView.setUint32(nwrittenOffset, nwritten, true); - - return ERRNO_SUCCESS; - }), - - "path_create_directory": syscall(( - fd: number, - pathOffset: number, - pathLength: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - if (!entry.path) { - return ERRNO_INVAL; - } - - const textDecoder = new TextDecoder(); - const data = new Uint8Array( - this.#memory.buffer, - pathOffset, - pathLength, - ); - const path = resolve(entry.path!, textDecoder.decode(data)); - - Deno.mkdirSync(path); - - return ERRNO_SUCCESS; - }), - - "path_filestat_get": syscall(( - fd: number, - flags: number, - pathOffset: number, - pathLength: number, - bufferOffset: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - if (!entry.path) { - return ERRNO_INVAL; - } - - const textDecoder = new TextDecoder(); - const data = new Uint8Array( - this.#memory.buffer, - pathOffset, - pathLength, - ); - const path = resolve(entry.path!, textDecoder.decode(data)); - - const memoryView = new DataView(this.#memory.buffer); - - const info = (flags & LOOKUPFLAGS_SYMLINK_FOLLOW) != 0 - ? Deno.statSync(path) - : Deno.lstatSync(path); - - memoryView.setBigUint64( - bufferOffset, - BigInt(info.dev ? info.dev : 0), - true, - ); - bufferOffset += 8; - - memoryView.setBigUint64( - bufferOffset, - BigInt(info.ino ? info.ino : 0), - true, - ); - bufferOffset += 8; - - switch (true) { - case info.isFile: - memoryView.setUint8(bufferOffset, FILETYPE_REGULAR_FILE); - bufferOffset += 8; - break; - - case info.isDirectory: - memoryView.setUint8(bufferOffset, FILETYPE_DIRECTORY); - bufferOffset += 8; - break; - - case info.isSymlink: - memoryView.setUint8(bufferOffset, FILETYPE_SYMBOLIC_LINK); - bufferOffset += 8; - break; - - default: - memoryView.setUint8(bufferOffset, FILETYPE_UNKNOWN); - bufferOffset += 8; - break; - } - - memoryView.setUint32(bufferOffset, Number(info.nlink), true); - bufferOffset += 8; - - memoryView.setBigUint64(bufferOffset, BigInt(info.size), true); - bufferOffset += 8; - - memoryView.setBigUint64( - bufferOffset, - BigInt(info.atime ? info.atime.getTime() * 1e6 : 0), - true, - ); - bufferOffset += 8; - - memoryView.setBigUint64( - bufferOffset, - BigInt(info.mtime ? info.mtime.getTime() * 1e6 : 0), - true, - ); - bufferOffset += 8; - - memoryView.setBigUint64( - bufferOffset, - BigInt(info.birthtime ? info.birthtime.getTime() * 1e6 : 0), - true, - ); - bufferOffset += 8; - - return ERRNO_SUCCESS; - }), - - "path_filestat_set_times": syscall(( - fd: number, - flags: number, - pathOffset: number, - pathLength: number, - atim: bigint, - mtim: bigint, - fstflags: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - if (!entry.path) { - return ERRNO_INVAL; - } - - const textDecoder = new TextDecoder(); - const data = new Uint8Array( - this.#memory.buffer, - pathOffset, - pathLength, - ); - const path = resolve(entry.path!, textDecoder.decode(data)); - - if ((fstflags & FSTFLAGS_ATIM_NOW) == FSTFLAGS_ATIM_NOW) { - atim = BigInt(Date.now()) * BigInt(1e6); - } - - if ((fstflags & FSTFLAGS_MTIM_NOW) == FSTFLAGS_MTIM_NOW) { - mtim = BigInt(Date.now()) * BigInt(1e6); - } - - Deno.utimeSync(path, Number(atim), Number(mtim)); - - return ERRNO_SUCCESS; - }), - - "path_link": syscall(( - oldFd: number, - oldFlags: number, - oldPathOffset: number, - oldPathLength: number, - newFd: number, - newPathOffset: number, - newPathLength: number, - ): number => { - const oldEntry = this.#fds[oldFd]; - const newEntry = this.#fds[newFd]; - if (!oldEntry || !newEntry) { - return ERRNO_BADF; - } - - if (!oldEntry.path || !newEntry.path) { - return ERRNO_INVAL; - } - - const textDecoder = new TextDecoder(); - const oldData = new Uint8Array( - this.#memory.buffer, - oldPathOffset, - oldPathLength, - ); - const oldPath = resolve(oldEntry.path!, textDecoder.decode(oldData)); - const newData = new Uint8Array( - this.#memory.buffer, - newPathOffset, - newPathLength, - ); - const newPath = resolve(newEntry.path!, textDecoder.decode(newData)); - - Deno.linkSync(oldPath, newPath); - - return ERRNO_SUCCESS; - }), - - "path_open": syscall(( - fd: number, - dirflags: number, - pathOffset: number, - pathLength: number, - oflags: number, - rightsBase: bigint, - rightsInheriting: bigint, - fdflags: number, - openedFdOffset: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - if (!entry.path) { - return ERRNO_INVAL; - } - - const textDecoder = new TextDecoder(); - const pathData = new Uint8Array( - this.#memory.buffer, - pathOffset, - pathLength, - ); - const resolvedPath = resolve(entry.path!, textDecoder.decode(pathData)); - - if (relative(entry.path, resolvedPath).startsWith("..")) { - return ERRNO_NOTCAPABLE; - } - - let path; - if ( - (dirflags & LOOKUPFLAGS_SYMLINK_FOLLOW) == LOOKUPFLAGS_SYMLINK_FOLLOW - ) { - try { - path = Deno.realPathSync(resolvedPath); - if (relative(entry.path, path).startsWith("..")) { - return ERRNO_NOTCAPABLE; - } - } catch (_err) { - path = resolvedPath; - } - } else { - path = resolvedPath; - } - - if ((oflags & OFLAGS_DIRECTORY) !== 0) { - // XXX (caspervonb) this isn't ideal as we can't get a rid for the - // directory this way so there's no native fstat but Deno.open - // doesn't work with directories on windows so we'll have to work - // around it for now. - const entries = Array.from(Deno.readDirSync(path)); - const openedFd = this.#fds.push({ - flags: fdflags, - path, - entries, - }) - 1; - - const memoryView = new DataView(this.#memory.buffer); - memoryView.setUint32(openedFdOffset, openedFd, true); - - return ERRNO_SUCCESS; - } - - const options = { - read: false, - write: false, - append: false, - truncate: false, - create: false, - createNew: false, - }; - - if ((oflags & OFLAGS_CREAT) !== 0) { - options.create = true; - options.write = true; - } - - if ((oflags & OFLAGS_EXCL) !== 0) { - options.createNew = true; - } - - if ((oflags & OFLAGS_TRUNC) !== 0) { - options.truncate = true; - options.write = true; - } - - const read = ( - RIGHTS_FD_READ | - RIGHTS_FD_READDIR - ); - - if ((rightsBase & read) != 0n) { - options.read = true; - } - - const write = ( - RIGHTS_FD_DATASYNC | - RIGHTS_FD_WRITE | - RIGHTS_FD_ALLOCATE | - RIGHTS_FD_FILESTAT_SET_SIZE - ); - - if ((rightsBase & write) != 0n) { - options.write = true; - } - - if ((fdflags & FDFLAGS_APPEND) != 0) { - options.append = true; - } - - if ((fdflags & FDFLAGS_DSYNC) != 0) { - // TODO(caspervonb): review if we can emulate this. - } - - if ((fdflags & FDFLAGS_NONBLOCK) != 0) { - // TODO(caspervonb): review if we can emulate this. - } - - if ((fdflags & FDFLAGS_RSYNC) != 0) { - // TODO(caspervonb): review if we can emulate this. - } - - if ((fdflags & FDFLAGS_SYNC) != 0) { - // TODO(caspervonb): review if we can emulate this. - } - - if (!options.read && !options.write && !options.truncate) { - options.read = true; - } - - const { rid } = Deno.openSync(path, options); - const openedFd = this.#fds.push({ - rid, - flags: fdflags, - path, - }) - 1; - - const memoryView = new DataView(this.#memory.buffer); - memoryView.setUint32(openedFdOffset, openedFd, true); - - return ERRNO_SUCCESS; - }), - - "path_readlink": syscall(( - fd: number, - pathOffset: number, - pathLength: number, - bufferOffset: number, - bufferLength: number, - bufferUsedOffset: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - if (!entry.path) { - return ERRNO_INVAL; - } - - const memoryData = new Uint8Array(this.#memory.buffer); - const memoryView = new DataView(this.#memory.buffer); - - const pathData = new Uint8Array( - this.#memory.buffer, - pathOffset, - pathLength, - ); - const path = resolve(entry.path!, new TextDecoder().decode(pathData)); - - const link = Deno.readLinkSync(path); - const linkData = new TextEncoder().encode(link); - memoryData.set(new Uint8Array(linkData, 0, bufferLength), bufferOffset); - - const bufferUsed = Math.min(linkData.byteLength, bufferLength); - memoryView.setUint32(bufferUsedOffset, bufferUsed, true); - - return ERRNO_SUCCESS; - }), - - "path_remove_directory": syscall(( - fd: number, - pathOffset: number, - pathLength: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - if (!entry.path) { - return ERRNO_INVAL; - } - - const textDecoder = new TextDecoder(); - const data = new Uint8Array( - this.#memory.buffer, - pathOffset, - pathLength, - ); - const path = resolve(entry.path!, textDecoder.decode(data)); - - if (!Deno.statSync(path).isDirectory) { - return ERRNO_NOTDIR; - } - - Deno.removeSync(path); - - return ERRNO_SUCCESS; - }), - - "path_rename": syscall(( - fd: number, - oldPathOffset: number, - oldPathLength: number, - newFd: number, - newPathOffset: number, - newPathLength: number, - ): number => { - const oldEntry = this.#fds[fd]; - const newEntry = this.#fds[newFd]; - if (!oldEntry || !newEntry) { - return ERRNO_BADF; - } - - if (!oldEntry.path || !newEntry.path) { - return ERRNO_INVAL; - } - - const textDecoder = new TextDecoder(); - const oldData = new Uint8Array( - this.#memory.buffer, - oldPathOffset, - oldPathLength, - ); - const oldPath = resolve(oldEntry.path!, textDecoder.decode(oldData)); - const newData = new Uint8Array( - this.#memory.buffer, - newPathOffset, - newPathLength, - ); - const newPath = resolve(newEntry.path!, textDecoder.decode(newData)); - - Deno.renameSync(oldPath, newPath); - - return ERRNO_SUCCESS; - }), - - "path_symlink": syscall(( - oldPathOffset: number, - oldPathLength: number, - fd: number, - newPathOffset: number, - newPathLength: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - if (!entry.path) { - return ERRNO_INVAL; - } - - const textDecoder = new TextDecoder(); - const oldData = new Uint8Array( - this.#memory.buffer, - oldPathOffset, - oldPathLength, - ); - const oldPath = textDecoder.decode(oldData); - const newData = new Uint8Array( - this.#memory.buffer, - newPathOffset, - newPathLength, - ); - const newPath = resolve(entry.path!, textDecoder.decode(newData)); - - Deno.symlinkSync(oldPath, newPath); - - return ERRNO_SUCCESS; - }), - - "path_unlink_file": syscall(( - fd: number, - pathOffset: number, - pathLength: number, - ): number => { - const entry = this.#fds[fd]; - if (!entry) { - return ERRNO_BADF; - } - - if (!entry.path) { - return ERRNO_INVAL; - } - - const textDecoder = new TextDecoder(); - const data = new Uint8Array( - this.#memory.buffer, - pathOffset, - pathLength, - ); - const path = resolve(entry.path!, textDecoder.decode(data)); - - Deno.removeSync(path); - - return ERRNO_SUCCESS; - }), - - "poll_oneoff": syscall(( - _inOffset: number, - _outOffset: number, - _nsubscriptions: number, - _neventsOffset: number, - ): number => { - return ERRNO_NOSYS; - }), - - "proc_exit": syscall(( - rval: number, - ): never => { - if (this.#exitOnReturn) { - Deno.exit(rval); - } - - throw new ExitStatus(rval); - }), - - "proc_raise": syscall(( - _sig: number, - ): number => { - return ERRNO_NOSYS; - }), - - "sched_yield": syscall((): number => { - return ERRNO_SUCCESS; - }), - - "random_get": syscall(( - bufferOffset: number, - bufferLength: number, - ): number => { - const buffer = new Uint8Array( - this.#memory.buffer, - bufferOffset, - bufferLength, - ); - crypto.getRandomValues(buffer); - - return ERRNO_SUCCESS; - }), - - "sock_recv": syscall(( - _fd: number, - _riDataOffset: number, - _riDataLength: number, - _riFlags: number, - _roDataLengthOffset: number, - _roFlagsOffset: number, - ): number => { - return ERRNO_NOSYS; - }), - - "sock_send": syscall(( - _fd: number, - _siDataOffset: number, - _siDataLength: number, - _siFlags: number, - _soDataLengthOffset: number, - ): number => { - return ERRNO_NOSYS; - }), - - "sock_shutdown": syscall(( - _fd: number, - _how: number, - ): number => { - return ERRNO_NOSYS; - }), - }; - - this.#started = false; - } - - /** - * Attempt to begin execution of instance as a command by invoking its - * _start() export. - * - * If the instance does not contain a _start() export, or if the instance - * contains an _initialize export an error will be thrown. - * - * The instance must also have a WebAssembly.Memory export named "memory" - * which will be used as the address space, if it does not an error will be - * thrown. - */ - start(instance: WebAssembly.Instance): null | number | never { - if (this.#started) { - throw new Error("WebAssembly.Instance has already started"); - } - - this.#started = true; - - const { _start, _initialize, memory } = instance.exports; - - if (!(memory instanceof WebAssembly.Memory)) { - throw new TypeError("WebAsembly.instance must provide a memory export"); - } - - this.#memory = memory; - - if (typeof _initialize == "function") { - throw new TypeError( - "WebAsembly.instance export _initialize must not be a function", - ); - } - - if (typeof _start != "function") { - throw new TypeError( - "WebAssembly.Instance export _start must be a function", - ); - } - - try { - _start(); - } catch (err) { - if (err instanceof ExitStatus) { - return err.code; - } - - throw err; - } - - return null; - } - - /** - * Attempt to initialize instance as a reactor by invoking its _initialize() export. - * - * If instance contains a _start() export, then an exception is thrown. - * - * The instance must also have a WebAssembly.Memory export named "memory" - * which will be used as the address space, if it does not an error will be - * thrown. - */ - initialize(instance: WebAssembly.Instance) { - if (this.#started) { - throw new Error("WebAssembly.Instance has already started"); - } - - this.#started = true; - - const { _start, _initialize, memory } = instance.exports; - - if (!(memory instanceof WebAssembly.Memory)) { - throw new TypeError("WebAsembly.instance must provide a memory export"); - } - - this.#memory = memory; - - if (typeof _start == "function") { - throw new TypeError( - "WebAssembly.Instance export _start must not be a function", - ); - } - - if (typeof _initialize != "function") { - throw new TypeError( - "WebAsembly.instance export _initialize must be a function", - ); - } - - _initialize(); - } -} diff --git a/std/wasi/snapshot_preview1_test.ts b/std/wasi/snapshot_preview1_test.ts deleted file mode 100644 index b6835b2858..0000000000 --- a/std/wasi/snapshot_preview1_test.ts +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import Context from "./snapshot_preview1.ts"; -import { assert, assertEquals, assertThrows } from "../testing/asserts.ts"; -import { copy } from "../fs/mod.ts"; -import * as path from "../path/mod.ts"; - -const tests = [ - "testdata/std_env_args.wasm", - "testdata/std_env_vars.wasm", - "testdata/std_fs_create_dir.wasm", - "testdata/std_fs_file_create.wasm", - "testdata/std_fs_file_metadata.wasm", - "testdata/std_fs_file_seek.wasm", - "testdata/std_fs_file_set_len.wasm", - "testdata/std_fs_file_sync_all.wasm", - "testdata/std_fs_file_sync_data.wasm", - "testdata/std_fs_hard_link.wasm", - "testdata/std_fs_metadata.wasm", - "testdata/std_fs_read.wasm", - "testdata/std_fs_read_dir.wasm", - "testdata/std_fs_remove_dir_all.wasm", - "testdata/std_fs_rename.wasm", - "testdata/std_fs_symlink_metadata.wasm", - "testdata/std_fs_write.wasm", - "testdata/std_io_stderr.wasm", - "testdata/std_io_stdin.wasm", - "testdata/std_io_stdout.wasm", - "testdata/std_process_exit.wasm", - "testdata/wasi_clock_res_get.wasm", - "testdata/wasi_clock_time_get.wasm", - "testdata/wasi_fd_fdstat_get.wasm", - "testdata/wasi_fd_fdstat_get.wasm", - "testdata/wasi_fd_fdstat_get.wasm", - "testdata/wasi_fd_renumber.wasm", - "testdata/wasi_fd_tell_file.wasm", - "testdata/wasi_fd_write_file.wasm", - "testdata/wasi_fd_write_stderr.wasm", - "testdata/wasi_fd_write_stdout.wasm", - "testdata/wasi_path_open.wasm", - "testdata/wasi_proc_exit.wasm", - "testdata/wasi_random_get.wasm", - "testdata/wasi_sched_yield.wasm", -]; - -const ignore = []; - -// TODO(caspervonb) investigate why these tests are failing on windows and fix -// them. -// The failing tests all involve symlinks in some way, my best guess so far is -// that there's something going wrong with copying the symlinks over to the -// temporary working directory, but only in some cases. -if (Deno.build.os == "windows") { - ignore.push("testdata/std_fs_metadata.wasm"); - ignore.push("testdata/std_fs_read_dir.wasm"); - ignore.push("testdata/wasi_path_open.wasm"); -} - -const rootdir = path.dirname(path.fromFileUrl(import.meta.url)); -const testdir = path.join(rootdir, "testdata"); - -for (const pathname of tests) { - Deno.test({ - name: path.basename(pathname), - ignore: ignore.includes(pathname), - fn: async function () { - const prelude = await Deno.readTextFile( - path.resolve(rootdir, pathname.replace(/\.wasm$/, ".json")), - ); - const options = JSON.parse(prelude); - - // TODO(caspervonb) investigate more. - // On Windows creating a tempdir in the default directory breaks nearly - // all the tests, possibly due to symlinks pointing to the original file - // which crosses drive boundaries. - const workdir = await Deno.makeTempDir({ - dir: testdir, - }); - - await copy( - path.join(testdir, "fixtures"), - path.join(workdir, "fixtures"), - ); - - try { - const process = await Deno.run({ - cwd: workdir, - cmd: [ - `${Deno.execPath()}`, - "run", - "--quiet", - "--unstable", - "--allow-all", - "--no-check", - path.resolve(rootdir, "snapshot_preview1_test_runner.ts"), - prelude, - path.resolve(rootdir, pathname), - ], - stdin: "piped", - stdout: "piped", - stderr: "piped", - }); - - if (options.stdin) { - const stdin = new TextEncoder().encode(options.stdin); - await Deno.writeAll(process.stdin, stdin); - } - - process.stdin.close(); - - const stdout = await Deno.readAll(process.stdout); - - if (options.stdout) { - assertEquals(new TextDecoder().decode(stdout), options.stdout); - } else { - await Deno.writeAll(Deno.stdout, stdout); - } - - process.stdout.close(); - - const stderr = await Deno.readAll(process.stderr); - - if (options.stderr) { - assertEquals(new TextDecoder().decode(stderr), options.stderr); - } else { - await Deno.writeAll(Deno.stderr, stderr); - } - - process.stderr.close(); - - const status = await process.status(); - assertEquals(status.code, options.exitCode ? +options.exitCode : 0); - - process.close(); - } catch (err) { - throw err; - } finally { - await Deno.remove(workdir, { recursive: true }); - } - }, - }); -} - -Deno.test("context_start", function () { - assertThrows( - () => { - const context = new Context({}); - context.start({ - exports: { - _start() {}, - }, - }); - }, - TypeError, - "must provide a memory export", - ); - - assertThrows( - () => { - const context = new Context({}); - context.start({ - exports: { - _initialize() {}, - memory: new WebAssembly.Memory({ initial: 1 }), - }, - }); - }, - TypeError, - "export _initialize must not be a function", - ); - - assertThrows( - () => { - const context = new Context({}); - context.start({ - exports: { - memory: new WebAssembly.Memory({ initial: 1 }), - }, - }); - }, - TypeError, - "export _start must be a function", - ); - - { - const context = new Context({ - exitOnReturn: false, - }); - const exitCode = context.start({ - exports: { - _start() { - }, - memory: new WebAssembly.Memory({ initial: 1 }), - }, - }); - assertEquals(exitCode, null); - } - - { - const context = new Context({ - exitOnReturn: false, - }); - const exitCode = context.start({ - exports: { - _start() { - const exit = context.exports["proc_exit"] as CallableFunction; - exit(0); - }, - memory: new WebAssembly.Memory({ initial: 1 }), - }, - }); - assertEquals(exitCode, 0); - } - - assertThrows( - () => { - const context = new Context({}); - context.start({ - exports: { - memory: new WebAssembly.Memory({ initial: 1 }), - _start() {}, - }, - }); - context.start({ - exports: {}, - }); - }, - Error, - "WebAssembly.Instance has already started", - ); -}); - -Deno.test("context_initialize", function () { - assertThrows( - () => { - const context = new Context({}); - context.initialize({ - exports: { - _initialize() {}, - }, - }); - }, - TypeError, - "must provide a memory export", - ); - - assertThrows( - () => { - const context = new Context({}); - context.initialize({ - exports: { - _start() {}, - memory: new WebAssembly.Memory({ initial: 1 }), - }, - }); - }, - TypeError, - "export _start must not be a function", - ); - - assertThrows( - () => { - const context = new Context({}); - context.initialize({ - exports: { - memory: new WebAssembly.Memory({ initial: 1 }), - }, - }); - }, - TypeError, - "export _initialize must be a function", - ); - assertThrows( - () => { - const context = new Context({}); - context.initialize({ - exports: { - memory: new WebAssembly.Memory({ initial: 1 }), - _initialize() {}, - }, - }); - context.initialize({ - exports: {}, - }); - }, - Error, - "WebAssembly.Instance has already started", - ); -}); - -Deno.test("std_io_stdin.wasm with stdin as file", function () { - const stdinPath = Deno.makeTempFileSync(); - Deno.writeTextFileSync(stdinPath, "Hello, stdin!"); - - const stdinFile = Deno.openSync(stdinPath); - - const context = new Context({ - exitOnReturn: false, - stdin: stdinFile.rid, - }); - - const binary = Deno.readFileSync(path.join(testdir, "std_io_stdin.wasm")); - const module = new WebAssembly.Module(binary); - const instance = new WebAssembly.Instance(module, { - wasi_snapshot_preview1: context.exports, - }); - - context.start(instance); - - stdinFile.close(); -}); - -Deno.test("std_io_stdout.wasm with stdout as file", function () { - const stdoutPath = Deno.makeTempFileSync(); - const stdoutFile = Deno.openSync(stdoutPath, { create: true, write: true }); - - const context = new Context({ - exitOnReturn: false, - stdout: stdoutFile.rid, - }); - - const binary = Deno.readFileSync(path.join(testdir, "std_io_stdout.wasm")); - const module = new WebAssembly.Module(binary); - const instance = new WebAssembly.Instance(module, { - wasi_snapshot_preview1: context.exports, - }); - - context.start(instance); - - stdoutFile.close(); - - assertEquals(Deno.readTextFileSync(stdoutPath), "Hello, stdout!"); -}); - -Deno.test("std_io_stderr.wasm with stderr as file", function () { - const stderrPath = Deno.makeTempFileSync(); - const stderrFile = Deno.openSync(stderrPath, { create: true, write: true }); - - const context = new Context({ - exitOnReturn: false, - stderr: stderrFile.rid, - }); - - const binary = Deno.readFileSync(path.join(testdir, "std_io_stderr.wasm")); - const module = new WebAssembly.Module(binary); - const instance = new WebAssembly.Instance(module, { - wasi_snapshot_preview1: context.exports, - }); - - context.start(instance); - - stderrFile.close(); - - assertEquals(Deno.readTextFileSync(stderrPath), "Hello, stderr!"); -}); diff --git a/std/wasi/snapshot_preview1_test_runner.ts b/std/wasi/snapshot_preview1_test_runner.ts deleted file mode 100644 index a211fa378a..0000000000 --- a/std/wasi/snapshot_preview1_test_runner.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import Context from "./snapshot_preview1.ts"; - -const options = JSON.parse(Deno.args[0]); -const pathname = Deno.args[1]; -const binary = await Deno.readFile(pathname); -const module = await WebAssembly.compile(binary); - -const context = new Context({ - env: options.env, - args: [pathname].concat(options.args), - preopens: options.preopens, -}); - -const instance = new WebAssembly.Instance(module, { - "wasi_snapshot_preview1": context.exports, -}); - -context.start(instance); diff --git a/std/wasi/testdata b/std/wasi/testdata deleted file mode 160000 index afe1b368d2..0000000000 --- a/std/wasi/testdata +++ /dev/null @@ -1 +0,0 @@ -Subproject commit afe1b368d229016794be458b27be39f052e74942 diff --git a/std/ws/README.md b/std/ws/README.md deleted file mode 100644 index 658f2e038c..0000000000 --- a/std/ws/README.md +++ /dev/null @@ -1,114 +0,0 @@ -# ws - -ws module is made to provide helpers to create WebSocket server. For client -WebSockets, use the -[WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API). - -## Usage - -```ts -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { serve } from "https://deno.land/std@$STD_VERSION/http/server.ts"; -import { - acceptWebSocket, - isWebSocketCloseEvent, - isWebSocketPingEvent, - WebSocket, -} from "https://deno.land/std@$STD_VERSION/ws/mod.ts"; - -async function handleWs(sock: WebSocket) { - console.log("socket connected!"); - try { - for await (const ev of sock) { - if (typeof ev === "string") { - // text message. - console.log("ws:Text", ev); - await sock.send(ev); - } else if (ev instanceof Uint8Array) { - // binary message. - console.log("ws:Binary", ev); - } else if (isWebSocketPingEvent(ev)) { - const [, body] = ev; - // ping. - console.log("ws:Ping", body); - } else if (isWebSocketCloseEvent(ev)) { - // close. - const { code, reason } = ev; - console.log("ws:Close", code, reason); - } - } - } catch (err) { - console.error(`failed to receive frame: ${err}`); - - if (!sock.isClosed) { - await sock.close(1000).catch(console.error); - } - } -} - -if (import.meta.main) { - /** websocket echo server */ - const port = Deno.args[0] || "8080"; - console.log(`websocket server is running on :${port}`); - for await (const req of serve(`:${port}`)) { - const { conn, r: bufReader, w: bufWriter, headers } = req; - acceptWebSocket({ - conn, - bufReader, - bufWriter, - headers, - }) - .then(handleWs) - .catch(async (err) => { - console.error(`failed to accept websocket: ${err}`); - await req.respond({ status: 400 }); - }); - } -} -``` - -## API - -### isWebSocketCloseEvent - -Returns true if input value is a WebSocketCloseEvent, false otherwise. - -### isWebSocketPingEvent - -Returns true if input value is a WebSocketPingEvent, false otherwise. - -### isWebSocketPongEvent - -Returns true if input value is a WebSocketPongEvent, false otherwise. - -### unmask - -Unmask masked WebSocket payload. - -### writeFrame - -Write WebSocket frame to inputted writer. - -### readFrame - -Read WebSocket frame from inputted BufReader. - -### createMask - -Create mask from the client to the server with random 32bit number. - -### acceptable - -Returns true if input headers are usable for WebSocket, otherwise false. - -### createSecAccept - -Create value of Sec-WebSocket-Accept header from inputted nonce. - -### acceptWebSocket - -Upgrade inputted TCP connection into WebSocket connection. - -### createSecKey - -Returns base64 encoded 16 bytes string for Sec-WebSocket-Key header. diff --git a/std/ws/example_server.ts b/std/ws/example_server.ts deleted file mode 100644 index 0e99bd03a0..0000000000 --- a/std/ws/example_server.ts +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { serve } from "../http/server.ts"; -import { - acceptWebSocket, - isWebSocketCloseEvent, - isWebSocketPingEvent, - WebSocket, -} from "./mod.ts"; - -async function handleWs(sock: WebSocket): Promise { - console.log("socket connected!"); - try { - for await (const ev of sock) { - if (typeof ev === "string") { - // text message - console.log("ws:Text", ev); - await sock.send(ev); - } else if (ev instanceof Uint8Array) { - // binary message - console.log("ws:Binary", ev); - } else if (isWebSocketPingEvent(ev)) { - const [, body] = ev; - // ping - console.log("ws:Ping", body); - } else if (isWebSocketCloseEvent(ev)) { - // close - const { code, reason } = ev; - console.log("ws:Close", code, reason); - } - } - } catch (err) { - console.error(`failed to receive frame: ${err}`); - - if (!sock.isClosed) { - await sock.close(1000).catch(console.error); - } - } -} - -if (import.meta.main) { - /** websocket echo server */ - const port = Deno.args[0] || "8080"; - console.log(`websocket server is running on :${port}`); - for await (const req of serve(`:${port}`)) { - const { conn, r: bufReader, w: bufWriter, headers } = req; - acceptWebSocket({ - conn, - bufReader, - bufWriter, - headers, - }) - .then(handleWs) - .catch(async (e) => { - console.error(`failed to accept websocket: ${e}`); - await req.respond({ status: 400 }); - }); - } -} diff --git a/std/ws/example_test.ts b/std/ws/example_test.ts deleted file mode 100644 index b941216070..0000000000 --- a/std/ws/example_test.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import "./example_server.ts"; diff --git a/std/ws/mod.ts b/std/ws/mod.ts deleted file mode 100644 index 0d2141a75d..0000000000 --- a/std/ws/mod.ts +++ /dev/null @@ -1,531 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { decode, encode } from "../encoding/utf8.ts"; -import { hasOwnProperty } from "../_util/has_own_property.ts"; -import { BufReader, BufWriter } from "../io/bufio.ts"; -import { readLong, readShort, sliceLongToBytes } from "../io/ioutil.ts"; -import { Sha1 } from "../hash/sha1.ts"; -import { writeResponse } from "../http/_io.ts"; -import { TextProtoReader } from "../textproto/mod.ts"; -import { Deferred, deferred } from "../async/deferred.ts"; -import { assert } from "../_util/assert.ts"; -import { concat } from "../bytes/mod.ts"; - -export enum OpCode { - Continue = 0x0, - TextFrame = 0x1, - BinaryFrame = 0x2, - Close = 0x8, - Ping = 0x9, - Pong = 0xa, -} - -export type WebSocketEvent = - | string - | Uint8Array - | WebSocketCloseEvent // Received after closing connection finished. - | WebSocketPingEvent // Received after pong frame responded. - | WebSocketPongEvent; - -export interface WebSocketCloseEvent { - code: number; - reason?: string; -} - -/** Returns true if input value is a WebSocketCloseEvent, false otherwise. */ -export function isWebSocketCloseEvent( - a: WebSocketEvent, -): a is WebSocketCloseEvent { - return hasOwnProperty(a, "code"); -} - -export type WebSocketPingEvent = ["ping", Uint8Array]; - -/** Returns true if input value is a WebSocketPingEvent, false otherwise. */ -export function isWebSocketPingEvent( - a: WebSocketEvent, -): a is WebSocketPingEvent { - return Array.isArray(a) && a[0] === "ping" && a[1] instanceof Uint8Array; -} - -export type WebSocketPongEvent = ["pong", Uint8Array]; - -/** Returns true if input value is a WebSocketPongEvent, false otherwise. */ -export function isWebSocketPongEvent( - a: WebSocketEvent, -): a is WebSocketPongEvent { - return Array.isArray(a) && a[0] === "pong" && a[1] instanceof Uint8Array; -} - -export type WebSocketMessage = string | Uint8Array; - -export interface WebSocketFrame { - isLastFrame: boolean; - opcode: OpCode; - mask?: Uint8Array; - payload: Uint8Array; -} - -export interface WebSocket extends AsyncIterable { - readonly conn: Deno.Conn; - readonly isClosed: boolean; - - [Symbol.asyncIterator](): AsyncIterableIterator; - - /** - * @throws `Deno.errors.ConnectionReset` - */ - send(data: WebSocketMessage): Promise; - - /** - * @param data - * @throws `Deno.errors.ConnectionReset` - */ - ping(data?: WebSocketMessage): Promise; - - /** Close connection after sending close frame to peer. - * This is canonical way of disconnection but it may hang because of peer's response delay. - * Default close code is 1000 (Normal Closure) - * @throws `Deno.errors.ConnectionReset` - */ - close(): Promise; - close(code: number): Promise; - close(code: number, reason: string): Promise; - - /** Close connection forcely without sending close frame to peer. - * This is basically undesirable way of disconnection. Use carefully. */ - closeForce(): void; -} - -/** Unmask masked websocket payload */ -export function unmask(payload: Uint8Array, mask?: Uint8Array): void { - if (mask) { - for (let i = 0, len = payload.length; i < len; i++) { - payload[i] ^= mask[i & 3]; - } - } -} - -/** Write WebSocket frame to inputted writer. */ -export async function writeFrame( - frame: WebSocketFrame, - writer: Deno.Writer, -): Promise { - const payloadLength = frame.payload.byteLength; - let header: Uint8Array; - const hasMask = frame.mask ? 0x80 : 0; - if (frame.mask && frame.mask.byteLength !== 4) { - throw new Error( - "invalid mask. mask must be 4 bytes: length=" + frame.mask.byteLength, - ); - } - if (payloadLength < 126) { - header = new Uint8Array([0x80 | frame.opcode, hasMask | payloadLength]); - } else if (payloadLength < 0xffff) { - header = new Uint8Array([ - 0x80 | frame.opcode, - hasMask | 0b01111110, - payloadLength >>> 8, - payloadLength & 0x00ff, - ]); - } else { - header = new Uint8Array([ - 0x80 | frame.opcode, - hasMask | 0b01111111, - ...sliceLongToBytes(payloadLength), - ]); - } - if (frame.mask) { - header = concat(header, frame.mask); - } - unmask(frame.payload, frame.mask); - header = concat(header, frame.payload); - const w = BufWriter.create(writer); - await w.write(header); - await w.flush(); -} - -/** Read websocket frame from given BufReader - * @throws `Deno.errors.UnexpectedEof` When peer closed connection without close frame - * @throws `Error` Frame is invalid - */ -export async function readFrame(buf: BufReader): Promise { - let b = await buf.readByte(); - assert(b !== null); - let isLastFrame = false; - switch (b >>> 4) { - case 0b1000: - isLastFrame = true; - break; - case 0b0000: - isLastFrame = false; - break; - default: - throw new Error("invalid signature"); - } - const opcode = b & 0x0f; - // has_mask & payload - b = await buf.readByte(); - assert(b !== null); - const hasMask = b >>> 7; - let payloadLength = b & 0b01111111; - if (payloadLength === 126) { - const l = await readShort(buf); - assert(l !== null); - payloadLength = l; - } else if (payloadLength === 127) { - const l = await readLong(buf); - assert(l !== null); - payloadLength = Number(l); - } - // mask - let mask: Uint8Array | undefined; - if (hasMask) { - mask = new Uint8Array(4); - assert((await buf.readFull(mask)) !== null); - } - // payload - const payload = new Uint8Array(payloadLength); - assert((await buf.readFull(payload)) !== null); - return { - isLastFrame, - opcode, - mask, - payload, - }; -} - -class WebSocketImpl implements WebSocket { - readonly conn: Deno.Conn; - private readonly mask?: Uint8Array; - private readonly bufReader: BufReader; - private readonly bufWriter: BufWriter; - private sendQueue: Array<{ - frame: WebSocketFrame; - d: Deferred; - }> = []; - - constructor({ - conn, - bufReader, - bufWriter, - mask, - }: { - conn: Deno.Conn; - bufReader?: BufReader; - bufWriter?: BufWriter; - mask?: Uint8Array; - }) { - this.conn = conn; - this.mask = mask; - this.bufReader = bufReader || new BufReader(conn); - this.bufWriter = bufWriter || new BufWriter(conn); - } - - async *[Symbol.asyncIterator](): AsyncIterableIterator { - let frames: WebSocketFrame[] = []; - let payloadsLength = 0; - while (!this._isClosed) { - let frame: WebSocketFrame; - try { - frame = await readFrame(this.bufReader); - } catch (e) { - this.ensureSocketClosed(); - break; - } - unmask(frame.payload, frame.mask); - switch (frame.opcode) { - case OpCode.TextFrame: - case OpCode.BinaryFrame: - case OpCode.Continue: - frames.push(frame); - payloadsLength += frame.payload.length; - if (frame.isLastFrame) { - const concat = new Uint8Array(payloadsLength); - let offs = 0; - for (const frame of frames) { - concat.set(frame.payload, offs); - offs += frame.payload.length; - } - if (frames[0].opcode === OpCode.TextFrame) { - // text - yield decode(concat); - } else { - // binary - yield concat; - } - frames = []; - payloadsLength = 0; - } - break; - case OpCode.Close: { - // [0x12, 0x34] -> 0x1234 - const code = (frame.payload[0] << 8) | frame.payload[1]; - const reason = decode( - frame.payload.subarray(2, frame.payload.length), - ); - await this.close(code, reason); - yield { code, reason }; - return; - } - case OpCode.Ping: - await this.enqueue({ - opcode: OpCode.Pong, - payload: frame.payload, - isLastFrame: true, - }); - yield ["ping", frame.payload] as WebSocketPingEvent; - break; - case OpCode.Pong: - yield ["pong", frame.payload] as WebSocketPongEvent; - break; - default: - } - } - } - - private dequeue(): void { - const [entry] = this.sendQueue; - if (!entry) return; - if (this._isClosed) return; - const { d, frame } = entry; - writeFrame(frame, this.bufWriter) - .then(() => d.resolve()) - .catch((e) => d.reject(e)) - .finally(() => { - this.sendQueue.shift(); - this.dequeue(); - }); - } - - private enqueue(frame: WebSocketFrame): Promise { - if (this._isClosed) { - throw new Deno.errors.ConnectionReset("Socket has already been closed"); - } - const d = deferred(); - this.sendQueue.push({ d, frame }); - if (this.sendQueue.length === 1) { - this.dequeue(); - } - return d; - } - - send(data: WebSocketMessage): Promise { - const opcode = typeof data === "string" - ? OpCode.TextFrame - : OpCode.BinaryFrame; - const payload = typeof data === "string" ? encode(data) : data; - const isLastFrame = true; - const frame = { - isLastFrame, - opcode, - payload, - mask: this.mask, - }; - return this.enqueue(frame); - } - - ping(data: WebSocketMessage = ""): Promise { - const payload = typeof data === "string" ? encode(data) : data; - const frame = { - isLastFrame: true, - opcode: OpCode.Ping, - mask: this.mask, - payload, - }; - return this.enqueue(frame); - } - - private _isClosed = false; - get isClosed(): boolean { - return this._isClosed; - } - - async close(code = 1000, reason?: string): Promise { - try { - const header = [code >>> 8, code & 0x00ff]; - let payload: Uint8Array; - if (reason) { - const reasonBytes = encode(reason); - payload = new Uint8Array(2 + reasonBytes.byteLength); - payload.set(header); - payload.set(reasonBytes, 2); - } else { - payload = new Uint8Array(header); - } - await this.enqueue({ - isLastFrame: true, - opcode: OpCode.Close, - mask: this.mask, - payload, - }); - } catch (e) { - throw e; - } finally { - this.ensureSocketClosed(); - } - } - - closeForce(): void { - this.ensureSocketClosed(); - } - - private ensureSocketClosed(): void { - if (this.isClosed) return; - try { - this.conn.close(); - } catch (e) { - console.error(e); - } finally { - this._isClosed = true; - const rest = this.sendQueue; - this.sendQueue = []; - rest.forEach((e) => - e.d.reject( - new Deno.errors.ConnectionReset("Socket has already been closed"), - ) - ); - } - } -} - -/** Returns true if input headers are usable for WebSocket, otherwise false. */ -export function acceptable(req: { headers: Headers }): boolean { - const upgrade = req.headers.get("upgrade"); - if (!upgrade || upgrade.toLowerCase() !== "websocket") { - return false; - } - const secKey = req.headers.get("sec-websocket-key"); - return ( - req.headers.has("sec-websocket-key") && - typeof secKey === "string" && - secKey.length > 0 - ); -} - -const kGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - -/** Create value of Sec-WebSocket-Accept header from inputted nonce. */ -export function createSecAccept(nonce: string): string { - const sha1 = new Sha1(); - sha1.update(nonce + kGUID); - const bytes = sha1.digest(); - return btoa(String.fromCharCode(...bytes)); -} - -/** Upgrade inputted TCP connection into WebSocket connection. */ -export async function acceptWebSocket(req: { - conn: Deno.Conn; - bufWriter: BufWriter; - bufReader: BufReader; - headers: Headers; -}): Promise { - const { conn, headers, bufReader, bufWriter } = req; - if (acceptable(req)) { - const sock = new WebSocketImpl({ conn, bufReader, bufWriter }); - const secKey = headers.get("sec-websocket-key"); - if (typeof secKey !== "string") { - throw new Error("sec-websocket-key is not provided"); - } - const secAccept = createSecAccept(secKey); - const newHeaders = new Headers({ - Upgrade: "websocket", - Connection: "Upgrade", - "Sec-WebSocket-Accept": secAccept, - }); - const secProtocol = headers.get("sec-websocket-protocol"); - if (typeof secProtocol === "string") { - newHeaders.set("Sec-WebSocket-Protocol", secProtocol); - } - const secVersion = headers.get("sec-websocket-version"); - if (typeof secVersion === "string") { - newHeaders.set("Sec-WebSocket-Version", secVersion); - } - await writeResponse(bufWriter, { - status: 101, - headers: newHeaders, - }); - return sock; - } - throw new Error("request is not acceptable"); -} - -const kSecChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-.~_"; - -/** Returns base64 encoded 16 bytes string for Sec-WebSocket-Key header. */ -export function createSecKey(): string { - let key = ""; - for (let i = 0; i < 16; i++) { - const j = Math.floor(Math.random() * kSecChars.length); - key += kSecChars[j]; - } - return btoa(key); -} - -export async function handshake( - url: URL, - headers: Headers, - bufReader: BufReader, - bufWriter: BufWriter, -): Promise { - const { hostname, pathname, search } = url; - const key = createSecKey(); - - if (!headers.has("host")) { - headers.set("host", hostname); - } - headers.set("upgrade", "websocket"); - headers.set("connection", "upgrade"); - headers.set("sec-websocket-key", key); - headers.set("sec-websocket-version", "13"); - - let headerStr = `GET ${pathname}${search} HTTP/1.1\r\n`; - for (const [key, value] of headers) { - headerStr += `${key}: ${value}\r\n`; - } - headerStr += "\r\n"; - - await bufWriter.write(encode(headerStr)); - await bufWriter.flush(); - - const tpReader = new TextProtoReader(bufReader); - const statusLine = await tpReader.readLine(); - if (statusLine === null) { - throw new Deno.errors.UnexpectedEof(); - } - const m = statusLine.match(/^(?\S+) (?\S+) /); - if (!m) { - throw new Error("ws: invalid status line: " + statusLine); - } - - assert(m.groups); - const { version, statusCode } = m.groups; - if (version !== "HTTP/1.1" || statusCode !== "101") { - throw new Error( - `ws: server didn't accept handshake: ` + - `version=${version}, statusCode=${statusCode}`, - ); - } - - const responseHeaders = await tpReader.readMIMEHeader(); - if (responseHeaders === null) { - throw new Deno.errors.UnexpectedEof(); - } - - const expectedSecAccept = createSecAccept(key); - const secAccept = responseHeaders.get("sec-websocket-accept"); - if (secAccept !== expectedSecAccept) { - throw new Error( - `ws: unexpected sec-websocket-accept header: ` + - `expected=${expectedSecAccept}, actual=${secAccept}`, - ); - } -} - -export function createWebSocket(params: { - conn: Deno.Conn; - bufWriter?: BufWriter; - bufReader?: BufReader; - mask?: Uint8Array; -}): WebSocket { - return new WebSocketImpl(params); -} diff --git a/std/ws/test.ts b/std/ws/test.ts deleted file mode 100644 index c437d8c28f..0000000000 --- a/std/ws/test.ts +++ /dev/null @@ -1,495 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -import { BufReader, BufWriter } from "../io/bufio.ts"; -import { - assert, - assertEquals, - assertThrowsAsync, - fail, -} from "../testing/asserts.ts"; -import { TextProtoReader } from "../textproto/mod.ts"; -import * as bytes from "../bytes/mod.ts"; -import { - acceptable, - acceptWebSocket, - createSecAccept, - createSecKey, - createWebSocket, - handshake, - OpCode, - readFrame, - unmask, - writeFrame, -} from "./mod.ts"; -import { decode, encode } from "../encoding/utf8.ts"; -import { delay } from "../async/delay.ts"; -import { serve } from "../http/server.ts"; -import { deferred } from "../async/deferred.ts"; - -Deno.test("[ws] read unmasked text frame", async () => { - // unmasked single text frame with payload "Hello" - const buf = new BufReader( - new Deno.Buffer(new Uint8Array([0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f])), - ); - const frame = await readFrame(buf); - assertEquals(frame.opcode, OpCode.TextFrame); - assertEquals(frame.mask, undefined); - const actual = new TextDecoder().decode( - new Deno.Buffer(frame.payload).bytes(), - ); - assertEquals(actual, "Hello"); - assertEquals(frame.isLastFrame, true); -}); - -Deno.test("[ws] read masked text frame", async () => { - // a masked single text frame with payload "Hello" - const buf = new BufReader( - new Deno.Buffer( - new Uint8Array([ - 0x81, - 0x85, - 0x37, - 0xfa, - 0x21, - 0x3d, - 0x7f, - 0x9f, - 0x4d, - 0x51, - 0x58, - ]), - ), - ); - const frame = await readFrame(buf); - assertEquals(frame.opcode, OpCode.TextFrame); - unmask(frame.payload, frame.mask); - const actual = new TextDecoder().decode( - new Deno.Buffer(frame.payload).bytes(), - ); - assertEquals(actual, "Hello"); - assertEquals(frame.isLastFrame, true); -}); - -Deno.test("[ws] read unmasked split text frames", async () => { - const buf1 = new BufReader( - new Deno.Buffer(new Uint8Array([0x01, 0x03, 0x48, 0x65, 0x6c])), - ); - const buf2 = new BufReader( - new Deno.Buffer(new Uint8Array([0x80, 0x02, 0x6c, 0x6f])), - ); - const [f1, f2] = await Promise.all([readFrame(buf1), readFrame(buf2)]); - assertEquals(f1.isLastFrame, false); - assertEquals(f1.mask, undefined); - assertEquals(f1.opcode, OpCode.TextFrame); - const actual1 = new TextDecoder().decode(new Deno.Buffer(f1.payload).bytes()); - assertEquals(actual1, "Hel"); - - assertEquals(f2.isLastFrame, true); - assertEquals(f2.mask, undefined); - assertEquals(f2.opcode, OpCode.Continue); - const actual2 = new TextDecoder().decode(new Deno.Buffer(f2.payload).bytes()); - assertEquals(actual2, "lo"); -}); - -Deno.test("[ws] read unmasked ping / pong frame", async () => { - // unmasked ping with payload "Hello" - const buf = new BufReader( - new Deno.Buffer(new Uint8Array([0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f])), - ); - const ping = await readFrame(buf); - assertEquals(ping.opcode, OpCode.Ping); - const actual1 = new TextDecoder().decode( - new Deno.Buffer(ping.payload).bytes(), - ); - assertEquals(actual1, "Hello"); - // deno-fmt-ignore - const pongFrame = [0x8a, 0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58] - const buf2 = new BufReader(new Deno.Buffer(new Uint8Array(pongFrame))); - const pong = await readFrame(buf2); - assertEquals(pong.opcode, OpCode.Pong); - assert(pong.mask !== undefined); - unmask(pong.payload, pong.mask); - const actual2 = new TextDecoder().decode( - new Deno.Buffer(pong.payload).bytes(), - ); - assertEquals(actual2, "Hello"); -}); - -Deno.test("[ws] read unmasked big binary frame", async () => { - const payloadLength = 0x100; - const a = [0x82, 0x7e, 0x01, 0x00]; - for (let i = 0; i < payloadLength; i++) { - a.push(i); - } - const buf = new BufReader(new Deno.Buffer(new Uint8Array(a))); - const bin = await readFrame(buf); - assertEquals(bin.opcode, OpCode.BinaryFrame); - assertEquals(bin.isLastFrame, true); - assertEquals(bin.mask, undefined); - assertEquals(bin.payload.length, payloadLength); -}); - -Deno.test("[ws] read unmasked bigger binary frame", async () => { - const payloadLength = 0x10000; - const a = [0x82, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00]; - for (let i = 0; i < payloadLength; i++) { - a.push(i); - } - const buf = new BufReader(new Deno.Buffer(new Uint8Array(a))); - const bin = await readFrame(buf); - assertEquals(bin.opcode, OpCode.BinaryFrame); - assertEquals(bin.isLastFrame, true); - assertEquals(bin.mask, undefined); - assertEquals(bin.payload.length, payloadLength); -}); - -Deno.test("[ws] createSecAccept", () => { - const nonce = "dGhlIHNhbXBsZSBub25jZQ=="; - const d = createSecAccept(nonce); - assertEquals(d, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="); -}); - -Deno.test("[ws] acceptable", () => { - const ret = acceptable({ - headers: new Headers({ - upgrade: "websocket", - "sec-websocket-key": "aaa", - }), - }); - assertEquals(ret, true); - - assert( - acceptable({ - headers: new Headers([ - ["connection", "Upgrade"], - ["host", "127.0.0.1:9229"], - [ - "sec-websocket-extensions", - "permessage-deflate; client_max_window_bits", - ], - ["sec-websocket-key", "dGhlIHNhbXBsZSBub25jZQ=="], - ["sec-websocket-version", "13"], - ["upgrade", "WebSocket"], - ]), - }), - ); -}); - -Deno.test("[ws] acceptable should return false when headers invalid", () => { - assertEquals( - acceptable({ - headers: new Headers({ "sec-websocket-key": "aaa" }), - }), - false, - ); - assertEquals( - acceptable({ - headers: new Headers({ upgrade: "websocket" }), - }), - false, - ); - assertEquals( - acceptable({ - headers: new Headers({ upgrade: "invalid", "sec-websocket-key": "aaa" }), - }), - false, - ); - assertEquals( - acceptable({ - headers: new Headers({ upgrade: "websocket", "sec-websocket-ky": "" }), - }), - false, - ); -}); - -Deno.test("[ws] write and read masked frame", async () => { - const mask = new Uint8Array([0, 1, 2, 3]); - const msg = "hello"; - const buf = new Deno.Buffer(); - const r = new BufReader(buf); - await writeFrame( - { - isLastFrame: true, - mask, - opcode: OpCode.TextFrame, - payload: encode(msg), - }, - buf, - ); - const frame = await readFrame(r); - assertEquals(frame.opcode, OpCode.TextFrame); - assertEquals(frame.isLastFrame, true); - assertEquals(frame.mask, mask); - unmask(frame.payload, frame.mask); - assertEquals(frame.payload, encode(msg)); -}); - -Deno.test("[ws] handshake should not send search when it's empty", async () => { - const writer = new Deno.Buffer(); - const reader = new Deno.Buffer(encode("HTTP/1.1 400\r\n")); - - await assertThrowsAsync( - async (): Promise => { - await handshake( - new URL("ws://example.com"), - new Headers(), - new BufReader(reader), - new BufWriter(writer), - ); - }, - ); - - const tpReader = new TextProtoReader(new BufReader(writer)); - const statusLine = await tpReader.readLine(); - - assertEquals(statusLine, "GET / HTTP/1.1"); -}); - -Deno.test( - "[ws] handshake should send search correctly", - async function wsHandshakeWithSearch(): Promise { - const writer = new Deno.Buffer(); - const reader = new Deno.Buffer(encode("HTTP/1.1 400\r\n")); - - await assertThrowsAsync( - async (): Promise => { - await handshake( - new URL("ws://example.com?a=1"), - new Headers(), - new BufReader(reader), - new BufWriter(writer), - ); - }, - ); - - const tpReader = new TextProtoReader(new BufReader(writer)); - const statusLine = await tpReader.readLine(); - - assertEquals(statusLine, "GET /?a=1 HTTP/1.1"); - }, -); - -Deno.test("[ws] ws.close() should use 1000 as close code", async () => { - const buf = new Deno.Buffer(); - const bufr = new BufReader(buf); - const conn = dummyConn(buf, buf); - const ws = createWebSocket({ conn }); - await ws.close(); - const frame = await readFrame(bufr); - assertEquals(frame.opcode, OpCode.Close); - const code = (frame.payload[0] << 8) | frame.payload[1]; - assertEquals(code, 1000); -}); - -function dummyConn(r: Deno.Reader, w: Deno.Writer): Deno.Conn { - return { - rid: -1, - closeWrite: (): Promise => Promise.resolve(), - read: (x: Uint8Array): Promise => r.read(x), - write: (x: Uint8Array): Promise => w.write(x), - close: (): void => {}, - localAddr: { transport: "tcp", hostname: "0.0.0.0", port: 0 }, - remoteAddr: { transport: "tcp", hostname: "0.0.0.0", port: 0 }, - }; -} - -function delayedWriter(ms: number, dest: Deno.Writer): Deno.Writer { - return { - write(p: Uint8Array): Promise { - return new Promise((resolve) => { - setTimeout(async (): Promise => { - resolve(await dest.write(p)); - }, ms); - }); - }, - }; -} -Deno.test({ - name: "[ws] WebSocket.send(), WebSocket.ping() should be exclusive", - fn: async (): Promise => { - const buf = new Deno.Buffer(); - const conn = dummyConn(new Deno.Buffer(), delayedWriter(1, buf)); - const sock = createWebSocket({ conn }); - // Ensure send call - await Promise.all([ - sock.send("first"), - sock.send("second"), - sock.ping(), - sock.send(new Uint8Array([3])), - ]); - const bufr = new BufReader(buf); - const first = await readFrame(bufr); - const second = await readFrame(bufr); - const ping = await readFrame(bufr); - const third = await readFrame(bufr); - assertEquals(first.opcode, OpCode.TextFrame); - assertEquals(decode(first.payload), "first"); - assertEquals(first.opcode, OpCode.TextFrame); - assertEquals(decode(second.payload), "second"); - assertEquals(ping.opcode, OpCode.Ping); - assertEquals(third.opcode, OpCode.BinaryFrame); - assertEquals(bytes.equals(third.payload, new Uint8Array([3])), true); - }, -}); - -Deno.test("[ws] createSecKeyHasCorrectLength", () => { - // Note: relies on --seed=86 being passed to deno to reproduce failure in - // #4063. - const secKey = createSecKey(); - assertEquals(atob(secKey).length, 16); -}); - -Deno.test( - "[ws] WebSocket should throw `Deno.errors.ConnectionReset` when peer closed connection without close frame", - async () => { - const buf = new Deno.Buffer(); - const eofReader: Deno.Reader = { - read(_: Uint8Array): Promise { - return Promise.resolve(null); - }, - }; - const conn = dummyConn(eofReader, buf); - const sock = createWebSocket({ conn }); - sock.closeForce(); - await assertThrowsAsync( - () => sock.send("hello"), - Deno.errors.ConnectionReset, - ); - await assertThrowsAsync(() => sock.ping(), Deno.errors.ConnectionReset); - await assertThrowsAsync(() => sock.close(0), Deno.errors.ConnectionReset); - }, -); - -Deno.test( - "[ws] WebSocket shouldn't throw `Deno.errors.UnexpectedEof`", - async () => { - const buf = new Deno.Buffer(); - const eofReader: Deno.Reader = { - read(_: Uint8Array): Promise { - return Promise.resolve(null); - }, - }; - const conn = dummyConn(eofReader, buf); - const sock = createWebSocket({ conn }); - const it = sock[Symbol.asyncIterator](); - const { value, done } = await it.next(); - assertEquals(value, undefined); - assertEquals(done, true); - }, -); - -Deno.test({ - name: - "[ws] WebSocket should reject sending promise when connection reset forcely", - fn: async () => { - const buf = new Deno.Buffer(); - let timer: number | undefined; - const lazyWriter: Deno.Writer = { - write(_: Uint8Array): Promise { - return new Promise((resolve) => { - timer = setTimeout(() => resolve(0), 1000); - }); - }, - }; - const conn = dummyConn(buf, lazyWriter); - const sock = createWebSocket({ conn }); - const onError = (e: unknown): unknown => e; - const p = Promise.all([ - sock.send("hello").catch(onError), - sock.send(new Uint8Array([1, 2])).catch(onError), - sock.ping().catch(onError), - ]); - sock.closeForce(); - assertEquals(sock.isClosed, true); - const [a, b, c] = await p; - assert(a instanceof Deno.errors.ConnectionReset); - assert(b instanceof Deno.errors.ConnectionReset); - assert(c instanceof Deno.errors.ConnectionReset); - clearTimeout(timer); - // Wait for another event loop turn for `timeout` op promise - // to resolve, otherwise we'll get "op leak". - await delay(10); - }, -}); - -Deno.test("[ws] WebSocket should act as asyncIterator", async () => { - const pingHello = new Uint8Array([0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]); - const hello = new Uint8Array([0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]); - const close = new Uint8Array([0x88, 0x04, 0x03, 0xf3, 0x34, 0x32]); - - enum Frames { - ping, - hello, - close, - end, - } - - let frame = Frames.ping; - - const reader: Deno.Reader = { - read(p: Uint8Array): Promise { - if (frame === Frames.ping) { - frame = Frames.hello; - p.set(pingHello); - return Promise.resolve(pingHello.byteLength); - } - - if (frame === Frames.hello) { - frame = Frames.close; - p.set(hello); - return Promise.resolve(hello.byteLength); - } - - if (frame === Frames.close) { - frame = Frames.end; - p.set(close); - return Promise.resolve(close.byteLength); - } - - return Promise.resolve(null); - }, - }; - - const conn = dummyConn(reader, new Deno.Buffer()); - const sock = createWebSocket({ conn }); - - const events = []; - for await (const wsEvent of sock) { - events.push(wsEvent); - } - - assertEquals(events.length, 3); - assertEquals(events[0], ["ping", encode("Hello")]); - assertEquals(events[1], "Hello"); - assertEquals(events[2], { code: 1011, reason: "42" }); -}); - -Deno.test("[ws] WebSocket protocol", async () => { - const promise = deferred(); - const server = serve({ port: 5839 }); - - const ws = new WebSocket("ws://localhost:5839", ["foo", "bar"]); - ws.onopen = () => { - assertEquals(ws.protocol, "foo, bar"); - ws.close(); - }; - ws.onerror = () => fail(); - ws.onclose = () => { - server.close(); - promise.resolve(); - }; - - const x = await server[Symbol.asyncIterator]().next(); - if (!x.done) { - const { conn, r: bufReader, w: bufWriter, headers } = x.value; - await acceptWebSocket({ - conn, - bufReader, - bufWriter, - headers, - }); - - await promise; - } else { - fail(); - } -}); diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index da7c8703e0..37b1819d8d 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -272,8 +272,8 @@ async fn get_tls_config( } async fn run_wss_server(addr: &SocketAddr) { - let cert_file = "std/http/testdata/tls/localhost.crt"; - let key_file = "std/http/testdata/tls/localhost.key"; + let cert_file = "cli/tests/tls/localhost.crt"; + let key_file = "cli/tests/tls/localhost.key"; let tls_config = get_tls_config(cert_file, key_file).await.unwrap(); let tls_acceptor = TlsAcceptor::from(tls_config); @@ -691,8 +691,8 @@ async fn wrap_main_server() { async fn wrap_main_https_server() { let main_server_https_addr = SocketAddr::from(([127, 0, 0, 1], HTTPS_PORT)); - let cert_file = "std/http/testdata/tls/localhost.crt"; - let key_file = "std/http/testdata/tls/localhost.key"; + let cert_file = "cli/tests/tls/localhost.crt"; + let key_file = "cli/tests/tls/localhost.key"; let tls_config = get_tls_config(cert_file, key_file) .await .expect("Cannot get TLS config"); diff --git a/test_util/std b/test_util/std new file mode 160000 index 0000000000..5ae977b81f --- /dev/null +++ b/test_util/std @@ -0,0 +1 @@ +Subproject commit 5ae977b81ff1522f73301bc87f929dbdc0d69540