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

@arbidocs/client

v0.3.19

Published

TypeScript SDK for the ARBI API — zero-knowledge auth, E2E encryption, and type-safe REST client

Downloads

1,265

Readme

@arbidocs/client

TypeScript client for the ARBI API — zero-knowledge auth, E2E encryption, and type-safe REST client.

Install

npm install @arbidocs/client

For Node.js environments (no browser), also install the IndexedDB polyfill:

npm install fake-indexeddb

Quick Start

import { createArbiClient } from '@arbidocs/client'

const arbi = createArbiClient({
  baseUrl: 'https://www.arbidocs.com',
  deploymentDomain: 'www.arbidocs.com',
})

// Login
const session = await arbi.auth.login({
  email: '[email protected]',
  password: 'mypassword',
})

// Type-safe API calls — auto-injects Bearer token
const { data: workspaces } = await arbi.fetch.GET('/api/user/workspaces')
console.log(workspaces) // WorkspaceResponse[]

Examples

Register + Login (Node.js)

// Polyfill IndexedDB for Node.js (must be first import)
require('fake-indexeddb/auto')
const { createArbiClient } = require('@arbidocs/client')

async function main() {
  const arbi = createArbiClient({
    baseUrl: 'https://www.arbidocs.com',
    deploymentDomain: 'www.arbidocs.com',
    credentials: 'omit', // No cookies in Node.js
  })

  // Register a new user
  const { signingPrivateKey } = await arbi.auth.register({
    email: '[email protected]',
    password: 'SecurePass123!',
    verificationCode: '...', // From /api/user/verify-email flow
    firstName: 'Alice',
    lastName: 'Smith',
  })

  // Login
  const session = await arbi.auth.login({
    email: '[email protected]',
    password: 'SecurePass123!',
  })

  console.log('Logged in as', session.userExtId)
  console.log('Token:', session.accessToken.slice(0, 20) + '...')
}

main()

Create a Workspace and Upload a Document

require('fake-indexeddb/auto')
const fs = require('fs')
const {
  createArbiClient,
  getSession,
  sealedBoxDecrypt,
  deriveEncryptionKeypairFromSigning,
  createWorkspaceKeyHeader,
} = require('@arbidocs/client')

async function main() {
  const arbi = createArbiClient({
    baseUrl: 'https://www.arbidocs.com',
    deploymentDomain: 'www.arbidocs.com',
    credentials: 'omit',
  })

  // Login
  const { accessToken, serverSessionKey } = await arbi.auth.login({
    email: '[email protected]',
    password: 'SecurePass123!',
  })

  // Create workspace
  const { data: workspace } = await arbi.fetch.POST('/api/workspace/create_protected', {
    body: {
      name: 'My Case Files',
      description: 'Documents for Case #1234',
      is_public: false,
    },
  })

  // Decrypt the workspace key and generate the auth header.
  // The server wraps the workspace symmetric key with your public key
  // during creation. You unwrap it here to prove you own the workspace.
  const session = await getSession()
  const pubKey = session.signingPrivateKey.slice(32, 64)
  const encKP = deriveEncryptionKeypairFromSigning({
    publicKey: pubKey,
    secretKey: session.signingPrivateKey,
  })
  const workspaceKey = sealedBoxDecrypt(workspace.wrapped_key, encKP.secretKey)
  const wsHeader = await createWorkspaceKeyHeader(workspaceKey, serverSessionKey)

  // Tell the SDK which workspace is active (middleware auto-injects the header)
  arbi.session.setSelectedWorkspace(workspace.external_id)
  arbi.session.setCachedWorkspaceHeader(workspace.external_id, wsHeader)

  // Upload a PDF (multipart — use raw fetch since openapi-fetch
  // doesn't handle multipart/form-data)
  const formData = new FormData()
  const pdf = fs.readFileSync('./contract.pdf')
  formData.append('files', new Blob([pdf], { type: 'application/pdf' }), 'contract.pdf')

  const uploadRes = await fetch(
    `https://www.arbidocs.com/api/document/upload?workspace_ext_id=${workspace.external_id}`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${accessToken}`,
        'workspace-key': wsHeader,
      },
      body: formData,
    }
  )

  const { doc_ext_ids } = await uploadRes.json()
  console.log('Uploaded document:', doc_ext_ids[0])

  // List documents (workspace-key header injected automatically by middleware)
  const { data: docs } = await arbi.fetch.GET(
    '/api/workspace/{workspace_ext_id}/documents',
    { params: { path: { workspace_ext_id: workspace.external_id } } }
  )

  for (const doc of docs) {
    console.log(`  ${doc.file_name} — ${doc.status}`)
  }
}

main()

Subscribe to Session State Changes

The SDK's SessionManager supports a subscribe pattern for reactive updates:

const arbi = createArbiClient({ baseUrl, deploymentDomain })

// React to session changes (useful for custom UI frameworks)
const unsubscribe = arbi.session.subscribe((state) => {
  console.log('Session changed:', {
    loggedIn: !!state.accessToken,
    email: state.userEmail,
    workspace: state.selectedWorkspaceId,
  })
})

// Later: stop listening
unsubscribe()

API Reference

createArbiClient(options)

Creates an SDK client instance.

interface ArbiClientOptions {
  baseUrl: string              // ARBI API URL (e.g. "https://www.arbidocs.com")
  deploymentDomain: string     // Used for deterministic key derivation
  credentials?: RequestCredentials  // Default: 'include'. Use 'omit' for Node.js
  ssoTokenProvider?: { getToken(): Promise<string | null> } | null
  onReloginSuccess?: (data) => void
}

Returns an ArbiClient with these namespaces:

| Namespace | Description | |-----------|-------------| | arbi.fetch | Type-safe openapi-fetch client with auto-auth middleware | | arbi.session | In-memory session state (token, user, workspace) | | arbi.auth | High-level auth: register, login, loginWithKey, logout, changePassword, relogin | | arbi.crypto | Crypto utilities: key derivation, signing, encryption, base64 | | arbi.storage | IndexedDB session persistence: getSession, saveSession |

Auth Methods

// Register
await arbi.auth.register({ email, password, verificationCode, firstName?, lastName? })

// Login with password
await arbi.auth.login({ email, password })

// Login with recovery key (Uint8Array)
await arbi.auth.loginWithKey({ email, signingPrivateKey })

// Change password
await arbi.auth.changePassword({ email, currentPassword, newPassword })

// Logout (clears session + IndexedDB)
await arbi.auth.logout()

// Re-login using stored private key (auto-called by middleware on 401)
await arbi.auth.relogin()

Middleware (Auto-Applied)

The arbi.fetch client has three middleware layers applied automatically:

  1. Bearer Auth — injects Authorization: Bearer <token> on every request
  2. Workspace Key — injects workspace-key header for workspace-scoped endpoints
  3. Auto-Relogin — on 401, re-derives credentials from stored key and retries

Standalone Exports

All building blocks are individually importable for advanced use:

import {
  // Crypto primitives
  initSodium, signMessage, sealedBoxDecrypt, sealedBoxEncrypt,
  deriveEncryptionKeypairFromSigning, createWorkspaceKeyHeader,
  generateUserKeypairs, generateLoginCredentials,
  encryptMessage, decryptMessage,
  base64Encode, base64Decode, base64ToBytes, bytesToBase64,

  // Storage
  getSession, saveSession, clearAllData, hasSession,

  // Middleware (compose your own client)
  createBearerAuthMiddleware, createWorkspaceKeyMiddleware, createAutoReloginMiddleware,

  // Relogin handler
  createReloginHandler,

  // Session manager
  createSessionManager,

  // OpenAPI schema types
  type paths, type components, type operations,
} from '@arbidocs/client'

How It Works

Zero-Knowledge Auth

ARBI uses Ed25519 signatures for authentication. Your password never leaves the client:

  1. Key derivation: Argon2id(email + password + deploymentDomain) → Ed25519 seed → signing keypair
  2. Registration: Public key sent to server, private key stays local
  3. Login: Client signs a timestamp, server verifies the signature
  4. Session: Server returns an encrypted session key for workspace operations

Workspace Encryption

Each workspace has a symmetric key (NaCl SecretBox). The key is:

  • Generated server-side on workspace creation
  • Wrapped with your X25519 public key (derived from your Ed25519 signing key)
  • Unwrapped client-side using sealedBoxDecrypt
  • Used to generate per-request workspace-key headers (HMAC proof of key possession)

IndexedDB Storage

The SDK stores the signing private key and session key in IndexedDB, encrypted with a non-extractable AES-GCM key from the Web Crypto API. In Node.js, use fake-indexeddb as a polyfill.

Requirements

  • Browser: Any modern browser (Chrome, Firefox, Safari, Edge)
  • Node.js: v18+ (needs fetch, FormData, Blob globals). Install fake-indexeddb for IndexedDB.
  • TypeScript: 5.0+ (optional — works with plain JS too)