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

@witniumtech/vault-sdk

v0.9.0

Published

TypeScript SDK for the Witnium Vault API

Readme

Witnium Vault SDK

A TypeScript SDK for interacting with the Witnium Vault API. Provides type-safe access to all API endpoints with cookie-based authentication for seamless browser integration.

Installation

First, configure npm to use GitHub Packages for the @witnium scope. Add to your .npmrc:

@witnium:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN

Then install:

npm install @witnium/vault-sdk

Quick Start

import { WitniumVaultClient } from '@witnium/vault-sdk';

// Create a client
const client = new WitniumVaultClient({
  baseUrl: 'https://witniumvault.witnium.tech'
});

// Login (sets session cookie)
await client.login({
  email: '[email protected]',
  password: 'your-password'
});

// List files
const result = await client.files.list({ page: 1, limit: 20 });
if (result.success) {
  console.log('Files:', result.data.files);
}

Features

  • Type-safe: Full TypeScript support with generated types from OpenAPI spec
  • Cookie-based auth: Seamless browser integration with automatic session management
  • Generic + Convenience methods: Use client.get('/api/files') or client.files.list()
  • Geospatial search: Find files within a radius of any location (GPS coordinates)
  • Visual similarity search: Find images similar to another image, text description, or uploaded photo using CLIP AI
  • Self-documenting: Runtime endpoint registry for AI tools (Lovable, etc.)
  • Error handling: Structured error responses with typed error objects

Authentication

The SDK uses cookie-based authentication, which works automatically in browser environments:

// Login
await client.login({
  email: '[email protected]',
  password: 'password'
});

// Check authentication status
const status = await client.getAuthStatus();
if (status.authenticated) {
  console.log('Logged in as:', status.user?.email);
}

// Logout
await client.logout();

MFA Support

If the user has MFA enabled, provide the TOTP code:

await client.login({
  email: '[email protected]',
  password: 'password',
  totp: '123456' // From authenticator app
});

// Or use a backup code
await client.login({
  email: '[email protected]',
  password: 'password',
  backupCode: 'ABCD-EFGH-1234'
});

API Methods

Generic Methods

The SDK provides generic HTTP methods that work with any endpoint:

// GET request
const result = await client.get('/api/files', { page: '1', limit: '20' });

// POST request (search)
const result = await client.post('/api/search', {
  query: 'contract',
  path: '/documents'
});

// PUT request
const result = await client.put('/api/files', {
  action: 'restore',
  fileId: 'file-123'
});

// DELETE request
const result = await client.delete('/api/files', { id: 'file-123' });

Convenience Methods

For common operations, use the typed convenience methods:

Files

// List files with pagination
const files = await client.files.list({ page: 1, limit: 20 });

// Get a specific file
const file = await client.files.get('file-123');

// Delete a file (soft delete)
await client.files.delete('file-123');

// Permanently delete
await client.files.delete('file-123', true);

// Restore a deleted file
await client.files.restore('file-123');

// Get version history
const history = await client.files.getHistory('file-group-123');

Search

// Simple search
const results = await client.search.query('contract');

// Search with filters
const results = await client.search.query('invoice', {
  fileTypes: ['pdf', 'docx'],
  dateRange: { from: '2024-01-01' },
  folderPath: '/documents',
  limit: 20
});

// Geospatial search - find files within radius of a location
const results = await client.search.query('photo', {
  location: {
    latitude: 59.3293,    // Center point latitude
    longitude: 18.0686,   // Center point longitude
    radiusKm: 10          // Search radius in kilometers
  }
});

// Combine geospatial with other filters
const results = await client.search.query('', {
  fileTypes: ['image/'],
  location: {
    latitude: 40.7128,
    longitude: -74.0060,
    radiusKm: 5
  },
  limit: 50
});

// Visual similarity search - find images similar to another image
const similar = await client.search.findSimilar({ fileId: 'image-123' });
if (similar.success) {
  console.log('Similar images:', similar.data.results);
  similar.data.results.forEach(img => {
    console.log(`${img.filename}: ${(img.similarity * 100).toFixed(1)}% similar`);
  });
}

// Visual similarity search - find images matching a text description
const textResults = await client.search.findSimilar({ text: 'red sports car' });

// Visual similarity search - upload an image to find similar ones
const imageData = btoa(/* raw image bytes */); // Base64 encoded
const uploadResults = await client.search.findSimilar({
  imageData,
  imageMimeType: 'image/jpeg'
});

Evidence Binders

// List binders
const binders = await client.evidenceBinders.list();

// Create a binder
const binder = await client.evidenceBinders.create({
  name: 'Q1 2024 Contracts',
  description: 'All contracts signed in Q1 2024'
});

// Export a binder
const exportJob = await client.evidenceBinders.export('binder-123', 'pdf');

Workspace

// Get audit logs
const logs = await client.workspace.getAuditLogs({
  page: 1,
  limit: 50,
  eventType: 'file.upload'
});

// Get folder tree
const tree = await client.workspace.getFolderTree();

// Get usage statistics
const usage = await client.workspace.getUsage();

Workspaces (Switching Between Workspaces)

Users can have both a personal workspace and access to multiple organization workspaces. The SDK makes it easy to switch between them:

// List all available workspaces
const result = await client.workspaces.list();
if (result.success) {
  for (const ws of result.data.clients) {
    if (ws.isPending) {
      console.log(`Pending invitation: ${ws.name}`);
    } else {
      console.log(`${ws.name} (${ws.role})`);
    }
  }
}

// Switch to a specific workspace
const switchResult = await client.workspaces.switchTo('org-123');
if (switchResult.success) {
  console.log(`Switched to ${switchResult.data.clientName}`);
  console.log(`Role: ${switchResult.data.role}`);
  console.log(`Workspace ID: ${switchResult.data.workspaceId}`);
  
  // All subsequent API calls now use this workspace
  const files = await client.files.list();
}

// Switch to personal workspace
await client.workspaces.switchToPersonal();

// Get current workspace context
const current = await client.workspaces.getCurrent();
if (current.success && current.data.authenticated) {
  console.log(`Current workspace: ${current.data.workspaceId}`);
  console.log(`Is personal: ${current.data.isPersonalWorkspace}`);
}

How it works: When you switch workspaces, the SDK calls an endpoint that regenerates your session token with the new workspace context. The browser automatically stores the updated session cookie, even for cross-origin requests. All subsequent API calls will operate in the context of the new workspace.

Statistics

// Get comprehensive file statistics
const stats = await client.statistics.getFileStats();
if (stats.success) {
  console.log('Total files:', stats.data.overview?.totalFiles);
  console.log('Total storage:', stats.data.overview?.totalStorageFormatted);
}

// Get specific sections with options
const stats = await client.statistics.getFileStats({
  startDate: '2024-01-01',
  endDate: '2024-12-31',
  granularity: 'month',
  sections: ['overview', 'timeline', 'typeDistribution'],
  topN: 5
});

// Convenience methods for common stats
const overview = await client.statistics.getOverview();
const timeline = await client.statistics.getTimeline('month');
const types = await client.statistics.getTypeDistribution(10);
const users = await client.statistics.getUserActivity(10);

Available sections: overview, timeline, typeDistribution, folderDistribution, userActivity, processing, versioning, witness, externalSources, deletions

Digital Witnesses

// Get blockchain info for a witness
const info = await client.witness.getBlockchainInfo('witness-id-hex');
if (info.success) {
  console.log('Confirmations:', info.data.data.confirmations);
  console.log('Block number:', info.data.data.blockNumber);
  console.log('Witnessed data:', info.data.data.witnessedData);

  // Quadruple-layer protection data
  console.log('Data signature:', info.data.data.signature);
  console.log('Witness nonce:', info.data.data.witnessNonce);
  console.log('Witness signature R:', info.data.data.witnessSignatureR);
  console.log('Witness signature S:', info.data.data.witnessSignatureS);
  console.log('Original timestamp:', info.data.data.originalTimestamp);
  console.log('Signing public key:', info.data.data.signingPublicKey);
}

Account (Personal Settings)

// Get subscription status
const status = await client.account.getSubscriptionStatus();
if (status.success) {
  if (status.data.isInTrial) {
    console.log(`Trial: ${status.data.daysRemaining} days left`);
  }
}

// Create checkout session for subscription
const checkout = await client.account.createCheckoutSession({
  tier: 'pro',
  interval: 'yearly'
});
if (checkout.success) {
  window.location.href = checkout.data.url;
}

// Get billing portal URL
const portal = await client.account.getBillingPortalUrl();

// MFA Management
const mfaStatus = await client.account.getMfaStatus();
const setup = await client.account.startMfaSetup();
await client.account.confirmMfaSetup(setupToken, '123456');
await client.account.disableMfa({ totpCode: '123456' });
const codes = await client.account.regenerateBackupCodes();

// Passkey Management
const options = await client.account.startPasskeyRegistration();
await client.account.finishPasskeyRegistration({ credential, friendlyName: 'MacBook' });
await client.account.updatePasskey(credentialId, { friendlyName: 'New Name' });
await client.account.removePasskey(credentialId);

// Profile Management
const profile = await client.account.getProfile();
await client.account.updateProfile({ name: 'New Name' });

// AI Settings (personal workspace)
const aiSettings = await client.account.getAISettings();
await client.account.updateAISettings({ enabled: true });

Admin (Organization Management)

import { Role } from '@witnium/vault-sdk';

// User Management
const users = await client.admin.listUsers();
const user = await client.admin.getUser('user-123');
await client.admin.updateUserRole('user-123', Role.MANAGER);
await client.admin.toggleUserStatus('user-123', false); // disable user
await client.admin.removeUser('user-123');

// Invitations
const invitations = await client.admin.listInvitations();
await client.admin.inviteUser('[email protected]', Role.USER);
await client.admin.resendInvitation('invitation-123');
await client.admin.cancelInvitation('invitation-123');

// Organization AI Settings
const orgAi = await client.admin.getAISettings();
await client.admin.updateAISettings({
  enabled: true,
  chatModel: 'claude-3-opus',
  excludedPaths: ['/sensitive/**']
});

// Subscription
const subscription = await client.admin.getSubscription();
const checkoutUrl = await client.admin.createCheckoutSession('team', 'year');
const portalUrl = await client.admin.getPortalUrl();

// Audit Logs
const logs = await client.admin.getAuditLogs({
  limit: 50,
  action: 'file.upload',
  startDate: '2024-01-01T00:00:00Z'
});

Error Handling

All methods return an ApiResult type that indicates success or failure:

const result = await client.files.get('invalid-id');

if (result.success) {
  console.log('File:', result.data.file);
} else {
  console.error('Error:', result.error.error);
  console.error('Status:', result.error.status);
  console.error('Code:', result.error.code);
}

For authentication errors, you can use the WitniumApiError class:

import { WitniumApiError } from '@witnium/vault-sdk';

try {
  await client.login({ email: '[email protected]', password: 'wrong' });
} catch (error) {
  if (error instanceof WitniumApiError) {
    if (error.isUnauthorized()) {
      console.log('Invalid credentials');
    }
    if (error.code === 'MFA_REQUIRED') {
      console.log('MFA code needed');
    }
  }
}

Endpoint Registry (For AI Tools)

The SDK includes a runtime endpoint registry for AI tools like Lovable:

import { EndpointRegistry } from '@witnium/vault-sdk';

// Get info about an endpoint
const info = EndpointRegistry.getEndpoint('/api/files');
console.log(info?.methods.GET?.description);
// Output: "List files in the current workspace"

// List all endpoints
const endpoints = EndpointRegistry.listEndpoints();

// Find endpoints by tag
const authEndpoints = EndpointRegistry.findByTag('Auth');

// Search endpoints
const fileEndpoints = EndpointRegistry.search('upload');

// Get a summary for AI context
const summary = EndpointRegistry.getSummary();
console.log(summary);
// Output: Markdown-formatted list of all endpoints

Configuration

const client = new WitniumVaultClient({
  // Required: API base URL
  baseUrl: 'https://witniumvault.witnium.tech',
  
  // Optional: Custom timeout (default: 30000ms)
  timeout: 60000,
  
  // Optional: Default headers for all requests
  defaultHeaders: {
    'X-Custom-Header': 'value'
  },
  
  // Optional: Custom fetch implementation (for testing)
  fetch: customFetchFunction
});

TypeScript Types

The SDK exports all types for use in your application:

import type {
  // Client types
  WitniumVaultClientConfig,
  ApiResult,
  ApiError,
  LoginCredentials,

  // API types
  FileMetadata,
  FilesListResponse,
  EvidenceBinder,
  SearchResult,

  // Resource options
  ListFilesOptions,
  SearchOptions,
  CreateBinderOptions,

  // Statistics types
  FileStatsOptions,
  FileStatsResponse,
  OverviewStats,
  TimelineStats,
  TypeDistributionStats,
} from '@witnium/vault-sdk';

Examples

Upload a File with Location

// File uploads use multipart/form-data
// Location is optional and becomes part of the immutable Digital Witness

const formData = new FormData();
formData.append('file', fileObject);
formData.append('folderPath', '/photos/vacation');

// Add location (GeoJSON Point format) - this is immutable once set
// The location becomes part of the blockchain-verified Digital Witness
formData.append('location', JSON.stringify({
  type: 'Point',
  coordinates: [18.0686, 59.3293]  // [longitude, latitude]
}));

// Optional: add custom metadata (also included in Digital Witness)
formData.append('customMetadata', JSON.stringify({
  camera: 'iPhone 15 Pro',
  description: 'Beach sunset'
}));

const response = await fetch('/api/upload', {
  method: 'POST',
  body: formData,
  credentials: 'include'
});

const result = await response.json();
console.log('File uploaded:', result.file._id);
console.log('Location:', result.file.location);  // { type: 'Point', coordinates: [lng, lat] }
console.log('Witness ID:', result.file.digitalWitness.witnessId);

Upload and Process a File

// After upload, trigger AI processing
const result = await client.files.processAI('file-123');
if (result.success) {
  console.log('Processing started');
}

Export Evidence Binder

// Create and export a binder
const binder = await client.evidenceBinders.create({
  name: 'Legal Discovery',
  description: 'Documents for case #12345'
});

if (binder.success) {
  const exportJob = await client.evidenceBinders.export(
    binder.data.binder._id,
    'pdf'
  );
  
  if (exportJob.success) {
    console.log('Export started:', exportJob.data.exportId);
  }
}

React Hooks

The SDK includes React hooks for easier integration. Import from @witnium/vault-sdk/react:

import {
  WitniumClientProvider,
  useWitniumClient,
  useFiles,
  useFile,
  useUpload,
  useDataSources,
} from '@witnium/vault-sdk/react';

WitniumClientProvider

Wrap your app with the provider to use hooks:

function App() {
  return (
    <WitniumClientProvider baseUrl="https://witniumvault.witnium.tech">
      <FileList />
    </WitniumClientProvider>
  );
}

useWitniumClient

Access the SDK client directly:

function MyComponent() {
  const client = useWitniumClient();

  const handleClick = async () => {
    const result = await client.files.list();
    // ...
  };

  return <button onClick={handleClick}>Load Files</button>;
}

useFiles

List files with automatic loading:

function FileList() {
  const { files, loading, error, refetch } = useFiles({
    page: 1,
    limit: 20,
    path: '/documents'
  });

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <ul>
      {files.map(file => (
        <li key={file._id}>{file.name}</li>
      ))}
    </ul>
  );
}

useUpload

Handle file uploads with progress tracking:

function UploadButton() {
  const { upload, uploads, isUploading, clearCompleted } = useUpload({
    onComplete: (file) => console.log('Uploaded:', file.name),
    onError: (error) => console.error('Failed:', error)
  });

  const handleFiles = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files) {
      upload(Array.from(e.target.files), '/documents');
    }
  };

  return (
    <div>
      <input type="file" multiple onChange={handleFiles} />
      {uploads.map(item => (
        <div key={item.id}>
          {item.file.name}: {item.progress}%
        </div>
      ))}
    </div>
  );
}

useDataSources

Manage external data source connections:

function DataSourcesList() {
  const { dataSources, loading, connect, disconnect } = useDataSources();

  return (
    <ul>
      {dataSources.map(ds => (
        <li key={ds.id}>
          {ds.provider}: {ds.status}
        </li>
      ))}
    </ul>
  );
}

Development

# Install dependencies
npm install

# Build the SDK
npm run build

# Generate types from OpenAPI spec
npm run generate-types

# Type check
npm run typecheck

License

MIT