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

@hydradb/sdk

v0.0.3

Published

The official TypeScript SDK for the Hydra DB platform.

Readme

Hydra DB TypeScript SDK

The official TypeScript SDK for the Hydra DB platform.

Hydra DB provides memory, knowledge ingestion, retrieval, graph context, and raw-vector workflows for AI applications.

Hydra DB docs

Features

  • Upload files into a tenant knowledge base
  • Add free-form memories, markdown, and user/assistant conversation memories
  • Verify ingestion status after upload
  • Search indexed knowledge and memories
  • Fetch uploaded source content and graph relations
  • Manage tenants and API keys
  • Store, search, filter, and delete raw embeddings
  • Use passthrough requests for endpoints not yet wrapped by the SDK

Installation

npm install @hydradb/sdk
# or
yarn add @hydradb/sdk
# or
pnpm add @hydradb/sdk

Client setup

import { HydraDBClient } from "@hydradb/sdk";

const client = new HydraDBClient({
  token: process.env.HYDRA_DB_API_KEY,
});

const TENANT_ID = process.env.HYDRA_TENANT_ID ?? "my-company";
const SUB_TENANT_ID = process.env.HYDRA_SUB_TENANT_ID ?? "my-sub-tenant";

The default API base URL is:

https://api.hydradb.com

For local development, pass baseUrl:

const localClient = new HydraDBClient({
  token: process.env.HYDRA_DB_API_KEY,
  baseUrl: "http://localhost:8080",
});

Important tenant and sub-tenant rule

Use the same tenant_id and sub_tenant_id across upload, verify, recall, fetch, and delete calls.

If you upload with a sub_tenant_id and then verify or search without it, you may check a different namespace. That can make statuses look inconsistent, for example queued in one namespace and graph_creation in another.

Tenant management

A tenant is the top-level isolated database. A sub-tenant is an optional isolated collection inside a tenant.

Create a standard tenant

await client.tenant.create({
  tenant_id: TENANT_ID,
});

Create an embeddings tenant

Raw embedding APIs require a tenant created with is_embeddings_tenant: true and a fixed embedding dimension.

await client.tenant.create({
  tenant_id: "my-embeddings-tenant",
  is_embeddings_tenant: true,
  embeddings_dimension: 1536,
});

Create a tenant with metadata schema

await client.tenant.create({
  tenant_id: TENANT_ID,
  tenant_metadata_schema: [
    {
      name: "department",
      data_type: "VARCHAR",
      enable_match: true,
      enable_dense_embedding: false,
      enable_sparse_embedding: false,
    },
  ],
});

List sub-tenant IDs

const subTenants = await client.tenant.getSubTenantIds({
  tenant_id: TENANT_ID,
});

console.log(subTenants.sub_tenant_ids);

List tenant IDs

const tenants = await client.tenant.getTenantIds();
console.log(tenants.tenant_ids);

Check tenant infrastructure status

const infraStatus = await client.tenant.getInfraStatus({
  tenant_id: TENANT_ID,
});

console.log(infraStatus);

Monitor tenant stats

const stats = await client.tenant.monitor({
  tenant_id: TENANT_ID,
});

console.log(stats);

Delete a tenant

Warning: this permanently deletes the tenant and its data.

await client.tenant.deleteTenant({
  tenant_id: TENANT_ID,
});

Upload knowledge

Use client.upload.knowledge() to upload files to the knowledge base.

The SDK sends a multipart request to:

POST /ingestion/upload_knowledge

Upload one file

const uploadResult = await client.upload.knowledge({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  files: [
    {
      path: "./report.pdf",
      filename: "report.pdf",
      contentType: "application/pdf",
    },
  ],
  upsert: true,
});

const sourceId = uploadResult.results?.[0]?.source_id;
console.log("source_id:", sourceId);
console.log("initial_status:", uploadResult.results?.[0]?.status);

Upload multiple files

const uploadResult = await client.upload.knowledge({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  files: [
    {
      path: "./a.pdf",
      filename: "a.pdf",
      contentType: "application/pdf",
    },
    {
      path: "./notes.txt",
      filename: "notes.txt",
      contentType: "text/plain",
    },
  ],
  upsert: true,
});

console.log(uploadResult.results);

You can also pass buffers, blobs, streams, or a file object with metadata:

import { readFileSync } from "node:fs";

await client.upload.knowledge({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  files: [
    {
      data: readFileSync("./report.pdf"),
      filename: "report.pdf",
      contentType: "application/pdf",
    },
  ],
});

Upload with metadata

file_metadata must be a JSON string. The array length should match the files array length.

Supported metadata object fields:

  • file_id: optional custom source ID
  • metadata: tenant-level metadata
  • additional_metadata: document-level metadata
  • relations: forceful relations to other Cortex/Hydra source IDs
const fileMetadata = [
  {
    file_id: "doc_a",
    metadata: { department: "sales" },
    additional_metadata: { author: "Alice" },
  },
  {
    file_id: "doc_b",
    metadata: { department: "marketing" },
    additional_metadata: { author: "Bob" },
    relations: {
      cortex_source_ids: ["doc_a"],
      properties: { relation: "same_upload_batch" },
    },
  },
];

const uploadResult = await client.upload.knowledge({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  files: [
    {
      path: "./a.pdf",
      filename: "a.pdf",
      contentType: "application/pdf",
    },
    {
      path: "./b.pdf",
      filename: "b.pdf",
      contentType: "application/pdf",
    },
  ],
  file_metadata: JSON.stringify(fileMetadata),
  upsert: true,
});

console.log(uploadResult.results);

Do not use old metadata keys such as id, tenant_metadata, or document_metadata inside file_metadata for file upload. The current upload endpoint expects file_id, metadata, and additional_metadata.

Upload app-generated knowledge

You can index app-generated source objects without uploading files by passing app_knowledge as a JSON string.

const appKnowledge = [
  {
    id: "app_source_1",
    title: "CRM Account Note",
    content: "Acme is interested in the enterprise plan.",
    type: "crm_note",
    tenant_metadata: { department: "sales" },
    document_metadata: { source: "crm" },
  },
];

await client.upload.knowledge({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  app_knowledge: JSON.stringify(appKnowledge),
  upsert: true,
});

app_sources still exists in the generated SDK as a deprecated alias, but new code should use app_knowledge.

Verify ingestion status

Upload can return queued immediately. That means the file was accepted by the ingestion pipeline, not that indexing is complete.

Use client.upload.verifyProcessing() with the returned source_id values.

const status = await client.upload.verifyProcessing({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  file_ids: ["source-id-1", "source-id-2"],
});

console.log(status.statuses);

Current processing status values:

queued
processing
graph_creation
completed
success
errored

Meaning:

| Status | Meaning | |---|---| | queued | Upload accepted and waiting for processing | | processing | Parsing, chunking, embedding, or indexing is running | | graph_creation | Vector indexing is done, graph creation is still running | | completed | Ingestion finished | | success | Alias for completed | | errored | Ingestion failed |

Poll until ingestion finishes

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

async function waitForIngestion(fileIds: string[]) {
  while (true) {
    const status = await client.upload.verifyProcessing({
      tenant_id: TENANT_ID,
      sub_tenant_id: SUB_TENANT_ID,
      file_ids: fileIds,
    });

    const statuses = status.statuses;
    console.log(statuses);

    const failed = statuses.find((item) => item.indexing_status === "errored");
    if (failed) {
      throw new Error(failed.error_message ?? `Ingestion failed for ${failed.file_id}`);
    }

    const done = statuses.every((item) =>
      item.indexing_status === "completed" || item.indexing_status === "success"
    );

    if (done) {
      return statuses;
    }

    await sleep(5000);
  }
}

const fileIds = uploadResult.results?.map((item) => item.source_id).filter(Boolean) ?? [];
await waitForIngestion(fileIds);

Add memories

Use client.upload.addMemory() to add text, markdown, or conversation memories.

The SDK sends JSON to:

POST /memories/add_memory

Add plain text memory

await client.upload.addMemory({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  upsert: true,
  memories: [
    {
      source_id: "memory_001",
      text: "User prefers detailed technical explanations.",
      title: "User preference",
      infer: true,
      user_name: "John",
      metadata: { category: "preference" },
      additional_metadata: { source: "chat" },
    },
  ],
});

Add markdown memory

await client.upload.addMemory({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  upsert: true,
  memories: [
    {
      source_id: "meeting_notes_001",
      title: "Meeting Notes",
      text: "# Meeting Notes\n\n- Budget approved\n- Launch planned for Q2",
      is_markdown: true,
      infer: false,
    },
  ],
});

Add user/assistant conversation memory

await client.upload.addMemory({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  upsert: true,
  memories: [
    {
      source_id: "conversation_001",
      user_assistant_pairs: [
        {
          user: "How do I like reports?",
          assistant: "You prefer weekly summary reports with charts.",
        },
      ],
      infer: true,
      user_name: "John",
      custom_instructions: "Extract durable user preferences.",
    },
  ],
});

Delete a memory

await client.upload.deleteMemory({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  memory_id: "memory_001",
});

Search and retrieval

Full recall

Hybrid semantic and keyword retrieval across indexed content.

const results = await client.recall.fullRecall({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  query: "What did the account notes say about Acme?",
  max_results: 10,
  mode: "fast",
  alpha: 0.8,
  recency_bias: 0,
  graph_context: true,
});

console.log(results.chunks);
console.log(results.sources);

alpha can be a number from 0.0 to 1.0, or the string "auto".

Recall preferences

Search user memory and preference data.

const preferences = await client.recall.recallPreferences({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  query: "report format preference",
  max_results: 5,
});

console.log(preferences.chunks);

Boolean recall

Keyword, phrase, and BM25-style search.

const boolResults = await client.recall.booleanRecall({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  query: "enterprise plan",
  operator: "phrase",
  search_mode: "sources",
  max_results: 10,
});

console.log(boolResults.chunks);

Supported operator values:

or
and
phrase

Supported search_mode values:

sources
memories

Fetch and inspect data

List knowledge sources

const sources = await client.fetch.listData({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  kind: "knowledge",
  page: 1,
  page_size: 50,
});

console.log(sources.data);
console.log(sources.pagination);

List memories

const memories = await client.fetch.listData({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  kind: "memories",
  page: 1,
  page_size: 50,
});

console.log(memories.data);

Filter listed data

const filtered = await client.fetch.listData({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  kind: "knowledge",
  filters: {
    tenant_metadata: { department: "sales" },
    document_metadata: { author: "Alice" },
    source_fields: { type: "crm_note" },
  },
});

console.log(filtered.data);

Reduce list payload size

For kind: "knowledge", you can use include_fields to return only selected source fields.

const slimSources = await client.fetch.listData({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  kind: "knowledge",
  include_fields: ["title", "document_metadata", "timestamp"],
});

console.log(slimSources.data);

Fetch source content

const source = await client.fetch.content({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  source_id: "source-id-1",
  mode: "content",
});

console.log(source);

Supported mode values:

content
url
both

Fetch graph relations

const relations = await client.fetch.graphRelationsBySourceId({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  source_id: "source-id-1",
  is_memory: false,
  limit: 10,
});

console.log(relations);

If you omit source_id, the endpoint can return relations across the sub-tenant.

Delete data

Use client.data.delete() to delete one or more source IDs.

await client.data.delete({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  ids: ["source-id-1", "source-id-2"],
});

Graph health

Fetch high-degree graph nodes for a tenant or sub-tenant.

const superNodes = await client.graphHealth.getSuperNodes({
  tenant_id: TENANT_ID,
  sub_tenant_id: SUB_TENANT_ID,
  degree_threshold: 50,
  limit: 20,
});

console.log(superNodes);

Raw embeddings

Raw embedding APIs are for bring-your-own-vector workflows. They require an embeddings tenant.

Insert raw embeddings

await client.embeddings.insert({
  tenant_id: "my-embeddings-tenant",
  sub_tenant_id: SUB_TENANT_ID,
  upsert: true,
  embeddings: [
    {
      source_id: "doc_001",
      metadata: { category: "finance", year: 2026 },
      embeddings: [
        {
          chunk_id: "doc_001_chunk_0",
          embedding: [0.1, 0.2, 0.3],
        },
      ],
    },
  ],
});

The vector length must match the embeddings_dimension used when creating the embeddings tenant.

Search raw embeddings

const rawResults = await client.embeddings.search({
  tenant_id: "my-embeddings-tenant",
  sub_tenant_id: SUB_TENANT_ID,
  query_embedding: [0.1, 0.2, 0.3],
  limit: 10,
});

console.log(rawResults);

Filter raw embeddings

const bySource = await client.embeddings.filter({
  tenant_id: "my-embeddings-tenant",
  sub_tenant_id: SUB_TENANT_ID,
  source_id: "doc_001",
  limit: 50,
});

console.log(bySource);
const byChunks = await client.embeddings.filter({
  tenant_id: "my-embeddings-tenant",
  sub_tenant_id: SUB_TENANT_ID,
  chunk_ids: ["doc_001_chunk_0"],
});

console.log(byChunks);

Delete raw embeddings

await client.embeddings.delete({
  tenant_id: "my-embeddings-tenant",
  sub_tenant_id: SUB_TENANT_ID,
  source_id: "doc_001",
});
await client.embeddings.delete({
  tenant_id: "my-embeddings-tenant",
  sub_tenant_id: SUB_TENANT_ID,
  chunk_ids: ["doc_001_chunk_0"],
});

API key management

This endpoint is meant for an authenticated dashboard/admin context. Do not expose admin credentials in client-side applications.

const apiKey = await client.key.createApiKey({
  owner: "[email protected]",
  scopes: ["ingest", "query"],
  env: "live",
  prefix: "sk",
});

console.log(apiKey.full_api_key);

Platform metrics

const metrics = await client.metricsMetricsGet();
console.log(metrics);

Passthrough fetch

Use client.passthroughFetch() for API endpoints that are not yet wrapped by the generated SDK. It uses the SDK's configured auth headers, base URL, timeout, retry, and fetch settings.

const response = await client.passthroughFetch("/metrics", {
  method: "GET",
});

const data = await response.json();
console.log(data);

Error handling

import { HydraDBError, HydraDBTimeoutError } from "@hydradb/sdk";

try {
  await client.upload.knowledge({
    tenant_id: TENANT_ID,
    sub_tenant_id: SUB_TENANT_ID,
    files: [{ path: "./missing.pdf" }],
  });
} catch (error) {
  if (error instanceof HydraDBTimeoutError) {
    console.error("Request timed out", error);
  } else if (error instanceof HydraDBError) {
    console.error("Hydra DB API error", error.statusCode, error.body);
  } else {
    console.error("Unexpected error", error);
  }
}

SDK method reference

| SDK method | API path | Description | |---|---|---| | client.tenant.create() | POST /tenants/create | Create a standard or embeddings tenant | | client.tenant.getSubTenantIds() | GET /tenants/sub_tenant_ids | List sub-tenant IDs | | client.tenant.getTenantIds() | GET /tenants/tenant_ids | List tenant IDs | | client.tenant.getInfraStatus() | GET /tenants/infra/status | Check tenant infrastructure | | client.tenant.monitor() | GET /tenants/monitor | Get tenant stats | | client.tenant.deleteTenant() | DELETE /tenants/delete | Delete a tenant | | client.upload.knowledge() | POST /ingestion/upload_knowledge | Upload files or app knowledge | | client.upload.verifyProcessing() | POST /ingestion/verify_processing | Check ingestion status | | client.upload.addMemory() | POST /memories/add_memory | Add memories | | client.upload.deleteMemory() | DELETE /memories/delete_memory | Delete one memory | | client.recall.fullRecall() | POST /recall/search | Hybrid semantic and keyword recall | | client.recall.recallPreferences() | POST /recall/preferences | Search preferences/memories | | client.recall.booleanRecall() | POST /recall/boolean | Boolean/BM25 recall | | client.fetch.listData() | POST /fetch/list | List knowledge or memories | | client.fetch.content() | POST /fetch/source | Fetch source content or URL | | client.fetch.graphRelationsBySourceId() | GET /fetch/graph_relations | Fetch graph relations | | client.data.delete() | POST /data/delete | Delete sources | | client.graphHealth.getSuperNodes() | GET /graph_health/super_nodes | Fetch graph super nodes | | client.embeddings.insert() | POST /embeddings/insert_raw_embeddings | Insert raw embeddings | | client.embeddings.search() | POST /embeddings/search_raw_embeddings | Search raw embeddings | | client.embeddings.filter() | POST /embeddings/filter_raw_embeddings | Filter raw embeddings | | client.embeddings.delete() | DELETE /embeddings/delete | Delete raw embeddings | | client.key.createApiKey() | POST /api_keys/create | Create API key | | client.metricsMetricsGet() | GET /metrics | Platform metrics | | client.passthroughFetch() | any path | Authenticated passthrough request |


Links

Support

If you have any questions or need help, reach out at [email protected].