Deno 1.18 Release Notes
Deno 1.18 has been tagged and released with the following features and changes:
- Web Cryptography API is now complete
- Auto-discovery of the config file
Error.cause
now displayed in all stack traces- Stabilization of test steps API
- Improvements to the FFI APIs
- Support for setting headers on outbound WebSockets
- Automatic keep-alive in inbound WebSockets
- Improvements to the LSP
- Coverage is now much more robust
- Startup time is improved
- V8 upgraded to version 9.8
If you already have Deno installed, you can upgrade to 1.18 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
Web Cryptography API is now complete
This releases finalizes our 6 month long effort to fully implement the Web Cryptography API. We’re done - the entirety of the Web Cryptography API is now implemented in Deno*.
Deno now passes 98.1% of the web platform test suite for the Web Cryptography API. For some comparison data: Chrome/Edge pass 94.5% of tests, Firefox passes 93.4%, and Safari passes in 99.8% of tests. You can see the current data for yourself on wpt.fyi.
To get here, these APIs were introduced in this release:
crypto.subtle.encrypt
:- AES-GCM support
- AES-CTR support
crypto.subtle.decrypt
:- AES-GCM support
- AES-CTR support
crypto.subtle.wrapKey
:- AES-KW support
crypto.subtle.unwrapKey
:- AES-KW support
crypto.subtle.importKey
:- EC P-384 support
crypto.subtle.exportKey
:- Support for ECDSA and ECDH pkcs8/spki/jwk exports
* With the exception of some very niche features, like P-521 elliptic curve keys.
A huge thanks to Sean Michael Wykes for helping get this over the finish line.
Auto-discovery of the config file
We are continuing to iterate on the configuration file that we first
introduced in Deno v1.14.
Previously, using a configuration file required you to specify the --config
flag, followed by the path to the config file.
Starting with this release, Deno will automatically discover configuration files
with the deno.json
or deno.jsonc
filenames. You can still explicitly pass a
config file path with --config
to force a specific file to be used.
Before v1.18:
$ deno run --config ./deno.json ./src/file1.js
$ deno fmt --config ./deno.json
$ deno lint --config ./deno.json
v1.18:
$ deno run ./src/file1.js
$ deno fmt
$ deno lint
For subcommands that don’t specify file arguments (eg. deno fmt
) Deno will
look for configuration files in the current working directory, walking the
directory tree upwards until a config file is found. For subcommands that
specify file arguments (e.g. deno run ./src/file1.js
), Deno will look for a
sibling configuration file to the given entrypoint, walking the directory tree
upwards.
Consider following directory tree:
/dev
/deno
/my-project
/src
/file1.js
/file2.js
If the current working directory is /dev/deno/my-project/
and we run
deno run src/file.js
, Deno will try to find configuration file by checking
these paths in order:
/dev/deno/my-project/src/deno.json
/dev/deno/my-project/src/deno.jsonc
/dev/deno/my-project/deno.json
/dev/deno/my-project/deno.jsonc
/dev/deno/deno.json
/dev/deno/deno.jsonc
/dev/deno.json
/dev/deno.jsonc
/deno.json
/deno.jsonc
If none of the paths exists, Deno will run without applying any configuration.
Additionally, Deno’s LSP will automatically discover the configuration file too; it will look for a configuration file in the same directory as workspace root, walking the directory tree upwards until a config file is found or the root directory is reached.
We plan to iterate on the features of the configuration file further in the coming months and we look forward to your feedback.
Error.cause
now displayed in all stack traces
Error.cause
is a relatively new property that allows programs to indicate a cause for
errors. Deno has supported this property since v1.13, however the cause was not
in all types of stack traces (namely uncaught exceptions). This is now fixed: a
chain of causes will be logged for uncaught errors.
Example:
// error_cause.js
function fizz() {
throw new Error("boom!");
}
function bar() {
try {
fizz();
} catch (e) {
throw new Error("fizz() has thrown", { cause: e });
}
}
function foo() {
try {
bar();
} catch (e) {
throw new Error("bar() has thrown", { cause: e });
}
}
foo();
Before v1.18:
$ deno run error_cause.js
error: Uncaught Error: bar() has thrown
throw new Error("bar() has thrown", { cause: e });
^
at foo (file:///test.js:17:15)
at file:///test.js:21:1
v1.18:
error: Uncaught Error: bar() has thrown
throw new Error("bar() has thrown", { cause: e });
^
at foo (file:///test.js:17:15)
at file:///test.js:21:1
Caused by: Uncaught Error: fizz() has thrown
throw new Error("fizz() has thrown", { cause: e });
^
at bar (file:///test.js:9:15)
at foo (file:///test.js:15:9)
at file:///test.js:21:1
Caused by: Uncaught Error: boom!
throw new Error("boom!");
^
at fizz (file:///test.js:2:11)
at bar (file:///test.js:7:9)
at foo (file:///test.js:15:9)
at file:///test.js:21:1
Stabilization of test steps API
Deno 1.15 introduced a new API for nested test steps in --unstable
. This
release stabilizes this API after positive feedback from the community.
This API addition allows users to specify sub-steps for tests defined by
Deno.test
. These sub steps get their own sanitizer scopes
and are rendered in the test runner with indents. The new API is general enough
so it can be wrapped by polyfills to emulate existing test frameworks like mocha
or node-tap. The original explainer for this new API explains it in
more detail.
Here is an example of a test that uses the new API. It creates a database connection, runs some queries against it in sub tests and then closes the connection.
Deno.test("database test", async (t) => {
const db = await Database.connect("postgres://localhost/test");
await t.step("insert user", async () => {
const users = await db.query(
"INSERT INTO users (name) VALUES ('Deno') RETURNING *",
);
assertEquals(users.length, 1);
assertEquals(users[0].name, "Deno");
});
await t.step("insert book", async () => {
const books = await db.query(
"INSERT INTO books (name) VALUES ('The Deno Manual') RETURNING *",
);
assertEquals(books.length, 1);
assertEquals(books[0].name, "The Deno Manual");
});
db.close();
});
The same test written in Mocha style would look like this:
describe("database test", () => {
let db: Database;
beforeAll(async () => {
db = await Database.connect("postgres://localhost/test");
});
it("insert user", async () => {
const users = await db!.query(
"INSERT INTO users (name) VALUES ('Deno') RETURNING *",
);
assertEquals(users.length, 1);
assertEquals(users[0].name, "Deno");
});
it("insert book", async () => {
const books = await db!.query(
"INSERT INTO books (name) VALUES ('The Deno Manual') RETURNING *",
);
assertEquals(books.length, 1);
assertEquals(books[0].name, "The Deno Manual");
});
afterAll(() => {
db!.close();
});
});
For those more familiar with this style, we have written a simple polyfill for Mocha that builds on top of this new API: https://gist.github.com/lucacasonato/54c03bb267074aaa9b32415dbfb25522.
Improvements to the FFI APIs
This release brings another handful of updates to the unstable FFI API.
We’re seeing very interesting projects based on the FFI API, showcasing how powerful FFI API can be.
Symbol type inference
Based on the definition of symbols provided by a dynamic library, TypeScript will now infer types of available methods and raise errors if call sites don’t match the expected types.
const dylib = Deno.dlopen(
"dummy_lib.so",
{
method1: { parameters: ["usize", "usize"], result: "void" },
method2: { parameters: ["void"], result: "void" },
method3: { parameters: ["usize"], result: "void" },
} as const,
);
// Correct invocation
dylib.symbols.method1(0, 0);
// error: TS2554 [ERROR]: Expected 2 arguments, but got 1.
dylib.symbols.method1(0);
// Correct invocation
dylib.symbols.method2(void 0);
// TS2345 [ERROR]: Argument of type 'null' is not assignable to parameter of type 'void'.
dylib.symbols.method2(null);
// Correct invocation
dylib.symbols.method3(0);
// TS2345 [ERROR]: Argument of type 'null' is not assignable to parameter of type 'number'.
dylib.symbols.method3(null);
Thank you to @sinclairzx81 for implementing this feature.
Aliases for symbol definitions
When defining symbols available in the dynamic library, you can now add aliases
to them. The use case for this feature is two-fold: a) you can rename symbols to
keep a consistent style in your code (alias snake_case
to camelCase
); b)
provide multiple overloads of the same function, eg. a synchronous version that
will block execution until function returns, and a “non-blocking” version that
will run the function on a thread pool returning a promise to the result.
use std::{
thread::sleep,
time::Duration
};
#[no_mangle]
pub extern "C" fn print_something() {
println!("something");
}
#[no_mangle]
pub extern "C" fn sleep_blocking(ms: u64) {
let duration = Duration::from_millis(ms);
sleep(duration);
}
const dylib = Deno.dlopen(libPath, {
"printSomething": {
name: "print_something",
parameters: [],
result: "void",
},
"sleep_nonblocking": {
name: "sleep_blocking",
parameters: ["u64"],
result: "void",
nonblocking: true,
},
"sleep_blocking": {
parameters: ["u64"],
result: "void",
},
});
dylib.symbols.printSomething();
let start = performance.now();
dylib.symbols.sleep_blocking(100);
console.assert(performance.now() - start >= 100);
start = performance.now();
dylib.symbols.sleep_nonblocking().then(() => {
console.assert(performance.now() - start >= 100);
});
Thank you to @DjDeveloperr for implementing this feature.
Deno.UnsafeFnPointer
API
A new Deno.UnsafeFnPointer
function was added that allows to call a function
from the dynamic library that is available as a pointer.
#[no_mangle]
pub extern "C" fn add_u32(a: u32, b: u32) -> u32 {
a + b
}
#[no_mangle]
pub extern "C" fn get_add_u32_ptr() -> *const c_void {
add_u32 as *const c_void
}
const dylib = Deno.dlopen(
"dummy_lib.so",
{
get_add_u32_ptr: { parameters: [], result: "pointer" },
} as const,
);
const addU32Ptr = dylib.symbols.get_add_u32_ptr();
const addU32 = new Deno.UnsafeFnPointer(addU32Ptr, {
parameters: ["u32", "u32"],
result: "u32",
});
console.log(addU32.call(123, 456));
Thank you to @DjDeveloperr for implementing this feature.
Support for setting headers on outbound WebSockets
Users now have the ability to set custom headers on outbound WebSockets. These headers are sent with the WebSocket handshake and can be used by the server to provide additional information about the WebSocket connection. This is a non standard extension and should thus be used with care.
To use this feature, you need to use the unstable WebSocketStream
API:
const ws = new WebSocketStream("wss://example.com", {
headers: { "X-Custom-Header": "foo" },
});
The headers property in the options bag has the same signature as the headers
property in the fetch
function, and Request
/Response
constructors.
Automatic keep-alive in inbound WebSockets
WebSocket connections accepted from clients using Deno.upgradeWebSocket
now
automatically handle pong messages from clients. Ping messages are now sent
automatically when no other messages have been sent for a while to keep the
connection alive.
To configure the ping/pong interval, you can use the idleTimeout
option in the
Deno.upgradeWebSocket
options bag. The default value is 120
seconds. The
feature can be disabled by setting the value to 0
.
import { serve } from "https://deno.land/[email protected]/http/server.ts";
serve((req: Request) => {
const { socket, response } = Deno.upgradeWebSocket(req, { idleTimeout: 60 });
handleSocket(socket);
return response;
});
function handleSocket(socket: WebSocket) {
socket.onopen = (e) => {
console.log("WebSocket open");
};
}
Improvements to the LSP
This release adds a couple of new features to Deno LSP that are available to all users of VS Code, JetBrains IDEs, and many other editors.
Code lens for debugging tests
Debugging individual test cases has now been made much simpler through the
addition of “Debug” code lens quick action that will be shown above Deno.test
calls. When clicked, the test will be run with the interactive debugger attached
to your editor.
Thanks to @jespertheend for contributing this feature.
Improved registry completions
This release much improves the auto-completions for registies in the LSP. The registry completions now include helpful hovercards for each part of the URL. For deno.land/x packages, the hovercard includes package description, the star count, the last update date, and a link to the package’s documentation on doc.deno.land.
If you want to add support for registry completions for your own registry, make sure to read through the documentation.
Coverage is now much more robust
This version brings an overhaul to deno coverage
subcommand.
We received a feedback from community that generating coverage data is slow and
often the data is inaccurate. Generating coverage was slow due to indavertent
type checking of source files that were used to perform source mapping; by
simplifying the loading pipeline we managed to cut down processing time
significantly. Additionally, the deno coverage
subcommand will now warn users
if the coverage data and available sources are in an inconsistent state. The
second problem of inaccurate data was alleviated by fixing some logic used to
merge coverage data collected from multiple files.
Startup time is improved
Deno uses V8 snapshots to
provide fast startup of the runtime and TypeScript compiler. These snapshots are
binary blobs. We produce them during build time and later embed them in the
deno
executable. To make the executable smaller the snapshots were previously
compressed by V8 using zlib
.
After an investigation it became apparent that using this zlib
compression
incurs a reather significant startup overhead that could be avoided. Starting
with v1.18 Deno uses lz4
and zstd
compression for the snapshots. This change
caused up to 33% faster startup of JavaScript runtime and up to 10% faster
startup of the TypeScript compiler.
For more details, see https://github.com/denoland/deno/pull/13320.
Thank you to @evanwashere for this improvement.
V8 upgraded to version 9.8
Deno 1.18 ships with version 9.8 of the V8 engine. This release doesn’t bring any new JavaScript features, but fixes several bugs that we discovered and reported in the recent months, including crashes in private methods (https://github.com/denoland/deno/issues/12940) and bug in ES module loading (https://github.com/denoland/deno/issues/11258).