vlt / docs

  • PricingBenchmarks (opens in new window)Community (opens in new window)Feedback
  • Overview
  • vlt
  • npm
  • pnpm
  • yarn
  • bun
  • deno
  • CI & Automation
  • Quick start
  • Install from the npm mirror
  • Install from your main registry
  • Public packages
  • Brand-new versions are hidden for 24 hours
  • Loading the token from a .env file
  • Publishing
  • Gotchas
  1. Registry
  2. /
  3. Publishing
  4. /
  5. deno

deno

Configure Deno for your vlt.io registries, and publish to main.

Verified against deno 2.9.

Deno reads npm registry configuration from .npmrc, so setup is the same shape as npm. Deno installs from both registries. It can also build a publishable npm package from a Deno project with deno pack, though the upload itself runs through npm publish — see publishing.

Quick start

Terminal
export VLT_TOKEN=<your token>

Create .npmrc in your project root:

INI
registry=https://registry.vlt.io/acme/npm/
@acme:registry=https://registry.vlt.io/acme/main/

//registry.vlt.io/acme/npm/:_authToken=${VLT_TOKEN}
//registry.vlt.io/acme/main/:_authToken=${VLT_TOKEN}

Then install as usual:

Terminal
deno install

Deno expands ${VLT_TOKEN} from the environment, so this file contains no secrets and is safe to commit.

Install from the npm mirror

Terminal
deno install npm:abbrev

Every bare npm: specifier resolves through your account mirror, including transitive dependencies. To move an existing project onto the mirror:

Terminal
rm -rf deno.lock node_modules
deno install

Install from your main registry

Terminal
deno install npm:@acme/my-package

Then import it as normal:

TypeScript
import myPackage from 'npm:@acme/my-package'

Public packages

Public packages on main install without a token — the registry line alone is enough, with no auth line:

INI
@acme:registry=https://registry.vlt.io/acme/main/

Brand-new versions are hidden for 24 hours

Like Yarn, Deno 2.9 ships a supply-chain protection that skips very recent releases — the minimum dependency age, on by default with a 24-hour window. It applies to custom registries too, so right after publishing to main an install fails:

Text
error: Could not find npm package '@acme/my-package' matching '*'.

A newer matching version was found, but it was not used because it was newer
than the specified minimum dependency date of 2026-09-09 06:54:51 UTC.

Deno names the policy and the flags in its hint text, so this one is easy to diagnose. To unblock a one-off install:

Terminal
deno install --min-dep-age=0

For a permanent fix, exempt your own scope while keeping the protection for everything else, in deno.json:

JSON
{
  "minimumDependencyAge": {
    "age": "P1D",
    "exclude": ["npm:@acme/*"],
  },
}

The npm: prefix in exclude is required. "@acme/*" on its own silently fails to match and the install stays blocked.

To switch the gate off entirely instead, either put this in .npmrc:

INI
min-release-age=0

or set NPM_CONFIG_MIN_RELEASE_AGE=0 in the environment, which is the more convenient form in CI.

Loading the token from a .env file

If you keep the token in a file rather than your shell profile:

Terminal
echo 'VLT_TOKEN=<your token>' >> .env
echo '.env' >> .gitignore

Deno does not read .env automatically — pass --env-file so the variable is available for .npmrc expansion:

Terminal
deno install --env-file

Publishing

deno publish targets JSR only — it has no --registry flag and cannot upload to main. But deno pack builds a real npm package from a Deno project, so publishing to main takes two steps: pack with deno, upload with npm.

This works from a Deno-native project with no package.json at all. Given a deno.json like this:

JSON
{
  "name": "@acme/my-package",
  "version": "1.0.0",
  "exports": "./mod.ts",
  "license": "MIT",
  "publish": { "include": ["mod.ts", "README.md", "LICENSE"] }
}

pack and publish:

Terminal
deno pack
npm publish acme-my-package-1.0.0.tgz

deno pack transpiles your TypeScript and writes a tarball containing generated .js, .d.ts and a generated package.json with correct exports, main and types fields. The result is an ordinary npm package: verified end-to-end by publishing to main and then consuming it from both deno run and plain Node.

The npm publish step needs the .npmrc from the top of this guide so the scope resolves to main. Useful deno pack flags: --set-version <v> to override the version, --dry-run to preview, and -o to name the output file.

Set publish.include before you pack

Unlike npm pack, deno pack has no built-in exclusion list — it sweeps up stray files in the project directory, including .npmrc and bunfig.toml. If either holds a literal token rather than a ${VLT_TOKEN} placeholder, packing without an allowlist publishes your registry credentials inside the tarball. npm refuses to include .npmrc; deno will happily do it.

An allowlist is the reliable fix — either publish.include in deno.json as above, or naming the files on the command line (deno pack mod.ts). Avoid relying on --ignore=.npmrc: it excludes that one file and still pulls in anything else lying around.

bun publish <tarball> can also upload a prebuilt tarball, but it aborts with MissingPackageJSON in a Deno-only project, so use npm unless you keep a package.json too.

Gotchas

  • Use ${VAR}, not $VAR. Only the braced form is expanded.
  • An unset variable is left as literal text, not emptied. A missing VLT_TOKEN is sent as the literal string ${VLT_TOKEN}, so a forgotten CI secret produces a bogus credential rather than a clear "no token" error. Use ${VLT_TOKEN?} if you want an empty value instead.
  • Most registry failures report was not found. A refused connection, a 404 and a bad token all surface as error: npm:<pkg> was not found., so check the registry URL and the token before concluding the package is missing. The age gate is the exception — it produces its own clearly-labelled error.
  • Switching registries requires regenerating deno.lock. Unlike installs from npmjs, a custom-registry install records the full tarball URL in the lockfile. A stale lockfile will keep fetching tarballs from the old host — silently, and unauthenticated if you no longer have credentials for it. Delete deno.lock when you change registries.
  • Trailing slashes must match between the registry URL and the auth line.
  • A host-wide //registry.vlt.io/:_authToken=${VLT_TOKEN} also works in deno and covers both registries, but the explicit per-registry lines above stay portable if you later add bun to the project.
  • Registry and auth settings go in .npmrc; the age gate goes in deno.json. deno.json has no registry or token field, so the two halves of this setup live in different files.
  • In a monorepo, deno looks for .npmrc next to the deno.json it loaded and then upward from there — not from the directory you happened to run the command in. Keep .npmrc beside deno.json at the workspace root.

PreviousbunNextCI & Automation

On this page

  • Quick start
  • Install from the npm mirror
  • Install from your main registry
  • Public packages
  • Brand-new versions are hidden for 24 hours
  • Loading the token from a .env file
  • Publishing
  • Gotchas
Edit this page

Deploy your package on vlt.io

Publish scoped and private packages, manage organizations and access, and give every developer and CI environment a consistent source for public and private JavaScript dependencies.

Publish now