npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

enclave-artifacts

v0.4.2

Published

Publish and manage artifacts on a self-hosted enclave instance

Readme

enclave-artifacts

Command-line client for enclave — self-hostable artifact generation and hosting. Publish a local directory to your instance and manage what you have published, without opening a browser.

This is the client. It talks to an enclave server you run yourself; it is not useful on its own.

Early software. First release. The server it talks to has never run anywhere but its author's machine and CI.

Install

npm install -g enclave-artifacts

Or run it without installing:

npx enclave-artifacts push ./dist

The command is enclave either way.

Getting started

enclave login --host enclave.example.com
enclave push ./dist --title "Kanban board"

login prints where to mint a token and reads it with the input masked (* per character, nothing echoed to the terminal). The token needs three scopes — artifacts:read, artifacts:write and shares:write — and is stored per host in ~/.config/enclave/credentials.json with mode 0600. ENCLAVE_TOKEN overrides the file, which is what you want in CI. --token <token> skips the interactive prompt entirely — the other option for CI, or for a terminal that cannot mask input correctly.

push writes .enclave.json next to the directory it published, recording which artifact the directory maps to and which version it last pushed. Commit that file — it holds no secret, and committing it is what lets a second machine or a CI job target the same artifact.

--host accepts a bare authority (enclave.example.com, 127.0.0.1:3000, [::1]:3000) or a full http/https origin, with or without a trailing slash. Only scheme://host[:port] is accepted — a path suffix (e.g. enclave.example.com/settings/tokens) is rejected rather than silently truncated, since that can retarget every authenticated request to the wrong origin. An explicit scheme is always honoured; otherwise enclave uses http for localhost, 127.0.0.1 and [::1], and https everywhere else.

An explicit http:// origin that isn't loopback is refused, since it would send the bearer token in cleartext. Pass --insecure to opt in anyway.

Republishing

A second push against a directory that already has .enclave.json appends a new version to the same artifact. The id and the URL do not change, so a link you already shared keeps working and starts serving the new content:

$ enclave push ./dist
✓ 2 files, 40 KB
✓ updated 3f2a91c4  v2
→ https://enclave.example.com/a/3f2a91c4-2f1e-4a0b-9d43-5c9d0f0a1b2c
  share it:  enclave share create 3f2a91c4 --expires 7d

--new opts out and publishes the directory as a separate artifact instead.

If the server holds a version newer than the one .enclave.json records — someone pushed from another machine — the push is refused before anything uploads:

$ enclave push ./dist
✗ server is at v5, you last pushed v2
  refusing to overwrite a newer version
  re-run with --force to publish anyway

--force drops that guard and publishes regardless, taking the next version number. Under --json the same refusal is {"error":{"code":"VERSION_CONFLICT","details":{"expectedVersionNo":2,"currentVersionNo":5}}} on stderr, with stdout left empty.

title and visibility belong to the artifact, not to a version — a republish leaves both alone. Change them with enclave rename and enclave privacy.

A share link pinned to a version keeps serving that version after a newer one is published.

Publishing from CI, where there is no state file

.enclave.json lives inside the directory you push, which is usually a build output — gitignored, and wiped by the build step that recreates it. A fresh checkout therefore has no state file, and a plain push would create a duplicate artifact at a new address rather than a new version.

--artifact names the target directly, so nothing has to survive the build:

npm run build
enclave push ./dist --artifact 3f2a91c4-2f1e-4a0b-9d43-5c9d0f0a1b2c

With no state file there is no local version to compare against, so that push appends unconditionally — the guard has nothing to guard. When a state file is present and names the same artifact, the guard applies as usual and --force still overrides it. Naming a different artifact than the state file is refused rather than guessed.

A full uuid is used as-is and costs no extra request, so this path needs only artifacts:write. A prefix of eight characters or more is matched against your own artifacts, which additionally needs artifacts:read.

--artifact and --new contradict each other and are rejected as a pair.

Commands

enclave version  [--json]              (also -v, -V, --version)
enclave login    [--host <host>] [--token <token>]
enclave logout   [--host <host>]

enclave push     <dir> [--title <t>] [--visibility private|org|public]
                       [--artifact <id>] [--new] [--force] [--dry-run] [--json]
enclave list     [--limit <n>] [--cursor <c>] [--json]
enclave show     <id> [--json]
enclave rename   <id> <title> [--json]
enclave privacy  <id> private|org|public [--json]
enclave rm       <id> [--json]
enclave restore  <id> [--json]

enclave share create <id> [--version <versionId>] [--expires <7d|2026-08-10T23:59:00+07:00>] [--json]
enclave share list   <id> [--json]
enclave share revoke <shareId>

<id> takes a full artifact uuid or any unambiguous prefix of eight characters or more.

The host resolves from --host, then ENCLAVE_HOST. push also falls back to .enclave.json. The token resolves from --token, then ENCLAVE_TOKEN, then the stored credentials — login included, so it runs unattended in CI.

Flags are scoped to the command that declares them. A flag a command does not take exits 2 rather than being silently discarded — enclave rm <id> --dry-run refuses instead of deleting.

--expires takes a duration (7d, 12h, 2w); a date (2026-08-10) or a date-time (2026-08-10T14:30), both resolved in this machine's local timezone; or an ISO-8601 instant with an explicit zone (2026-08-10T23:59:00+07:00, 2026-08-10T16:59:00Z), taken exactly as given — including fractional seconds of any length and lowercase z. A bare date means local end of that day (23:59:59.999 local), not UTC midnight (breaking vs early scripts that assumed UTC start of day). Anything else is refused. The resolved instant, in both frames (UTC and local date+time), is printed to stderr before the share link is created.

What gets uploaded

The server accepts thirteen file types — html css js mjs json svg png jpg jpeg webp woff2 txt md — with paths matching [a-zA-Z0-9._-/] and no spaces. A bundle is validated as a unit, so one disallowed file rejects the whole upload.

Rather than let that happen, push drops what the server would refuse and prints every file it skipped, with the reason. A typical dist/ loses its sourcemaps and favicon and uploads fine:

$ enclave push ./dist
skipped 3 files:
  app.js.map       unsupported (.map)
  favicon.ico      unsupported (.ico)
  fonts/Inter.ttf  unsupported (.ttf)
✓ 2 files, 40 KB
✓ created 3f2a91c4  v1
→ https://enclave.example.com/a/3f2a91c4-2f1e-4a0b-9d43-5c9d0f0a1b2c
  private — only you can open that link
  share it:  enclave share create 3f2a91c4 --expires 7d
  or open to the instance:  enclave privacy 3f2a91c4 org

Use .enclaveignore (gitignore syntax) to drop more. --dry-run shows the split without uploading anything.

The client's copy of these rules is a convenience so a push does not spend an upload learning a file was never going to be accepted. The server is authoritative and enforces them regardless.

Scripting

Every command that returns an object takes --json, which puts the raw API object on stdout and nothing else — errors and diagnostics always go to stderr, so | jq is safe on every path. login, logout and share revoke have no object to return and reject the flag with exit 2.

| Exit code | Meaning | | --------- | ---------------------------------------------------------------------------- | | 0 | Success | | 1 | Ran, and the answer was no — not found, refused, unreachable, token rejected | | 2 | Malformed invocation; the command never ran |

Not included

enclave token create does not exist, deliberately. The server refuses to let an API token mint another token, so a leaked token cannot outlive its own revocation. Mint tokens in the browser.

Generating an artifact from a prompt, and administering users, invites and the audit log, are browser-only for now.

Licence

Apache-2.0. Source: github.com/datj9/enclave