Deno logoDeno

Deno 1.7 Release Notes

Bartek IwaƄczuk, Luca Casonato


Today we are releasing Deno 1.7.0. This release contains many new features, some stabilizations, and some great improvements to existing APIs and tooling.

If you already have Deno installed you can upgrade to 1.7 by running deno upgrade. If you are installing Deno for the first time, you can use one of the methods listed below:

# Using Shell (macOS and Linux):
curl -fsSL https://deno.land/x/install/install.sh | sh

# Using PowerShell (Windows):
iwr https://deno.land/x/install/install.ps1 -useb | iex

# Using Homebrew (macOS):
brew install deno

# Using Scoop (Windows):
scoop install deno

# Using Chocolatey (Windows):
choco install deno

Before reading on, please consider filling out the Deno survey. Even if you've never used Deno! It takes only 10 minutes and will help us tremendously in directing our development.

New features and changes

Improvements to deno compile

In the last release we added the ability to compile applications written for Deno into standalone, self-contained executables. When we initially released deno compile in 1.6.0, we had a list of pain points and features we wanted to address. This release addresses three of these.

We are happy to report that in 1.7 deno compile can now cross-compile from any in stable supported architecture (Windows x64, MacOS x64, and Linux x64) to any other in stable supported architecture. This means you can now create binaries for Windows and MacOS from a single Linux CI machine.

Additionally, deno compile now generates binaries that are 40-60% smaller than those generated by Deno 1.6. To try out this feature, use the --lite flag when compiling your application; this tells deno compile to use a slimmed-down runtime-only Deno binary instead of the full Deno binary that was used previously.

Below you can see an example of a simple hello world program being cross compiled for Linux from macOS, then it being run on Linux.

Finally, deno compile can now create binaries that have built-in CA certificates, custom V8 flags, locked down Deno permissions, and pre-populated command line arguments. This should make deno compile useful to more people.

Here is an example where we create an executable from the std/http/file_server module that listens on port 8080 (instead of default 4507), and has CORS enabled. The permissions that the running code has is also locked down (can only read from current working directory, and can only listen on port 8080).

Support for importing data URLs

Data URLs are a useful tool for executing code generated on the fly. In this release we added support for data URLs in imports (both static and dynamic), as well as in Web Workers. This feature is already supported across all modern browsers and NodeJS.

Here is an example for you to try out:

// main.ts
export const a = "a";

export enum A {
  A,
  B,
  C,
}

Above code can be expressed as following data URL: "data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=". This URL is created by base64 encoding the file contents, and appending it to data:application/typescript;base64,. For JavaScript you would append the contents to data:application/javascript;base64,.

This import specifier can later be imported like so:

// https://deno.com/v1.7/import_data_url.ts
import * as a from "data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=";

console.log(a.a);
console.log(a.A);
console.log(a.A.A);

Try it:

$ deno run https://deno.com/v1.7/import_data_url.ts
a
{ "0": "A", "1": "B", "2": "C", A: 0, B: 1, C: 2 }
0

Similarly for workers:

// https://deno.com/v1.7/worker_data_url.ts
import { deferred } from "https://deno.land/std@0.83.0/async/deferred.ts";
import { assertEquals } from "https://deno.land/std@0.83.0/testing/asserts.ts";

const promise = deferred();
const tsWorker = new Worker(
  `data:application/typescript;base64,${
    btoa(`
    if (self.name !== "tsWorker") {
      throw Error(\`Invalid worker name: \${self.name}, expected tsWorker\`);
    }
    onmessage = function (e): void {
      postMessage(e.data);
      close();
    };
  `)
  }`,
  { type: "module", name: "tsWorker" },
);

tsWorker.onmessage = (e): void => {
  assertEquals(e.data, "Hello World");
  promise.resolve();
};

tsWorker.postMessage("Hello World");

await promise;
tsWorker.terminate();

New unstable Deno.resolveDns API

This release adds a new Deno.resolveDns API. It can be used to query DNS records from a DNS resolver. At the moment only DNS over UDP/TCP is supported (no DNS over HTTPS nor DNS over TLS). One can specify a custom nameserver (for example Cloudflare's 1.1.1.1 or Google's 8.8.8.8) to use, but by default we will use the system resolver (e.g. /etc/resolv.conf on Linux).

The API currently supports A, AAAA, ANAME, CNAME, MX, PTR, SRV, and TXT records. Responses are returned as structured data.

Here is an example for you to try. The example is a very simple version of the dig tool on unix. You can pass it a domain name as the first argument, and it will return the A records for this domain via stdout.

// https://deno.com/v1.7/dig.ts
const domainName = Deno.args[0];
if (!domainName) {
  throw new Error("Domain name not specified in first argument");
}

const records = await Deno.resolveDns(domainName, "A");
for (const ip of records) {
  console.log(ip);
}
$ deno run --allow-net --unstable https://deno.com/v1.7/dig.ts deno.land
104.21.18.123
172.67.181.211

Internal compiler APIs become Deno.emit

We have replaced the three unstable APIs (Deno.transpileOnly, Deno.bundle, and Deno.compile) used to interact with Deno's built-in TypeScript compiler by a single improved function (Deno.emit). You can read all about how to use Deno.emit to bundle, transpile, and more in the TypeScript section of the manual.

Markdown support in deno fmt

deno fmt now supports formatting markdown files, including formatting of JavaScript and TypeScript codeblocks in these files.

Additionally a new flag --ext was added to allow specifying file extension when formatting code from stdin (deno fmt -). The available file extensions are js, jsx, ts, tsx, and md. Keep in mind that this flag has no effect when formatting files on disk.

Here is an example:

# Format files on disk
$ deno fmt docs.md source_code.js source_code2.ts

# Format contents from stdin as Markdown
$ cat docs.md | deno fmt --ext=md -

Align web streams API to spec

This release a lot of effort has gone into aligning our implementations of various web APIs (Text Encoding, URL, Streams, and WASM) to the various specifications for these APIs. This has mostly been internal bug fixes, but in one case specifically there is a rather severe user facing change.

Previously we were implementing Streams API according to the spec from circa March 2020. In this revision of the spec the ReadableStream class has a getIterator method that can be used to get an async iterator from the ReadableStream. In the up-to-date revision the ReadableStream class is an async iterator, and the getIterator method has been removed.

In an effort to move closer to spec, we want to remove the getIterator method on ReadableStream. To give you time to update your usage of this deprecated API, we have marked the method as deprecated for this release (1.7). We are planning to remove the deprecated method in Deno 1.8, which is slated to be released in 6 weeks, on March 2nd 2021.

This deprecated API is used in some std modules (specifically std/async, and std/http) in versions 0.83.0 or lower. Please upgrade to std version 0.84.0. In your own code, remove all .getIterator() calls as shown below:

- for await (const item of body.getIterator()) {
+ for await (const item of body) {

Support for configurable web worker permissions

By default, Deno executes user code in a full sandbox, unless the user passes --allow-* flags on the CLI. Unfortunately, those permissions can't be scoped to specific modules. Many users have requested this feature, and we are happy to announce that some progress has been made on this front. Starting with Deno 1.7, users can spawn Web Workers with custom set of permissions, making it possible to run untrusted code inside a Deno process.

It is important to know that permissions given to a worker must be a subset of process permissions, ie. if the process was run without "read" permission then trying to create worker with "read" permission will result in PermissionDenied error.

NOTE: This feature is not browser compatible. Browsers will ignore deno field in the worker option bag.

Here is an example for you to try. It will spawn a worker with the read permission, which will then try to read the file ./log.txt and send it back to the client.

// worker_permissions.ts
const workerUrl = new URL("worker_permissions_worker.ts", import.meta.url).href;
const worker = new Worker(workerUrl, {
  type: "module",
  deno: {
    namespace: true,
    permissions: {
      read: true,
    },
  },
});

worker.postMessage({ cmd: "readFile", fileName: "./log.txt" });
// worker_permissions_worker.ts
self.onmessage = async function (e) {
  const { cmd, fileName } = e.data;
  if (cmd !== "readFile") {
    throw new Error("Invalid command");
  }
  const buf = await Deno.readFile(fileName);
  const fileContents = new TextDecoder().decode(buf);
  console.log(fileContents);
  self.close();
};

Try it:

$ echo "hello world" > ./log.txt
$ deno run --allow-read --unstable https://deno.com/v1.7/worker_permissions.ts
hello world

You can also try run it without the --allow-read permission. This will cause an error to be thrown, because you are trying to escalate permissions:

$ deno run --unstable https://deno.com/v1.7/worker_permissions.ts
error: Uncaught PermissionDenied: Can't escalate parent thread permissions
    throw new ErrorClass(res.err.message);
          ^

Add support for globalThis.location and relative fetch

An unfortunate effect of Deno not having a "document" to run it's JavaScript in (a HTML page), is that Deno has never had a good way to determine the origin of a script. This is important for APIs like window.localstorage, where the data you interact with depends on the page (document) you are on. The Local Storage API is one of many APIs that makes use of the origin, but this one specifically is one we want to add soon.

This is also very useful for isomorphic code (code that runs on client and server), for example during server side rendering of React components, because they can now both make use of globalThis.location and relative fetch.

In this release we are addressing this with the addition of the --location flag that lets you set "document" location for scripts. This location can be any http or https URL (it does not need to exist). If this flag is unset, window.location will still be present, but will throw on access. The same goes for relative URLs in fetch and new Worker. They will be relative to the location if it is set, otherwise they will throw. In workers the location will always be set to the entrypoint script of the Worker.

$ cat example.ts
console.log(globalThis.location.href);

const res = await fetch("/std/version.ts");
console.log(res.status, res.url);
console.log(await res.text());
$ deno run --location="https://deno.land" --allow-net example.ts
https://deno.land/
200 https://deno.land/std@0.83.0/version.ts
// Copyright 2018-2020 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.83.0";

We are aware that some modules use window.location to determine if they are running in a browser or not. This is bad practice. Use typeof Deno !== "undefined" to determine if you are running in Deno, and use typeof document !== "undefined" to determine if the DOM is available or not.

Support for fetch request body streaming

Next to our support for streaming response bodies in fetch, we now also support streaming request bodies. This can be used to upload a large file to a webserver, without first having to buffer it in memory. This can be done by passing a ReadableStream to the body field in the fetch options.

import { readableStreamFromAsyncIterator } from "https://deno.land/std@0.84.0/io/streams.ts";

// Open the file we want to upload to the server.
const file = await Deno.open("./large_file_on_disk.txt");

// Construct a `ReadableStream` from the `Deno.Reader` returned by `Deno.open`.
const body = readableStreamFromAsyncIterator(Deno.iter(file));

// Send the body to the server.
const res = await fetch("https://myfileserver.com/upload", {
  method: "POST",
  body,
});

Currently the only runtimes supporting fetch upload streaming are Chromium and Deno. Both implementations have a limitation where you cannot start receiving the response body until the request body has been fully sent. This is not a limitation in the Fetch specification, rather in the implementations, and will be addressed in the future.

For some more examples and usecases for fetch upload streaming take a look at Jake Archibald's post on the matter: https://web.dev/fetch-upload-streaming/.

TLS session cache

Some servers require that users reuse existing TLS sessions (for example FTP). Until now Deno has not had the ability to reuse TLS sessions, and instead would re-establish a new TLS session for each connection. In this release we have added a process global TLS session cache that will allow to reuse existing TLS sessions between the connections.

The session cache is an in memory cache with a size of 1024 sessions. Overflow sessions will cause others to be evicted. The TLS session cache is used on a best effort basis.

Changes to Deno APIs

Deno.shutdown() and Conn#closeWrite() are stabilized in this release. These functions are used to gracefully close a connection by signaling to the other side that you are done sending data. Unlike the unstable version of this API, the shutdown() method no longer has a mode parameter; only the write end of a socket can be shut down.

Also in this release the signature of the option bag for the unstable Deno.createHttpClient API has changed. This API can be used to customize how a fetch is executed. Instead of being able to specify caFile (the path to a file containing a custom CA certificate), you now specify caData. This means you can now use in memory certificates for Deno.createHttpClient.

- const client = Deno.createHttpClient({ caFile: "./my_ca.pem" });
- const res = await fetch("https://my.kubernetes:4443", { client })
+ const client = Deno.createHttpClient({ caData: Deno.readFileSync("./my_ca.pem") });
+ const res = await fetch("https://my.kubernetes:4443", { client })

The unstable Deno.permission APIs for the net permission have also changed slightly. Instead of taking a url parameter, we now use host, to match what the --allow-net flag supports.

- await Deno.permissions.query({ name: "net", url: "localhost:4000" });
+ await Deno.permissions.query({ name: "net", host: "localhost:4000" });

Improvements to coverage

deno test --coverage now has the ability to report partially covered lines, as well as to collect coverage for Deno subprocesses spawned from tests. We will continue improving coverage capabilities including other report formats in the upcoming releases.

Tokio 1.0

Deno 1.7 marks the end of long migration from Tokio 0.2 to Tokio 1.0.

Users had previously reported a lot of issue regarding semi-random hangs occurring in numerous Deno APIs. After thorough investigation it was determined that all of them were caused by interaction with the tokio runtime. Due to API changes in tokio 1.0 we had to re-architecture significant parts of deno_core to cater for those changes. In effect the ResourceTable, a structure that holds Rust allocated objects (eg. a file handle, TCP connection) was rewritten from scratch, adding the ability to queue different "ops" acting on resources; this means that writes to the same socket or file are now guaranteed to happen in the same order as they were started, and so do reads.

Other news

As mentioned above, we spent a lot of time this release to align our web APIs to the various API specifications. This has been greatly helped by integrating the web platform test suite into our tests. Web platform tests is the test suite that all browser vendors use to test compatibility with the web platform specifications. We have enabled thousands of tests, but we are far from done with this effort (there are still thousands of web platform tests to enable). If you think you can help with this effort, take a look at this issue: https://github.com/denoland/deno/issues/9001.

We would greatly appreciate if you filled out the Deno survey, it takes only 10 minutes and will help us tremendously in further development of Deno.