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

hydrous

v2.0.1

Published

Official JavaScript/TypeScript SDK for the Hydrous platform — auth, records, analytics, and storage

Readme

Hydrous SDK

Official JavaScript / TypeScript SDK for the Hydrous platform.
One package — auth, records, analytics, and storage.

npm install hydrous

Table of Contents

  1. Quick Start
  2. Configuration
  3. Auth
  4. Records
  5. Analytics
  6. Storage
  7. Error Handling
  8. TypeScript Types Reference

1. Quick Start

import { createClient } from 'hydrous';

const hydrous = createClient({
  url:    'https://api.yourapp.hydrous.app',
  apiKey: 'hk_live_…',
});

// Upload a file
const { data, error } = await hydrous.storage.upload(
  'ssk_my_bucket_key',       // ← bucket key always comes first
  file,
  {
    path:       'avatars/alice.jpg',
    onProgress: (p) => console.log(`${p.percent}%`),
  }
);

2. Configuration

import { HydrousClient } from 'hydrous';

const hydrous = new HydrousClient({
  url:     'https://api.yourapp.hydrous.app', // your project URL
  apiKey:  'hk_live_…',                       // your project API key
  timeout: 30_000,                             // optional — ms (default 30s)
});

| Option | Type | Required | Description | |-----------|----------|----------|--------------------------------------| | url | string | ✅ | Your Hydrous project base URL | | apiKey | string | ✅ | Your project API key | | timeout | number | ✗ | Request timeout in ms (default 30000) |


3. Auth

Sign Up

const { data, error } = await hydrous.auth.signUp({
  email:    '[email protected]',
  password: 'supersecret',
  metadata: { plan: 'pro' },   // optional
});

if (data) {
  console.log('New user:', data.user.id);
  console.log('Access token:', data.accessToken);
}

Sign In

const { data, error } = await hydrous.auth.signIn({
  email:    '[email protected]',
  password: 'supersecret',
});

if (data) {
  console.log('Welcome back,', data.user.email);
}

Sign Out

await hydrous.auth.signOut();

Get Current User

const { data: user } = await hydrous.auth.getUser();
console.log(user?.email);

Refresh Session

const { data: session } = await hydrous.auth.refreshSession();

4. Records

Insert

Single record:

const { data, error } = await hydrous.records.insert('users', {
  name:  'Alice',
  email: '[email protected]',
  role:  'admin',
});

Bulk insert (array):

const { data } = await hydrous.records.insert('products', [
  { name: 'Widget A', price: 9.99 },
  { name: 'Widget B', price: 14.99 },
]);
console.log(`Inserted ${data.length} products`);

Select / Query

const { data, count } = await hydrous.records.select('users', {
  where:   { field: 'role', operator: 'eq', value: 'admin' },
  orderBy: { field: 'createdAt', direction: 'desc' },
  limit:   20,
  offset:  0,
  select:  ['id', 'name', 'email'],   // optional column projection
});

Multiple filters:

import { eq, gt, inArray } from 'hydrous';

const { data } = await hydrous.records.select('orders', {
  where: [
    eq('status',  'shipped'),
    gt('total',   100),
    inArray('tag', ['vip', 'priority']),
  ],
});

Get by ID

const { data: user } = await hydrous.records.get('users', 'user_abc123');

Update

const { data } = await hydrous.records.update('users', 'user_abc123', {
  name: 'Alice Smith',
});

Delete

const { error } = await hydrous.records.delete('users', 'user_abc123');

Query Helpers

| Helper | SQL equivalent | |---------------------|------------------------| | eq(field, val) | field = val | | neq(field, val) | field != val | | gt(field, val) | field > val | | lt(field, val) | field < val | | inArray(field, []) | field IN (…) |

import { eq, gt, inArray } from 'hydrous';

5. Analytics

Track an Event

await hydrous.analytics.track({
  event:      'page_view',
  properties: { page: '/home', referrer: 'google.com' },
  userId:     'user_abc123',
  sessionId:  'sess_xyz',
});

Batch Track

More efficient than calling track() in a loop:

await hydrous.analytics.trackBatch([
  { event: 'signup',    userId: 'u1' },
  { event: 'onboarded', userId: 'u1', properties: { step: 'profile' } },
]);

Query Events

const { data, count } = await hydrous.analytics.query({
  event:   'page_view',
  from:    '2024-01-01',
  to:      '2024-01-31',
  limit:   500,
  groupBy: 'properties.page',
});

6. Storage

The storage module handles all file operations. Every method takes a bucket key as its first argument — a string that begins with ssk_.

How Bucket Keys Work

A bucket key (ssk_…) is a scoped credential that grants specific permissions (read / write / delete) to a bucket. You create them in the Hydrous dashboard.

hydrous.storage.<method>(
  'ssk_your_bucket_key',   // ← first, always a string
  ...args
)

Upload a File

const { data, error } = await hydrous.storage.upload(
  'ssk_my_bucket_key',
  file,                      // File | Blob | Uint8Array | ArrayBuffer
  {
    path:      'avatars/alice.jpg',  // destination path in your bucket
    overwrite: true,                 // replace if exists (default: false)
    onProgress: (progress) => {
      console.log(progress.stage, progress.percent + '%');
    },
  }
);

if (data) {
  console.log('Stored at:', data.path);
  console.log('Space saved:', data.spaceSaved, 'bytes');
}

Upload Raw Text / JSON

No File object needed — pass any string directly.

// Upload a plain text file
await hydrous.storage.uploadText(
  'ssk_my_bucket_key',
  'reports/summary.txt',
  'Monthly report content…',
  { mimeType: 'text/plain' }
);

// Upload JSON
await hydrous.storage.uploadText(
  'ssk_my_bucket_key',
  'data/config.json',
  JSON.stringify({ theme: 'dark', lang: 'en' }),
  { mimeType: 'application/json' }
);

Track Upload Progress

onProgress is called on every progress tick with a rich UploadProgress object.

await hydrous.storage.upload(
  'ssk_my_bucket_key',
  file,
  {
    onProgress: (p) => {
      // p.stage          — 'pending' | 'compressing' | 'uploading' | 'done' | 'error'
      // p.percent        — 0–100 integer
      // p.bytesUploaded  — bytes sent so far
      // p.totalBytes     — total bytes to send
      // p.bytesPerSecond — current speed (null before first tick)
      // p.eta            — estimated seconds remaining (null until speed is known)
      // p.index          — file index (always 0 for single uploads)
      // p.total          — total files (always 1 for single uploads)

      switch (p.stage) {
        case 'pending':
          console.log('Queued');
          break;
        case 'compressing':
          console.log('Compressing…');
          break;
        case 'uploading':
          console.log(`Uploading ${p.percent}% — ${formatSpeed(p.bytesPerSecond)} — ETA ${p.eta}s`);
          break;
        case 'done':
          console.log('✅ Done!', p.result);
          break;
        case 'error':
          console.error('❌ Error:', p.error);
          break;
      }
    },
  }
);

function formatSpeed(bps: number | null): string {
  if (!bps) return '—';
  if (bps > 1_000_000) return `${(bps / 1_000_000).toFixed(1)} MB/s`;
  if (bps > 1_000)     return `${(bps / 1_000).toFixed(0)} KB/s`;
  return `${bps} B/s`;
}

Stage lifecycle:

pending → compressing → uploading → done
                                  ↘ error

Note: In browsers, upload progress (bytes leaving the NIC) is tracked via XMLHttpRequest. The final done stage fires only after the server confirms the write to cloud storage — so 100% means the file is truly saved.


Batch Upload

Upload many files in one request. Progress fires per-file so you can render individual progress bars.

const inputFiles = Array.from(fileInput.files);  // FileList → array

const { data, error } = await hydrous.storage.batchUpload(
  'ssk_my_bucket_key',
  inputFiles,
  {
    prefix:      'uploads/2024/',    // prepended to each filename
    overwrite:   false,
    concurrency: 5,                  // max parallel uploads on the server (1–10)
    onProgress:  (p) => {
      // p.index identifies WHICH file this event is for (0-based)
      console.log(`File ${p.index} (${p.path}): ${p.stage} ${p.percent}%`);
    },
  }
);

console.log('Succeeded:', data.succeeded.length);
console.log('Failed:',    data.failed.length);

Per-file paths override:

await hydrous.storage.batchUpload('ssk_key', files, {
  paths: [
    'documents/report-q1.pdf',
    'documents/report-q2.pdf',
  ],
});

All files are validated upfront before any uploads begin — if your quota would be exceeded the entire batch is rejected cleanly with no partial writes.


Download a File

Returns the file as an ArrayBuffer.

const { data: buffer, error } = await hydrous.storage.download(
  'ssk_my_bucket_key',
  'avatars/alice.jpg'
);

if (buffer) {
  // Browser: display image
  const blob = new Blob([buffer], { type: 'image/jpeg' });
  img.src    = URL.createObjectURL(blob);

  // Node: write to disk
  import { writeFileSync } from 'fs';
  writeFileSync('alice.jpg', Buffer.from(buffer));
}

Batch Download

const { data: files } = await hydrous.storage.batchDownload(
  'ssk_my_bucket_key',
  ['reports/jan.pdf', 'reports/feb.pdf', 'reports/mar.pdf'],
  {
    concurrency: 3,
    autoSave:    true,          // browser: auto-triggers Save dialog per file
    onProgress:  (p) => {
      console.log(`${p.path}: ${p.status}`);   // 'pending' | 'success' | 'error'
    },
  }
);

// files[n].content  — ArrayBuffer
// files[n].mimeType — string
// files[n].path     — original path
// files[n].size     — bytes

List Files & Folders

const { data } = await hydrous.storage.list('ssk_my_bucket_key', {
  prefix: 'avatars/',   // list inside a folder — omit for root
  limit:  50,           // max items per page (1–100)
});

for (const item of data.items) {
  if (item.type === 'folder') {
    console.log('📁', item.path);
  } else {
    console.log('📄', item.path, item.size, 'bytes');
  }
}

// Paginate
if (data.pagination.hasNextPage) {
  const page2 = await hydrous.storage.list('ssk_my_bucket_key', {
    prefix: 'avatars/',
    cursor: data.pagination.nextCursor,
  });
}

File Metadata

const { data: meta } = await hydrous.storage.metadata(
  'ssk_my_bucket_key',
  'avatars/alice.jpg'
);

console.log(meta.size);          // stored bytes
console.log(meta.originalSize);  // bytes before compression
console.log(meta.mimeType);
console.log(meta.isCompressed);
console.log(meta.updatedAt);

Delete a File

const { error } = await hydrous.storage.deleteFile(
  'ssk_my_bucket_key',
  'avatars/old-photo.jpg'
);

Delete a Folder

Recursively deletes the folder and everything inside it.

await hydrous.storage.deleteFolder('ssk_my_bucket_key', 'temp/');

Create a Folder

await hydrous.storage.createFolder('ssk_my_bucket_key', 'avatars/2024/');

Move a File

await hydrous.storage.move(
  'ssk_my_bucket_key',
  'drafts/report.pdf',
  'published/report.pdf'
);

Copy a File

await hydrous.storage.copy(
  'ssk_my_bucket_key',
  'templates/invoice.pdf',
  'invoices/invoice-001.pdf'
);

Signed URLs

Generate a time-limited public URL for a private file.

const { data } = await hydrous.storage.signedUrl(
  'ssk_my_bucket_key',
  'contracts/agreement.pdf',
  { expiresIn: 300 }   // 5 minutes (default: 3600 = 1 hour)
);

console.log(data.signedUrl);   // share this — expires at data.expiresAt

Bucket Stats

const { data: stats } = await hydrous.storage.stats('ssk_my_bucket_key');

console.log('Files:',         stats.totalFiles);
console.log('Stored:',        stats.totalSizeBytes,   'bytes');
console.log('Space saved:',   stats.spaceSavedBytes,  'bytes');
console.log('Credits used:',  stats.creditsTotalUsed);
console.log('Compression:',   stats.compressionRatio);

7. Error Handling

Every method returns { data, error }. error is null on success.

const { data, error } = await hydrous.storage.upload('ssk_key', file);

if (error) {
  console.error(error.message);  // human-readable message
  console.error(error.code);     // machine-readable code e.g. 'QUOTA_EXCEEDED'
  console.error(error.status);   // HTTP status code (if applicable)
}

Catching thrown errors

If you prefer try/catch, import isHydrousError:

import { isHydrousError } from 'hydrous';

try {
  // ...
} catch (err) {
  if (isHydrousError(err)) {
    console.error(err.code, err.message);
  }
}

Common error codes

| Code | Meaning | |---------------------|----------------------------------------------| | HTTP_ERROR | Non-2xx HTTP response from the server | | NETWORK_ERROR | Failed to reach the server | | QUOTA_EXCEEDED | Storage quota has been reached | | FILE_TOO_LARGE | File exceeds the per-file size limit (50 MB) | | INVALID_MIME | MIME type not permitted | | FILE_EXISTS | File already exists and overwrite is false | | UPLOAD_ABORTED | Upload was cancelled by the client | | UPLOAD_TIMEOUT | Upload timed out | | NO_SESSION | Auth operation requires a session | | UNKNOWN_ERROR | An unexpected error occurred |


8. TypeScript Types Reference

UploadProgress

interface UploadProgress {
  index:          number;          // file index (0-based)
  total:          number;          // total files in the operation
  path:           string;          // destination path
  stage:          UploadStage;     // see below
  bytesUploaded:  number;
  totalBytes:     number;
  percent:        number;          // 0–100
  bytesPerSecond: number | null;   // null before first tick
  eta:            number | null;   // seconds remaining, null until speed known
  result?:        UploadResult;    // set when stage === 'done'
  error?:         string;          // set when stage === 'error'
  code?:          string;
}

type UploadStage = 'pending' | 'compressing' | 'uploading' | 'done' | 'error';

UploadResult

interface UploadResult {
  path:         string;
  compressed:   boolean;
  originalSize: number;
  storedSize:   number;
  spaceSaved:   number;
  mimeType:     string;
}

StorageItem (from list())

interface StorageItem {
  name:         string;
  path:         string;
  type:         'file' | 'folder';
  size?:        number | null;
  mimeType?:    string | null;
  isCompressed?: boolean;
  updatedAt?:   string | null;
}

StorageStats

interface StorageStats {
  totalFiles:             number;
  totalSizeBytes:         number;
  totalOriginalSizeBytes: number;
  spaceSavedBytes:        number;
  uploadsCount:           number;
  downloadsCount:         number;
  creditsUsedUpload:      number;
  creditsUsedDownload:    number;
  creditsTotalUsed:       number;
  compressionRatio:       string;  // e.g. "34.2%"
}

License

MIT © Hydrous