Deno 1.17 Release Notes
Deno 1.17 has been tagged and released with the following features and changes:
- Import assertions and JSON modules
- Improvements to the Web Cryptography API
--no-check=remote
flag- Unstable support for ALPN in
Deno.connectTls()
- Unref timers
- Updates to abort reasons in AbortSignal
- Updates to the Deno Language Server
- Updates to
deno test
- Updates to the file watcher
- Updates to the REPL
- Updates to the FFI API
- TypeScript 4.5
- Updates to the standard library
If you already have Deno installed, you can upgrade to 1.17 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
New features and changes
Import assertions and JSON modules
Deno v1.17 ships with full support for import assertions and JSON modules.
Import assertions is a Stage 3 proposal that shipped in V8 earlier this year. The main use case of this proposal is to allow imports of JSON modules; which without import assertions could pose a security vulnerability.
Prior to this release the only way to read JSON file was to use fetch()
or
Deno.readTextFile()
in combination with JSON.parse()
. Starting with this
release you can import JSON files directly into your module graph:
// Prior to Deno v1.17
// Get JSON data from remote server
const response = await fetch("https://example.com/data.json");
const jsonData = await response.json();
console.log(jsonData);
// Get JSON data from local file
const text = await Deno.readTextFile("./data.json");
const jsonData = JSON.parse(text);
console.log(jsonData);
// Starting with Deno v1.17
// Get JSON data from remote server
import jsonData from "https://exmaple.com/data.json" with { type: "json" };
console.log(jsonData);
// Get JSON data from local file
import jsonData from "./data.json" with { type: "json" };
console.log(jsonData);
You can also import JSON modules dynamically using import()
:
const jsonData = await import("./data.json", { assert: { type: "json" } });
console.log(jsonData);
An advantage of using import declarations for JSON modules is that you don’t need to provide permissions to import them; dynamic imports are subject to regular permissions checks.
Improvements to the Web Cryptography API
This release once again adds new features to the Web Cryptography API. With these additions, the API is now nearing feature parity with browsers.
Added in v1.17:
crypto.subtle.importKey
:- support importing RSA keys in SPKI format
- support importing RSA keys in JWK format
- support importing AES keys in JWK format
- support importing EC keys in PKCS#8 format
- support importing EC keys in SPKI format
- support importing EC keys in JWK format
crypto.subtle.exportKey
:- support exporting RSA keys in JWK format
- support exporting AES keys in JWK format
crypto.subtle.unwrapKey
:- support for unwrapping using RSA-OAEP
- support for unwrapping using AES-GCM
The last few features (elliptic curve key imports and exports, and more AES encryption/decryption formats) are still in development, but are coming along nicely and will likely be available in the next release.
See progress here: denoland/deno#11690
Thanks to Yacine Hmito, Sean Michael Wykes and Divy Srivastava for contributing these improvements.
--no-check=remote
flag
One of the common challenges with Deno is that sometimes remote dependencies don’t type check properly even though they have the correct runtime behavior. When type checking your local code this can cause diagnostics to be issued and your program not to run even when you have no control over the remote dependencies.
The --no-check=remote
option was added to help with this. When this flag is
passed, the program will be type checked as a whole, but any diagnostics that
are coming from a remote module will be discarded. If there are no local
diagnostics, your program will run.
Pass the option on the command line:
> deno run --no-check=remote server.ts
We encourage you to experiment with this flag and report feedback. We think this mode might be a good default type checking configuration for future releases of Deno.
Deno.connectTls()
Unstable support for negotiating ALPN for ALPN is a TLS extension that allows a client to negotiate a communication protocol with a server. This is used when using HTTP/2 for example, to negotiate if the server supports HTTP/2.
Up to now Deno did not support manually negotiating ALPN. This release adds that capability. If you are interested in this functionality, we’d love to hear your feedback.
An example:
const conn = await Deno.connectTls({
hostname: "example.com",
port: 443,
alpnProtocols: ["h2", "http/1.1"],
});
const { alpnProtocol } = await conn.handshake();
if (alpnProtocol !== null) {
console.log("Negotiated protocol:", alpnProtocol);
} else {
console.log("No protocol negotiated");
}
Thanks to Yury Selivanov for contributing this feature.
Unref timers
This release adds two new unstable APIs:
Deno.unrefTimer(id: number)
Deno.refTimer(id: number)
These APIs can be used to change the behavior of timers (setTimeout
and
setInterval
) to block or not block the event loop from exiting.
By default all timers block the event loop from exiting until they are cleared.
In the following example the program will finish after printing
hello from timeout
after 5 seconds.
setTimeout(() => {
console.log("hello from timeout");
}, 5000);
console.log("hello world!");
However sometimes it’s not desired for timers to prevent the event loop from exiting. An example of this behavior is periodic collection of telemetry data:
// Collect data every 5s
setInterval(() => {
const data = collectSomeData();
fetch("https://example.com/telemetry", { method: "POST", body: data })
.catch(() => {});
}, 5000);
// Main entry point to the program
await longRunningTask();
In the above example, we want our program to exit once longRunningTask
finishes. To achieve this behavior we can “unref” the interval:
// Collect data every 5s
const intervalId = setInterval(() => {
const data = collectSomeData();
fetch("https://example.com/telemetry", { body: data }).catch(() => {});
}, 5000);
// Unref the telemetry interval so the program exits immediately, once
// `longRunningTask` finishes.
Deno.unrefTimer(intervalId);
// Main entry point to the program
await longRunningTask();
AbortSignal
Updates to abort reasons in The last release added support for aborting AbortSignal
s with specific abort
reasons. These reasons are now correctly propagated through these APIs:
Deno.readFile()
andDeno.readTextFile()
Deno.writeFile()
andDeno.writeTextFile()
- All of the WHATWG streams APIs (
ReadableStream
,TransformStream
,WritableStream
, and friends). WebSocketStream
fetch
,Request
, andResponse
In addition we now support the new throwIfAborted()
method on AbortSignal
that can be used to synchronously throw an error if the signal has already been
aborted.
const controller = new AbortController();
const signal = controller.signal;
try {
signal.throwIfAborted();
} catch (err) {
unreachable(); // the abort signal is not yet aborted
}
controller.abort("Hello World");
try {
signal.throwIfAborted();
unreachable(); // the method throws this time
} catch (err) {
assertEquals(err, "Hello World");
}
Thanks to Andreu Botella for contributing these changes.
Updates to the Deno Language Server
The Deno Language Server adds support for a revised module registry suggestion
protocol. This will allow large package registries (like NPM) to provide partial
result sets, intelligent incremental search, and documentation details about
packages and modules as part of the editing experience. Adding the new
capabilities to deno.land/x
and deno.land/std
will happen soon after 1.17
ships.
Another behind the scenes improvement is that the values from the package registries will respect the cache headers supplied. Previously, the results from a registry were cached indefinitely and required users to clear the cache manually to get new or changed results.
To take advantage of these features, ensure that your editor’s Deno
configuration includes supported registries in the deno.suggest.imports.hosts
are configured. For example:
{
"deno.suggest.imports.hosts": {
"https://deno.land": true,
"https://cdn.nest.land": true,
"https://crux.land": true
}
}
While not related directly to the Deno 1.17 release, vscode_deno is being updated to enable known package registries which support the registry protocol by default. This will increase visibility of this feature, making it easier for people to find code to run under Deno.
With the new features of the registry protocol, we hope to work with the likes of esm.sh, Skypack and JSPM to provide intelligent registries to make it easy to consume code from the wider JavaScript and TypeScript ecosystem.
If you want to add support for import completions to your own registry, you can read more about the registry protocol here.
Also, the Deno Language Server reaches a level of maturity, adding a couple of outstanding features, including goto type definition support and workspace symbol searching.
deno test
Updates to Deno ships with a built-in test runner. You can register tests using
Deno.test()
API. Until now there were two available overloads of the API -
“shorthand” and “definition” based. The shorthand works well for simple tests,
while the definition gives the most control, allowing for example tests to be
ignored:
import { assertEquals } from "https://deno.land/[email protected]/testing/asserts.ts";
Deno.test("My test description", (): void => {
assertEquals("hello", "hello");
});
Deno.test({
name: "example test",
ignore: Deno.build.os == "windows",
fn(): void {
assertEquals("world", "world");
},
});
It becomes quite cumbersome if you need to ignore/focus a single test that was
written using “shorthand” overload. You would need to rewrite whole test to use
the “definition” overload. To alleviate this problem and provide more
flexibility in writing tests we’ve added four more overloads to the
Deno.test()
API.
import { assertEquals } from "https://deno.land/[email protected]/testing/asserts.ts";
Deno.test(function myTestName(): void {
assertEquals("hello", "hello");
});
Deno.test("My test description", { permissions: { read: true } }, (): void => {
assertEquals("hello", "hello");
});
Deno.test(
{ name: "My test description", permissions: { read: true } },
(): void => {
assertEquals("hello", "hello");
},
);
Deno.test({ permissions: { read: true } }, function myTestName(): void {
assertEquals("hello", "hello");
});
You can now mix both forms, and provide a partial “definition” as the first or second argument.
Another update is for users using unstable test steps API. The final test report output will now include number of test steps with distinction for passed/ignored/failed steps. The test step API is scheduled to be stabilized in v1.18
Deno.test("nested failure", async (t) => {
const success = await t.step("step 1", async (t) => {
let success = await t.step("inner 1", () => {
throw new Error("Failed.");
});
if (success) throw new Error("Expected failure");
success = await t.step("inner 2", () => {});
if (!success) throw new Error("Expected success");
});
if (success) throw new Error("Expected failure");
});
$ deno test --unstable test.ts
running 1 test from file:///dev/test.ts
test nested failure ...
test step 1 ...
test inner 1 ... FAILED (11ms)
Error: Failed.
at file:///dev/test.ts:4:13
at testStepSanitizer (deno:runtime/js/40_testing.js:187:13)
at asyncOpSanitizer (deno:runtime/js/40_testing.js:68:15)
at resourceSanitizer (deno:runtime/js/40_testing.js:138:13)
at exitSanitizer (deno:runtime/js/40_testing.js:170:15)
at TestContext.step (deno:runtime/js/40_testing.js:800:19)
at file:///dev/test.ts:3:27
at testStepSanitizer (deno:runtime/js/40_testing.js:187:13)
at asyncOpSanitizer (deno:runtime/js/40_testing.js:68:15)
at resourceSanitizer (deno:runtime/js/40_testing.js:138:13)
test inner 2 ... ok (11ms)
FAILED (32ms)
FAILED (44ms)
failures:
nested failure
Error: 1 test step failed.
at runTest (deno:runtime/js/40_testing.js:432:11)
at async Object.runTests (deno:runtime/js/40_testing.js:541:22)
failures:
nested failure
test result: FAILED. 0 passed (1 step); 1 failed (2 steps); 0 ignored; 0 measured; 0 filtered out (66ms)
Updates to the file watcher
Deno ships with a built-in file watcher that can be activated using the
--watch
flag.
The file watcher automatically discovers files to watch based on the files
contained in module graph (for the deno run
subcommand) or file paths passed
as arguments (for subcommands like deno lint
or deno fmt
).
In this release the --watch
flag for deno run
accepts an optional list of
external files that should also be watched for changes.
$ deno run --watch=data/external.txt script.ts
Thanks to Jasper van den End for contributing this feature.
A small quality of life improvement to the watcher is automatic clearing of terminal screen on restart:
Updates to the REPL
Module specifier completions
The REPL (deno repl
) now provides completions for relative specifiers on the
file system and absolute specifiers from the deno.land
registry.
These completions can be requested by pressing the <TAB>
key while typing a
module specifier.
Node.js compatibility mode
The REPL can now be run in
Node.js compatibility mode;
allowing access to Node’s global variables (eg. process
), import CommonJS
modules with require()
and providing ESM resolution compatible with Node.js.
To enter REPL in compatibility mode use deno repl --compat --unstable
.
$ deno repl --compat --unstable
Deno 1.17.0
exit using ctrl+d or close()
> console.log(process)
process {
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
...
}
Ignore certificate errors
This release adds the --unsafely-ignore-certificate-errors
flag to the
deno repl
subcommand that allows disabling TLS certificate verification.
Note that this is a dangerous setting. You should not use this flag to silence certificate errors. Disabling TLS certificate verification (ignoring certificate errors) makes TLS pointless because it allows MITM attacks. Data sent over a TLS connection is not confidential if the certificate has not been verified and trusted.
Read more in Deno 1.13 Release Notes blog post.
Thanks to VishnuJin for contributing this feature.
Updates to the FFI API
Following
improvements to the FFI API in v1.15
this release adds two new APIs for working with pointers across languages
boundary: Deno.UnsafePointer
and Deno.UnsafePointerView
. Additionally
buffer
parameter type was renamed to pointer
.
Example:
// test_ffi.rs
static BUFFER: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
#[no_mangle]
pub extern "C" fn return_buffer() -> *const u8 {
BUFFER.as_ptr()
}
#[no_mangle]
pub extern "C" fn is_null_ptr(ptr: *const u8) -> u8 {
ptr.is_null() as u8
}
// test_ffi.js
const [libPrefix, libSuffix] = {
darwin: ["lib", "dylib"],
linux: ["lib", "so"],
windows: ["", "dll"],
}[Deno.build.os];
const libPath = `${targetDir}/${libPrefix}test_ffi.${libSuffix}`;
const dylib = Deno.dlopen(libPath, {
"return_buffer": { parameters: [], result: "pointer" },
"is_null_ptr": { parameters: ["pointer"], result: "u8" },
});
const ptr = dylib.symbols.return_buffer();
dylib.symbols.print_buffer(ptr, 8);
const ptrView = new Deno.UnsafePointerView(ptr);
const into = new Uint8Array(6);
const into2 = new Uint8Array(3);
const into2ptr = Deno.UnsafePointer.of(into2);
const into2ptrView = new Deno.UnsafePointerView(into2ptr);
const into3 = new Uint8Array(3);
ptrView.copyInto(into);
console.log([...into]);
ptrView.copyInto(into2, 3);
console.log([...into2]);
into2ptrView.copyInto(into3);
console.log([...into3]);
const string = new Uint8Array([
...new TextEncoder().encode("Hello from pointer!"),
0,
]);
const stringPtr = Deno.UnsafePointer.of(string);
const stringPtrview = new Deno.UnsafePointerView(stringPtr);
console.log(stringPtrview.getCString());
console.log(stringPtrview.getCString(11));
console.log(Boolean(dylib.symbols.is_null_ptr(ptr)));
console.log(Boolean(dylib.symbols.is_null_ptr(null)));
console.log(Boolean(dylib.symbols.is_null_ptr(Deno.UnsafePointer.of(into))));
$ deno run --allow-ffi --allow-read --unstable test_ffi.js
[1, 2, 3, 4, 5, 6, 7, 8]
[ 1, 2, 3, 4, 5, 6 ]
[ 4, 5, 6 ]
[ 4, 5, 6 ]
Hello from pointer!
pointer!
false
true
false
We are already seeing interesting projects using these new APIs, eg. native bindings to SQLite C API: https://deno.land/x/sqlite3
FFI requires the --allow-ffi
and the --unstable
flags. Beware that enabling
FFI will effectively disable all security protections in Deno.
Thank you to Elias Sjögreen for contributing this feature.
TypeScript 4.5
Deno 1.17 ships with the latest stable version of TypeScript. This was part of what unlocked the ability to support import assertions. Also, a new way to import types is supported:
Previously it was recommended to import items only be used in the type position this way:
import { A } from "https://example.com/mod.ts";
import type { B } from "https://example.com/mod.ts";
But now these can be combined into a single import statement:
import { A, type B } from "https://example.com/mod.ts";
Also, TypeScript 4.5 added support for .mts
and .d.mts
extensions as well as
first class support for .mjs
files, which are properly handled in Deno 1.17.
For more information on new features in TypeScript see TypeScript’s 4.5 blog post
Updates to the standard library
This release includes a number of new features and breaking changes to the standard library. We are hard at work at cleaning up the standard library to get it into a state where it can stabilized.
Breaking changes
In version 0.118.0 of deno_std
following APIs undergone breaking changes:
ws
module was removed - instead useDeno.upgradeWebSocket()
APIassertThrowsAsync
fromtesting/asserts.ts
was removed - instead useassertRejects
API from the same modulehttp/server_legacy.ts
was removed - a legacy implementation of HTTP server, instead usehttp/server.ts
copy
API was removed fromfs/mod.ts
- instead import this API directly fromfs/copy.ts
onSignal
was removed fromsignals
module - instead useDeno.addSignalListener()
APIfindLast
andfindLastIndex
were removed fromcollections
modulehttp
server module no longer accepts string address, use{ host: string, port: number }
interface instead
New HTTP server options
Previously the serve
function in the std/http
module would swallow all
errors produced by the HTTP handler function. This release changes that
behavior, so that the server will log all errors produced by handlers using
console.error
. You can customize the behavior by passing a custom onError
function to the serve
options.
import { Server } from "https://deno.land/[email protected]/http/server.ts";
const port = 4505;
const handler = (request: Request) => {
const body = `Your user-agent is:\n\n${
request.headers.get(
"user-agent",
) ?? "Unknown"
}`;
return new Response(body, { status: 200 });
};
const onError = (_error: unknown) => {
return new Response("custom error page", { status: 500 });
};
const server = new Server({ port, handler, onError });
std/collections
Additions to std/collections
is a playground for us to experiment with more advanced
functions for iterating over arrays and iterables. This release adds
aggregateGroups
. It applies the given aggregator to each group in the given
grouping, returning the results together with the respective group keys:
import { aggregateGroups } from "https://deno.land/[email protected]/collections/mod.ts";
import { assertEquals } from "https://deno.land/[email protected]/testing/asserts.ts";
const foodProperties = {
"Curry": ["spicy", "vegan"],
"Omelette": ["creamy", "vegetarian"],
};
const descriptions = aggregateGroups(
foodProperties,
(current, key, first, acc) => {
if (first) {
return `${key} is ${current}`;
}
return `${acc} and ${current}`;
},
);
assertEquals(descriptions, {
"Curry": "Curry is spicy and vegan",
"Omelette": "Omelette is creamy and vegetarian",
});
std/fmt/bytes
Byte formatting using A new std/fmt/bytes
module has been added to the standard library. It provides
a function for formatting bytes in a human readable format:
import { prettyBytes } from "https://deno.land/[email protected]/fmt/bytes.ts";
import { assertEquals } from "https://deno.land/[email protected]/testing/asserts.ts";
assertEquals(prettyBytes(1024), "1.02 kB");
assertEquals(prettyBytes(1024, { binary: true }), "1 kiB");
This API is a port of the popular pretty-bytes npm package.