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

@aginix/vulcan-js

v0.2.1

Published

Universal client SDK for the @aginix/vulcan family — Supabase-style createClient() with a storage module that speaks to @aginix/adonis-vulcan-storage.

Readme

@aginix/vulcan-js

Universal client SDK for the @aginix/vulcan family. Supabase-style createClient() returns a single client with named sub-modules. Today it ships a storage module that speaks to @aginix/adonis-vulcan-storage.

Install

npm install @aginix/vulcan-js

ESM only, Node 24+, all modern browsers. No runtime dependencies — uses native fetch.

Quick start

import { createClient } from '@aginix/vulcan-js'

const vulcan = createClient('/api')
// or
const vulcan = createClient('http://api.localhost')
// or with auth + extra headers
const vulcan = createClient('http://api.localhost', {
  getToken: async () => session?.accessToken ?? null,
  headers: { 'X-App': 'web' },
})

createClient(baseUrl, options?) returns a VulcanClient. Sub-modules hang off named properties — today vulcan.storage; auth/query/realtime will plug in the same way as they ship.

Result envelope

Every method returns a Supabase-style Promise<{ data, error }>. Inspect error first. Methods never throw on HTTP failures.

const { data, error } = await vulcan.storage.get(id)
if (error) {
  // error: { message, status?, code?, cause? }
  return showError(error)
}
data // typed payload

vulcan.storage

upload(path, file, options?) — full flow

Reserves a path with the server, PUTs the file raw bytes to whatever uploadUrl the server returned, then re-fetches the saved metadata. If the PUT fails the half-created row is best-effort cleaned up server-side so the disk and DB stay in sync.

The SDK PUTs the same way regardless of disk capability — no proxied/signed branching at the call site. The server returns either:

  • an external signed URL (S3 / GCS) — fast direct-to-storage,
  • or a same-origin URL with a server-minted token in the query string (FS driver in dev).

Either way the PUT semantics are identical from the SDK's perspective: raw body, Content-Type from the file, no Authorization header. The URL is self-authenticating.

const { data, error } = await vulcan.storage.upload(
  `users/${userId}/avatar.png`,
  file,
  {
    // all optional
    disk: 'avatars',
    name: 'avatar.png',          // defaults to file.name
    contentType: 'image/png',    // defaults to file.type
    metadata: { uploadedBy: userId },
    signal: abortController.signal,
  }
)
// data: StorageObject  { id, name, disk, path, mimetype, size, createdAt, updatedAt, metadata }

disk is a logical handle that matches a service key in the server's config/drive.ts (e.g. 's3', 'gcs', 'avatars') — not the underlying cloud bucket name. path is the key inside that disk, relative, no prefix.

get(id) — fetch metadata

const { data, error } = await vulcan.storage.get(id)

getDownloadUrl(id, opts?) — download URL

const { data, error } = await vulcan.storage.getDownloadUrl(id, { expiresInSeconds: 300 })
// data: { url, expiresIn }
imgEl.src = data.url

Returns either an external signed URL (S3 / GCS) or a same-origin proxy URL whose query token is the authorization (FS in dev). Either way, the URL is safe to drop into <img src> / window.location — no extra headers needed.

The server clamps expiresInSeconds to whatever the matching security rule + global ceiling allow.

Preview a PDF (or image) in a new tab — pass disposition: 'inline' so the server sends Content-Disposition: inline instead of forcing a download, then open the URL:

const { data, error } = await vulcan.storage.getDownloadUrl(id, {
  disposition: 'inline',
})
if (data) window.open(data.url, '_blank')

The browser still picks behavior from Content-Type: PDFs and images render inline; types the browser can't preview fall back to download. Default disposition is 'attachment' (always downloads), which is what you want for a regular Save / Download button.

remove(id) — delete

const { error } = await vulcan.storage.remove(id)
// data: true

Lower-level building blocks

For custom upload flows (e.g. progress tracking, resumable uploads later) the two underlying steps are exposed:

const { data: reservation, error } = await vulcan.storage.reserveUpload(path, {
  name,
  contentType,
  size,
  disk,
  metadata,
})
// then PUT to reservation.uploadUrl yourself, e.g. using XHR with onprogress

Options

| Option | Type | Notes | |---|---|---| | getToken | () => string \| null \| Promise<string \| null> | Called before each request to set Authorization: Bearer <token>. Return null for anonymous. | | headers | Record<string, string> or () => Record<string, string> | Extra headers applied to every request (e.g. tenant id). | | fetch | typeof fetch | Override the transport. Useful for retries, telemetry, or tests. |

Per-request headers (passed via options) take precedence over the static ones.

Error shape

type VulcanError = {
  message: string
  status?: number   // HTTP status, when known
  code?: string     // server-side error code, e.g. 'E_STORAGE_AUTHORIZATION'
  cause?: unknown   // original Error for network failures
}

Common codes from the storage backend:

  • E_INVALID_STORAGE_PATH (400) — path failed validation
  • E_STORAGE_AUTHORIZATION (403) — security rule denied
  • E_STORAGE_OBJECT_NOT_FOUND (404) — object id doesn't exist

License

Proprietary — see LICENSE.md.