Dependency management

Ende is a package manager for JSR packages and HTTPS imports, built into the ende binary. Dependencies are declared in ende.json. This page covers the day-to-day workflow; the toolbelt table lists every command.

Quick start

In a project that already declares dependencies in ende.json, install them with:

ende install

Add a new dependency from JSR:

ende add jsr:@ende/assert

The dependency is recorded in ende.json, and you import it by its bare name:

import { assertEquals } from "@ende/assert";

The dependency toolbelt

Every dependency task has a built-in subcommand. Each links to its full reference:

Command What it does
ende install Install the project's dependencies (or a tool, with -g)
ende add Add a dependency to the config file
ende remove Remove a dependency from the config file
ende outdated List dependencies with newer versions
ende update Update dependencies to newer versions
ende why Explain why a package is in your dependency tree
ende info Show a module's full dependency graph and cache details
ende ci Install strictly from the lockfile, for CI
ende publish Publish a package to JSR
ende uninstall Remove a globally installed tool
ende clean Remove the global module cache

Choosing versions

It is possible to specify a version range for the package you are importing. This is done using the @ symbol followed by a version range specifier, and follows the semver versioning scheme. To share a single version range across workspace members, declare it in the workspace root's imports.

For example:

@scopename/mypackage           # highest version
@scopename/[email protected]    # exact version
@scopename/mypackage@16        # highest 16.x version >= 16.0.0
@scopename/mypackage@^16.1.0   # highest 16.x version >= 16.1.0
@scopename/mypackage@~16.1.0   # highest 16.1.x version >= 16.1.0

Here is an overview of all the ways you can specify a version or a range:

Symbol Description Example
1.2.3 An exact version. Only this specific version will be used. 1.2.3
^1.2.3 Compatible with version 1.2.3. Allows updates that do not change the leftmost non-zero digit.
For example, 1.2.4 and 1.3.0 are allowed, but 2.0.0 is not.
^1.2.3
~1.2.3 Approximately equivalent to version 1.2.3. Allows updates to the patch version.
For example, 1.2.4 is allowed, but 1.3.0 is not.
~1.2.3
>=1.2.3 Greater than or equal to version 1.2.3. Any version 1.2.3 or higher is allowed. >=1.2.3
<=1.2.3 Less than or equal to version 1.2.3. Any version 1.2.3 or lower is allowed. <=1.2.3
>1.2.3 Greater than version 1.2.3. Only versions higher than 1.2.3 are allowed. >1.2.3
<1.2.3 Less than version 1.2.3. Only versions lower than 1.2.3 are allowed. <1.2.3
1.2.x Any patch version within the minor version 1.2. For example, 1.2.0, 1.2.1, etc. 1.2.x
1.x Any minor and patch version within the major version 1. For example, 1.0.0, 1.1.0, 1.2.0, etc. 1.x
* Any version is allowed. *

Inspecting and updating dependencies

See which dependencies have newer versions, then update them:

ende outdated      # display what's behind, without changing anything
ende update        # update to the latest semver-compatible versions
ende update --latest   # ignore semver ranges and go to the newest releases

When you want to know where a package comes from, ende why prints every dependency path that leads to it:

ende why jsr:@ende/fmt

ende info shows a module's full dependency graph, including sizes and cache locations, for your entry point or any specifier:

ende info main.ts
ende info jsr:@ende/http

Lockfile and reproducible installs

Ende automatically maintains a ende.lock file recording the exact version and integrity hash of every dependency, and verifies it on subsequent runs. Commit it. In CI and production, install strictly from the lockfile with ende ci, which errors if ende.lock is missing or out of date instead of silently resolving new versions:

ende ci

You can also enforce a frozen lockfile on any command with --frozen-lockfile, or configure the lockfile's behavior and path in ende.json. For why this matters and what to do when the lockfile fights you, see Supply chain management.

Overriding dependencies

Ende provides mechanisms to override dependencies, enabling developers to use custom or local versions of libraries during development or testing.

Note: If you need to cache and modify dependencies locally for use across builds, set "vendor": true in ende.json to vendor remote modules.

Overriding local packages

The links field in ende.json points JSR dependencies at local copies. This allows you to override dependencies with local versions during development without needing to publish them.

Example:

ende.json
{
  "links": [
    "../some-package-or-workspace"
  ]
}

Key points:

  • The links field accepts paths to directories containing packages or workspaces. If you reference a single package within a workspace, the entire workspace will be included.
  • This feature is only respected in the workspace root. Using links elsewhere will trigger warnings.

Limitations:

  • Git-based dependency overrides are unavailable.
  • The links field requires proper configuration in the workspace root.

Overriding HTTPS imports

Ende also allows overriding HTTPS imports through the scopes field in ende.json. This feature is particularly useful when substituting a remote dependency with a local patched version for debugging or temporary fixes.

Example:

ende.json
{
  "imports": {
    "example/": "https://deno.land/x/example/"
  },
  "scopes": {
    "https://deno.land/x/example/": {
      "https://deno.land/x/[email protected]/mod.ts": "./patched/mod.ts"
    }
  }
}

Key points:

  • The scopes field in the import map allows you to redirect specific imports to alternative paths.
  • This is commonly used to override remote dependencies with local files for testing or development purposes.
  • Scopes apply only to the root of your project. Nested scopes within dependencies are ignored.

Development only dependencies

Sometimes dependencies are only needed during development, for example dependencies of test files or build tools. In Ende, the runtime does not require you to distinguish between development and production dependencies, as the runtime only loads and installs the dependencies that the code being executed actually uses.

However, it can be useful to mark dev dependencies to aid people who are reading your package. When using ende.json, the convention is to add a // dev comment after any "dev only" dependency:

ende.json
{
  "imports": {
    "@ende/fs": "jsr:@ende/fs@^2026.10.15",
    "@ende/testing": "jsr:@ende/testing@^2026.10.15" // dev
  }
}

To install only what an entrypoint actually imports, for example when building a production image, use ende install --entrypoint:

ende install --entrypoint main.ts

HTTPS imports

Ende can import modules directly from https: URLs, either inline or mapped in ende.json. This suits small single-file scripts, but JSR is recommended for applications: HTTPS imports can drift to different versions across files, aren't managed by ende add/ende install, and trust the serving host. To pin and localize them, set "vendor": true in ende.json.

Go deeper