Deno 2.6: dx is the new npx

Product Update

Deno 2.6 introduces the dx utility for package binary execution, enhances security with granular permissions and deno audit, accelerates type checking, and significantly improves Node.js compatibility and dependency management. This update also stabilizes key APIs and refines development workflows.

To upgrade to Deno 2.6, execute deno upgrade in your terminal. If Deno is not yet installed, follow one of the installation methods below:

  • Using Shell (macOS and Linux):
    curl -fsSL https://deno.land/install.sh | sh
    
  • Using PowerShell (Windows):
    iwr https://deno.land/install.ps1 -useb | iex
    

What's New in Deno 2.6

This release introduces a host of enhancements, including:

  • Running package binaries with dx
  • More granular permissions
  • Faster type-checking with tsgo and LSP improvements
  • Wasm source phase imports
  • Running CommonJS with --require
  • Security auditing with deno audit
  • Dependency management improvements
  • Bundler enhancements
  • Expanded Node.js compatibility
  • API changes and stabilizations
  • Significant performance boosts
  • Quality-of-life improvements for developers
  • V8 engine upgraded to version 14.2

Run Package Binaries with dx

Deno 2.6 unveils dx, a new utility designed as an equivalent to npx, providing a convenient way to execute binaries from npm and JSR packages.

$ dx cowsay "Hello, Deno!"
______________
< Hello, Deno! >
--------------
   \
    ^__^
    (oo)\\_______
    (__)\\       )\\\/\
        ||----w |
        ||     ||

To enable dx, ensure you install its alias:

deno x --install-alias

Experienced users will note that dx operates similarly to deno run, but with key distinctions:

  • dx defaults to --allow-all permissions, unless specific permission flags are provided.
  • dx prompts for confirmation before downloading a package.
  • dx automatically runs lifecycle scripts if the user accepts the download prompt.
  • dx defaults to npm:<package_name> unless an alternative specifier is used.
  • dx prevents running local files, reserving its function for package binaries.

With dx, executing package binaries becomes more intuitive, offering npx-like convenience while leveraging Deno’s robust security model and performance optimizations. Further details on dx are available in the Deno documentation.

More Granular Permissions

This release enhances control over permissions with the introduction of --ignore-read and --ignore-env flags. These allow selective suppression of file reads or environment variable access. Instead of a NotCapable error, Deno can now return NotFound for ignored file reads or undefined for ignored environment variables.

Consider this example:

main.ts

const awsSecretKey = Deno.env.get("AWS_SECRET_KEY");
console.log(awsSecretKey);

const passwd = await Deno.readTextFile("/etc/passwd");
console.log(passwd);

Running with granular ignore flags:

$ deno run --ignore-read=/etc --ignore-env=AWS_SECRET_KEY main.ts
undefined
error: Uncaught (in promise) NotFound: No such file or directory (os error 2)
    at Object.readTextFile (ext:deno_fs/30_fs.js:799:24)
    at file:///dev/main.ts:4:27

These flags offer greater flexibility when executing untrusted code. They address scenarios where dependencies might attempt to read numerous environment variables or configuration files, which they typically handle gracefully when missing, but previously failed on Deno's NotCapable errors. Now, such code can run without granting full permissions.

Additionally, Deno.env.toObject() now supports partial environment permissions. If only a subset of environment variables is allowed, Deno.env.toObject() will reflect this by returning only the permitted variables.

main.ts

console.log(Deno.env.toObject());

Executing with partial environment access:

$ SOME_SECRET=foo deno run --allow-env=SOME_SECRET main.ts
{ SOME_SECRET: "foo" }

An experimental permission broker is also included, offering advanced control over permission requests via a separate management process. This is particularly useful for platform authors needing to mediate permissions for untrusted code.

$ DENO_PERMISSION_BROKER_PATH=/perm_broker.sock deno run untrusted_code.ts

When active, the permission broker overrides all --allow-*, --deny-*, and --ignore-* flags, routing all permission requests to the broker process. More information is available in the Deno documentation.

Faster Type Checking with tsgo and Language Server Improvements

Deno 2.6 integrates tsgo, an experimental TypeScript type checker re-written in Go. This new checker offers significantly faster performance compared to its TypeScript-based predecessor.

Enable tsgo for deno check using the --unstable-tsgo flag or DENO_UNSTABLE_TSGO=1 environment variable:

$ deno check --unstable-tsgo main.ts

Internal projects have observed up to a 2x speed improvement in type-checking times with tsgo.

Several long-standing issues in type checking have been resolved. Deno now exhibits greater tolerance for non-standard import schemes and bare ambient module declarations, reducing false-positive errors when integrating with framework tooling or custom module loaders.

Support for common tsconfig.json options has also advanced:

  • compilerOptions.paths now functions as expected for module resolution.
  • skipLibCheck is respected for graph errors.
  • Advanced settings like isolatedDeclarations are now supported.

These improvements contribute to a more predictable type-checking experience, especially within multi-package workspaces.

The language server (LSP) has received quality-of-life upgrades. The source.organizeImports action is now supported, enabling automatic import sorting and cleanup directly within your editor. Deno leverages the editor’s native implementation where available, ensuring a seamless experience. Test authoring is also enhanced with improved integration for describe and it style test functions, allowing editors to better understand and surface information about individual test cases.

Under-the-hood fixes make the language server less intrusive: configuration updates are detected immediately upon tsconfig.json changes, lockfiles are not written during "cache on save" operations, and lint-ignore directives behave more consistently with leading comments. These refinements minimize friction and enhance the overall developer workflow.

Wasm Source Phase Imports

Deno 2.6 introduces "source phase imports," a new JavaScript feature. Unlike standard import statements that provide a live module instance, source phase imports offer the raw source representation of a module. For WebAssembly (Wasm), this means you can import a compiled Wasm.Module directly during the build step, eliminating the need to fetch the file at runtime.

Here’s an example:

main.ts

import source addModule from "./add.wasm";

const addInstance = WebAssembly.instantiate(addModule);
const add = addInstance.exports.add;

console.log(add(1, 2));

Combined with the Wasm imports introduced in Deno v2.1, working with WebAssembly in Deno is now more ergonomic and efficient.

Run CommonJS with --require

Following the --preload flag shipped in Deno v2.4, which allows loading files before the main module executes, Deno 2.6 introduces the --require flag. This flag serves a similar purpose but is specifically designed for executing CommonJS modules instead of ES modules.

$ deno run --require ./setup.cjs main.ts

This addition further improves Deno’s Node.js compatibility by supporting a common pattern for preloading modules. Support for custom module loaders is planned for a future release.

Security Auditing with deno audit

A significant addition is the new deno audit subcommand, which helps identify security vulnerabilities in your dependencies by checking the GitHub CVE database. This command generates a report for both JSR and npm packages.

For an additional layer of security, an experimental deno audit --socket flag integrates with socket.dev:

$ deno install npm:lodahs
Add npm:lodahs@0.0.1-security

Dependencies:
+ npm:lodahs 0.0.1-security

$ deno audit --socket
No known vulnerabilities found

Socket.dev firewall report

╭ pkg:npm/lodahs@0.0.1-security
│ Supply Chain Risk: 0
│ Maintenance: 76
│ Quality: 41
│ Vulnerabilities: 100
│ License: 100
╰ Alerts (1/0/0):
  [critical] malware

Found 1 alerts across 1 packages
Severity: 0 low, 0 medium, 0 high, 1 critical

The deno audit command scans your entire dependency graph against the GitHub CVE database and, if --socket is present, socket.dev’s vulnerability database. This is particularly valuable in CI/CD pipelines for enforcing build failures when vulnerabilities are detected.

If the SOCKET_API_KEY environment variable is present, Deno will use it for more detailed socket.dev reports, applying your organization’s policies and access rules.

Dependency Management

Deno 2.6 continues to refine its dependency management features, offering several notable improvements for managing, auditing, and controlling project dependencies. These enhancements address common pain points and provide better visibility into supply chain security.

Granular Control over postinstall Scripts with deno approve-scripts

Many npm packages utilize lifecycle scripts (e.g., postinstall, install, preinstall) that execute arbitrary code during installation. While often legitimate (e.g., compiling native addons, setting up configurations), these scripts also pose a security risk.

The new deno approve-scripts replaces deno install --allow-scripts, providing more ergonomic and granular control over which packages can execute these scripts.

When deno install encounters packages with unapproved lifecycle scripts, Deno will issue a warning and suggest running deno approve-scripts. This command initiates an interactive picker, allowing you to review and selectively approve or deny scripts for each package:

$ deno approve-scripts
? Select which packages to approve lifecycle scripts for (<space> to select, ↑/↓/j/k to navigate, a to select all, i to invert selection, enter to accept,
<Ctrl-c> to cancel)
○ npm:@denotest/node-addon@1.0.0
  ● npm:chalk@5.0.0

Your choices are saved in the allowScripts configuration within your deno.json file, creating an audit trail of packages trusted to execute code during installation.

Controlling Dependency Stability

You can now enforce a minimum age for dependencies via the deno.json configuration file, ensuring your project only uses vetted dependencies. This mitigates risks associated with newly published packages that might contain malware or breaking changes.

Specify the minimum age in your configuration:

deno.json

{
  "minimumDependencyAge": "7 days"
}

This option ensures that during dependency installation or updates, only packages at least 7 days old will be used. The minimum age can be specified in minutes (e.g., "120" for two hours), ISO-8601 duration (e.g., "P2D" for two days), or RFC3339 absolute timestamp (e.g., "2025-09-16" for a cutoff date, "2025-09-16T12:00:00+00:00" for a cutoff time), or "0" to disable the feature.

Improved Lockfile and Install Workflows

The --lockfile-only flag for deno install enables updating your lockfile without downloading or installing the actual packages. This is particularly useful in CI environments for verifying dependency changes without modifying node_modules or cache, facilitating parallel builds and reducing unnecessary downloads:

$ deno install --lockfile-only # Updates deno.lock without fetching packages
$ deno install                # Now installs with verified lockfile

Installation output for npm packages has been improved, offering clearer feedback on which packages were added, updated, or reused from cache, even without a traditional node_modules directory. Additionally, edge cases related to package.bin resolution and shimming on Windows have been fixed to better align with npm’s behavior, enhancing compatibility with ecosystem tools. Finally, subpath imports beginning with '#/' are now supported for improved Node.js compatibility.

Bundler Improvements

Deno 2.6 brings significant refinements to its bundler, enhancing reliability and compatibility. The bundler now works seamlessly within Web Workers, allowing dynamic code bundling in multithreaded contexts. Handling of different target platforms has improved; when bundling for the browser with --platform browser, the bundler correctly avoids Node.js-specific APIs like createRequire, ensuring clean browser-compatible bundles.

Runtime-specific specifiers such as cloudflare: and bun: are now treated as external dependencies by default, preventing them from being bundled and causing import errors:

main.ts

import { serve } from "jsr:@std/http";
import { toml } from "bun:toml";         // External, not bundled
import { parseEnv } from "cloudflare:workers"; // External, not bundled

export default {
  fetch(req) {
    return new Response("Hello from Cloudflare Workers");
  },
};

Bundling this example:

$ deno bundle --platform browser main.ts bundle.js
# bun:toml and cloudflare:workers remain as external imports

Other improvements include fixing the transformation of import.meta.main when bundling JSR entrypoints, enhancing type safety by correctly typing the output of Deno.bundle() as Uint8Array<ArrayBuffer>, and improving development workflows so that HTML entrypoints reload properly with --watch. Reliability fixes address failed esbuild cleanup operations and internal name clashes that could lead to mysterious build failures.

Node.js Compatibility

Deno’s Node.js compatibility layer continues to mature in Deno 2.6, with dozens of improvements across file operations, cryptography, process management, and database APIs. This release underscores the commitment to making Node.js code "just work" in Deno.

@types/node Included by Default

TypeScript developers receive a major quality-of-life improvement: @types/node type declarations are now included by default. Previously, manual import of @types/node was required for proper type hints for Node.js APIs. Deno now handles this automatically, providing IDE autocompletion and type safety for all Node.js compatibility features without any setup:

import { readFile } from "node:fs/promises";
// ✅ Full type hints without manual installation
const data = await readFile("./file.txt", "utf-8");

Deno will still respect any existing @types/node version defined in your project, ensuring compatibility with specific type requirements.

Node.js API Fixes and Changes:

  • node:crypto
    • Respect authTagLength in createCipheriv for GCM ciphers (#31253)
    • Autopadding behavior on crypto.Cipheriv (#31389)
    • crypto Cipheriv and Decipheriv base64 encoding (#30806)
    • Inspect X509Certificate class (#30882)
    • Prevent cipher operations after finalize (#31533)
    • Accept ArrayBuffer on crypto.timingSafeEqual (#30773)
  • node:fs
    • Implement FileHandle.appendFile(data[, options]) (#31301)
    • Implement FileHandle.readLines() (#31107)
    • Missing statfs export from node:fs/promises (#31528)
    • fs.cp and fs.cpSync compatibility (#30502)
    • fs.read/fs.readSync and fs.write/fs.writeSync compatibility (#31013)
    • fs.readFile, fs.readFileSync assert encoding (#30830)
    • fs.stat and fs.statSync compatibility (#30637, #30866)
    • fs.statfsSync and fs.statfs compatibility (#30662)
    • FileHandle compatibility (#31164, #31094)
    • fs.realpath buffer encoding (#30885)
    • Respect abort signal option on FileHandle.readFile (#31462)
    • Respects flag option on fs.readfile and fs.readfilesync (#31129)
    • Set default callback for fs.close (#30720)
    • Validate fs.close callback function (#30679)
    • Validate fs.read on empty buffer (#30706)
    • Validate readlink arguments (#30691)
    • Support option object parameter on fs.write and fs.writeSync (#30999)
    • Make fs.glob accept URL cwd (#30705)
  • node:process
    • Define process.versions.sqlite (#31277)
    • Export process.ppid (#31137)
    • False deprecation warning on crypto.createHmac (#31025)
    • Implement process:seteuid() (#31160)
    • Implement process.setegid() (#31155)
    • Implement process.setgid() and process.setuid() (#31162)
    • process.moduleLoadList as undefined (#31022)
    • Ensure process.argv is an array of strings (#31322)
    • Stub missing process.sourceMapsEnabled (#31358)
    • Make process.stdin.isTTY writable (#31464)
    • Checking Symbol in env should not ask for permission (#30965)
    • Handle falsy values enumerability in process.env (#30708)
  • node:sqlite
    • Implement ‘backup’ capability (#29842)
    • StatementSync.iterate() should reset is_iter_finished flag on every call (#31361)
    • Allow ATTACH DATABASE with --allow-all (#30763)
    • Fix sqlite extension used for testing; ensure related tests are actually meaningful (#31455)
    • Implement DatabaseSync.aggregate() (#31461)
    • Implement DatabaseSync.function() and better error details (#31386)
    • Implement StatementSync#columns() method (#31119)
    • setAllowUnknownNamedParameters error message (#31319)
    • sqlite.DatabaseSync explicit resource management compatibility (#31311)
    • Fix segfault on calling StatementSync methods after connection has closed (#31331)
    • Add setAllowUnknownNamedParameters option (#31202)
  • Other Assorted Changes:
    • Fix misused napi_callback_info in CallbackInfo (#30983)
    • Ensure that the node:console implementation has an implementation for emitWarning in scope (#31263)
    • Support advanced serialization in IPC (#31380)
    • deepStrictEqual now correctly handles Number objects (#31233)
    • Return string family in server.address() (#31465)
    • Ensure active timers entry is deleted on Timeout.prototype.refresh (#31436)
    • dns.resolve6 compatibility (#30974)
    • path.matchesGlob compatibility (#30976)
    • url.domainToASCII returns empty string for invalid domains (#31219)
    • Avoid stack overflow in node:zlib’s gunzip (#30865)
    • cpus() should not error when there’s no cpu info (#31097)
    • Ensure 'exit' event is fired only once for worker_threads (#31231)
    • Handle empty writes in chunked HTTP requests (#31066)
    • Handle multiple calls in inspector.Session.post() (#31067)
    • Implement dns.lookupService (#31310)
    • Implement fchmod on Windows (#30704)
    • Implement performance.timerify() (#31238)
    • Implement util.getSystemErrorMessage() (#31147)
    • Inconsistent error message thrown by AssertionError (#31089)
    • Make kReinitializeHandle work for TLS wrap (#31079)
    • Map BadResource error to the corresponding Node error (#30926)
    • Omit smi from zlib.crc32 op function (#30907)
    • Reimplement setImmediate API (#30328)
    • setTimeout promisified to handle abort signal (#30855)
    • Truncate first non-hex value on Buffer.from (#31227)

API Changes

Deno 2.6 introduces new APIs and stabilizes existing ones.

  • The BroadcastChannel API is now stable, simplifying communication across workers and contexts.

    main.ts

    const channel = new BroadcastChannel("my-channel");
    channel.onmessage = (event) => {
      console.log("Message from worker:", event.data);
    };
    const worker = new Worker("./worker.ts");
    worker.postMessage("Hello from main");
    

    worker.ts

    const channel = new BroadcastChannel("my-channel");
    channel.postMessage("Hello from worker");
    
  • Web streams (ReadableStream, WritableStream, and TransformStream) now support transferability, allowing efficient passing between workers without copying.

    transfer.ts

    const INDEX_HTML = Deno.readTextFileSync("./index.html");
    const worker = new Worker("./the_algorithm.js", { type: "module" });
    
    Deno.serve(async (req) => {
      if (req.method === "POST" && req.path === "/the-algorithm") {
        const { port1, port2 } = new MessageChannel();
        worker.postMessage(
          { stream: req.body, port: port1 },
          { transfer: [req.body, port1] },
        );
        const res = await new Promise((resolve) => {
          port1.onmessage = (e) => resolve(e.data);
        });
        return new Response(res);
      }
      if (req.path === "/") {
        return new Response(INDEX_HTML, { "content-type": "text/html" });
      }
      return new Response(null, { status: 404 });
    });
    
  • ImageData now supports Float16Array, offering more efficient memory usage for certain image processing workflows.

    image_data.ts

    const imageData = new ImageData(
      new Float16Array(width * height * 4),
      width,
      height,
    );
    
  • Signal handling is improved with support for integer signals in Deno.kill() and child process kill methods.

  • Deno.HttpClient now works with WebSocket connections, including new TCP proxy support.

    main.ts

    const client = new Deno.HttpClient({
      allowHost: true,
      proxy: { url: new URL("http://proxy.example.com:8080") },
    });
    const ws = new WebSocket("wss://api.example.com/socket", {
      httpClient: client,
    });
    

Performance Improvements

Deno 2.6 delivers performance enhancements across the platform.

  • A memory leak in the fetch API, which affected multi-threaded programs, has been resolved.
  • V8 engine integration is optimized with stack-allocated scopes, reducing memory allocation overhead and improving garbage collection efficiency.
  • package.json resolution now employs negative caching, preventing repetitive checks for non-existent files and yielding significant performance boosts for projects with numerous dependencies.
  • For Node.js compatibility, critical operations like getOwnNonIndexProperties and Buffer.compare have been moved to native code, improving performance for code relying on these APIs.

Quality of Life Improvements

Beyond major features, Deno 2.6 introduces numerous smaller yet impactful improvements for a smoother development experience.

  • The deno install -g command now requires a -- separator when passing arguments to installed scripts, preventing ambiguity between deno flags and script-specific flags. Additionally, multiple global packages can now be installed in a single command:

    # Install multiple packages at once
    deno install -g npm:prettier npm:typescript
    
  • For JSR package publishers, adding "publish": false to deno.json prevents accidental publishing of monorepo packages or private modules.

    deno.json

    {
      "name": "@myorg/private-tools",
      "publish": false
    }
    
  • Test coverage improvements enhance code quality measurement. Coverage data is now collected from workers (previously excluded), and Blob URLs are properly excluded to reduce noise from dynamically generated code. HTML coverage reports now include a dark mode toggle for improved readability. JUnit test reports are now correctly formatted without ANSI escape codes, ensuring compatibility with CI/CD systems expecting clean XML.

  • Stack traces have been dramatically improved with better filtering of internal frames. Deno now filters out noisy internal frames, dims internal Deno runtime frames in grey for clearer distinction from user code, and shows relative paths for enhanced readability:

    Error formatting in Deno v2.5

    Error formatting in Deno v2.6

  • Command-line completions for deno task are now dynamic and context-aware. As tasks are defined in deno.json, shell autocompletion will automatically discover and suggest them. Remember to re-generate shell completions after upgrading using deno completions --dynamic.

  • The lint plugin API now has access to env and read permissions, enabling more sophisticated linting rules that can interact with environment configuration and read files.

  • A small but useful feature is the --empty flag for deno init, which creates an empty deno.json file, ideal for starting new projects from scratch.

    $ deno init --empty
    ✅ Project initialized
    
    $ cat deno.json
    {}
    
  • Source map support has been improved with native runtime support. When an exception is thrown, Deno will now check for "magic" comments like //# sourceMappingURL=... and apply the source map to the stack trace, enhancing debugging.

V8 14.2

Deno 2.6 upgrades the V8 engine to version 14.2, incorporating the latest performance improvements, bug fixes, and new JavaScript features from the V8 team.