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

@zysec-ai/relata-sdk

v0.2.12

Published

TypeScript SDK for the Relata data engine — ontology-driven, enterprise-grade workloads

Readme

@zysec-ai/relata-sdk — TypeScript SDK

TypeScript client for the Relata data engine — ontology-driven, enterprise-grade workloads. Zero runtime dependencies (native fetch only), typed responses, fluent query builder, a Mem0-style governed memory client, and 12+ typed v1.1 clients that mirror the server's REST surface — all with full transport hardening (RFC 7807 problem+json, X-Request-ID, retry, multi-tenant headers).

Compatible with Node.js 18+, Deno, Bun, and browser environments.

  • Source: sdks/typescript/src/
  • Package: @zysec-ai/relata-sdk
  • Runtime: Node.js 18+, Deno, Bun, browsers (anywhere with native fetch)
  • Runtime deps: zero — uses native fetch, native crypto.randomUUID(), native AbortController
  • Parity: tracks the Python reference SDK (../python/relata/) method-for-method

TypeScript is async-native, so every SDK method returns a Promise. There are no separate Async* classes — that is the idiomatic parity with the Python SDK's sync/async split.

Install

npm install @zysec-ai/relata-sdk
# or
yarn add @zysec-ai/relata-sdk
# or
bun add @zysec-ai/relata-sdk

Quick start

import { createClient } from "@zysec-ai/relata-sdk";

const relata = createClient("http://localhost:9090", {
  bearerToken: process.env.RELATA_TOKEN,   // required when server sets RELATA_BEARER_TOKEN
  defaultPurpose: "analytics",              // required — every query must declare a purpose
  tenant: "org-acme",                       // X-Relata-Tenant-Id (multi-tenant)
  timeoutMs: 15_000,
  maxRetries: 3,                            // retry on 502/503/504 + network errors
});

// Raw SQL query
const result = await relata.query("SELECT * FROM Person LIMIT 10");
console.log(result.rows);

// Typed query
interface Person { id: string; name: string; dob?: string }
const persons = await relata.query<Person>(
  "SELECT id, name, dob FROM Person LIMIT 5",
);
persons.rows[0]?.name; // typed: string | undefined

// Fluent builder
const recent = await relata
  .select("Person")
  .where("name LIKE 'Ahmed%'")
  .asOf("2025-01-01T00:00:00Z")
  .withProvenance()
  .orderBy("name")
  .limit(20)
  .execute<Person>();

Cypher

RelataDB auto-detects Cypher: any query starting with MATCH is translated to SQL before execution. The SDK is language-agnostic — send the string as you would SQL:

const result = await relata.query("MATCH (n:Person {id: 'p1'}) RETURN *");
// → SELECT * FROM Person WHERE id = 'p1'

Supported: MATCH / OPTIONAL MATCH, WHERE, RETURN, UNION / UNION ALL (#378), and CALL traverse.* / CALL gds.* procedures (#377). CREATE / MERGE writes route through the governed write door. See the SQL reference.

Agent memory in three lines

Memory is a Mem0-style surface over the governed /memory/* verbs — purpose + ACL stay on by default.

import { Memory } from "@zysec-ai/relata-sdk";

const m = new Memory("http://localhost:9090", {
  purpose: "agent-notes",
  bearerToken: process.env.RELATA_TOKEN,
});
const memId = await m.add("Alice prefers dark mode");          // store
const hits  = await m.search("ui preferences", { topK: 5 });   // recall (confidence × recency × relevance)
await m.forget(memId);                                          // governed retract, not a hard delete
await m.close();                                                // no-op on native fetch (API symmetry)

createClient(baseUrl, options?)

Factory function. Returns a RelataClient.

const relata = createClient("http://localhost:9090", {
  bearerToken: "...",
  defaultPurpose: "analytics",
  timeoutMs: 30_000,
  tenant: "org-acme",         // X-Relata-Tenant-Id
  actingAs: "user-bob",       // X-Acting-As (delegation)
  delegatedBy: "user-alice",  // X-Delegated-By
  maxRetries: 3,              // retry 502/503/504 + network errors
  retryBackoffMs: 500,        // exponential base (default 500ms)
  headers: {                  // arbitrary caller headers, win over SDK defaults
    "X-Verified-Principal": "proxy-client",
  },
  fetch: customFetch,         // override for testing / observability
});

RelataClient — the main client

| Method | Returns | Description | |---|---|---| | .query<T>(sql, options?) | Promise<QueryResult<T>> | Execute raw SQL. Wire shape normalised (see below). | | .select(type) | QueryBuilder | Begin a fluent query | | .health() | Promise<HealthResponse> | GET /health — liveness | | .status() | Promise<StatusResponse> | GET /status — profile, role, quota | | .stats() | Promise<Stats> | GET /debug/stats — engine counts | | .version() | Promise<VersionInfo> | GET /version — build info | | .ready() | Promise<ReadyReport> | GET /health/ready — 9-condition readiness | | .auditCount() | Promise<AuditCountResponse> | GET /audit/count — entry count + chainValid | | .clusterNodes() | Promise<ClusterNode[]> | GET /cluster/nodes — list cluster members | | .ingestDocument(chunksJsonl, manifestJson) | Promise<IngestDocumentResponse> | POST /ingest/document — datagrep-extractor envelope (returns taskId for polling) | | .ingestDocumentStatus(taskId) | Promise<IngestDocumentTaskStatus> | GET /ingest/document/:task_id — poll an async ingest to completion (#1001) |

Every method is async (TS is async-native). The X-Request-ID header is auto-generated per request via crypto.randomUUID() — pin your own by setting headers: { "X-Request-ID": "..." }.

Purpose (mandatory)

Every query must declare a purpose registered in the tenant's PurposeRegistry (SPECS §5.22.4). The server rejects purposeless queries with HTTP 400 (PurposeError).

// Client-level default
const relata = createClient(url, { defaultPurpose: "analytics" });

// Per-query override
await relata.query("SELECT ...", { purpose: "audit" });

// Via builder
relata.select("Person").purpose("audit").execute();

Common values: "analytics", "operations", "security_incident", "compliance_review", "audit".

Multi-tenant + delegation headers

const relata = createClient(url, {
  bearerToken: token,
  tenant: "org-acme",           // X-Relata-Tenant-Id
  actingAs: "user-bob",         // X-Acting-As
  delegatedBy: "user-alice",    // X-Delegated-By
});

Without a token the SDK runs as the built-in api-user principal — fine for local dev, not for production multi-tenant.

Retry behaviour

maxRetries (default 0 = off) retries on HTTP {502, 503, 504} and on raw network errors (DNS, connection refused). Backoff is exponential: retryBackoffMs * 2^attempt. Timeouts are never retried (operations may have side-effects).

const relata = createClient(url, {
  defaultPurpose: "analytics",
  maxRetries: 3,
  retryBackoffMs: 500,
});

Logging (silent by default)

The SDK never writes to console on its own — pass an explicit logger to get visibility into retry attempts, future deprecation warnings, etc. Two ready-to-use implementations ship in the box:

import { createClient, ConsoleLogger, NoOpLogger, type Logger } from "@zysec-ai/relata-sdk";

// 1. Use the bundled console logger (CLIs / scripts).
const relata = createClient(url, {
  logger: new ConsoleLogger("my-app"),  // → "relata:my-app WARN retrying ..."
  maxRetries: 3,
});

// 2. Stay silent (default — equivalent to omitting `logger`).
const quiet = createClient(url, { logger: new NoOpLogger() });

// 3. Wire your own observability backend.
const custom: Logger = {
  debug: (msg, ctx) => myOtel.span(msg, ctx),
  info:  (msg, ctx) => myOtel.span(msg, ctx),
  warn:  (msg, ctx) => myOtel.warn(msg, ctx),
  error: (msg, ctx) => myOtel.error(msg, ctx),
};

ConsoleLogger writes warn/error to stderr and info/debug to stdout, so JSON-piping callers never see diagnostics on the data stream. Each line is shaped <prefix> <LEVEL> <message> followed by an optional JSON-encoded context object — stable enough for grep but not a substitute for a real structured logger in production.

QueryResult wire-shape normalisation

The server replies with one of two shapes:

| Wire shape | Meaning | |---|---| | {"data": [...rows...], "query_id": "...", "elapsed_ms": N} | Rich (row data in data) | | {"rows": <int count>, "query_id": "...", "elapsed_ms": N} | Legacy (row count in rows) |

The SDK normalises both so callers always see result.rows as an array, matching the Python _normalise_wire_shape validator:

const result = await relata.query("SELECT * FROM Person LIMIT 5");
result.rows;        // Record<string, unknown>[] — always an array
result.rowCount;    // number — always rows.length
result.columns;     // string[] — column names when the server sends them
result.queryId;     // string
result.elapsedMs;   // number

Memory — agent memory

Mem0-style governed agent memory over /memory/* (ADR-144). Construct with a mandatory purpose.

import { Memory } from "@zysec-ai/relata-sdk";

const m = new Memory("http://localhost:9090", {
  purpose: "agent-notes",
  bearerToken: process.env.RELATA_TOKEN,
  sessionId: "sess-1",         // optional default session
});

const id1 = await m.add("Alice prefers dark mode");
const id2 = await m.add("Bob likes light mode", { memoryClass: "episodic", confidence: 0.8 });
const ids = await m.addBatch(["first", "second", { content: "third", confidence: 0.5 }]);

const hits = await m.search("ui preferences", { topK: 5, asOf: "2025-01-01" });
const mem  = await m.get(id1);          // Record<string, unknown> | null
const newId = await m.update(id1, "Alice prefers dark mode (revised)");
const decision = await m.forget(id1);   // governed retention-policy retract

// The five cognitive verbs (#77):
await m.associate(id1, id2, "contradicts", { confidence: 0.9 });
const eps = await m.episodes({ sessionId: "sess-1" });
const chain = await m.justify(id1);
const resolved = await m.resolve(id1, { policy: "highest_confidence" });
const summary = await m.summarise([id1, id2], { summaryContent: "both prefer different modes" });

| Method | HTTP | Description | |---|---|---| | .add(content, opts?) | POST /memory/remember | Store a memory; returns its id | | .addBatch(items, opts?) | POST /memory/remember/batch | Bulk store; returns index-aligned ids | | .search(query, opts?) | GET /memory/recall | Recall ranked by confidence × recency × relevance | | .get(memoryId) | GET /memory/recognize/:id | Fetch one memory or null | | .update(memoryId, content) | POST /memory/consolidate | Governed supersede; returns new id | | .forget(memoryId) | DELETE /memory/forget/:id | Governed retention-policy retract | | .associate(src, tgt, relation, opts?) | POST /memory/associate | Link two memories | | .episodes(opts?) | GET /memory/episodes | List episodes | | .justify(memoryId) | GET /memory/justify/:id | PROV-O assertion chain | | .resolve(memoryId, opts?) | POST /memory/resolve/:id | Resolve a contradiction | | .summarise(sourceIds, opts?) | POST /memory/summarise | Summary belief from sources |

The MCP envelope ({"content": [{"type":"text","text":"<json>"}]}) is unwrapped transparently.

QueryBuilder — fluent SQL

Fluent builder returned by relata.select(type).

const result = await relata
  .select("Person")
  .purpose("analytics")
  .where("name LIKE 'Ahmed%'")
  .asOf("2025-01-01T00:00:00Z")
  .withProvenance()
  .orderBy("name")
  .limit(20)
  .execute<{ id: string; name: string }>();

| Method | Description | |---|---| | .columns(...cols) | Columns to select (* by default) | | .purpose(p) | Override purpose for this query | | .where(condition) | Append AND condition | | .asOf(timestamp) | Bi-temporal AS OF clause | | .withProvenance() | Attach PROV-O metadata | | .orderBy(col, dir?) | Add ORDER BY clause (ASC or DESC) | | .limit(n) | Set LIMIT (positive integer) | | .offset(n) | Set OFFSET (non-negative integer) | | .pathsBetween(a, b, opts?) | Switch to PATHS_BETWEEN sub-builder | | .toSQL() | Return SQL string without executing | | .execute<T>() | Execute and return QueryResult<T> |

PathsQueryBuilder

Returned by .pathsBetween(a, b, opts?). Same .purpose / .limit / .withProvenance / .asOf / .execute<T>() surface.

Typed v1.1 clients — fromClient(client)

Each typed client inherits the parent client's auth, tenant, and header context, so governance stays consistent across the surface. TypeScript is async-native, so every method returns a Promise (no Async* classes).

import { createClient, GovernanceClient, McpClient, AuditClient } from "@zysec-ai/relata-sdk";

const relata = createClient(url, { bearerToken: token, defaultPurpose: "compliance", tenant: "acme" });
const gov    = GovernanceClient.fromClient(relata);
const mcp    = McpClient.fromClient(relata);
const audit  = AuditClient.fromClient(relata);

const rule   = await gov.createRule({ name: "big-xfer", object_type: "Transaction", condition: "amount_usd > 1000000", action: "alert" });
const tools  = await mcp.listTools();
const entries = await audit.entries({ purpose: "compliance", limit: 50 });

| Module | Class | Surface | |---|---|---| | governance.ts | GovernanceClient | Rules, retention (holds + WORM), breakglass, alerts, DSAR | | mcp.ts | McpClient | 22+ typed MCP tool wrappers + generic callTool | | a2a.ts | A2AClient | A2A tasks + LangGraph checkpoints + agent card | | audit.ts | AuditClient | Audit entries (filtered/paginated) + signed receipts + PDF export | | identity.ts | IdentityClient | Identity label/uncertainty + lookup tables + ERASE SUBJECT | | objects.ts | ObjectClient | Typed upsert + batch via /ingest?object_type= | | ingest.ts | IngestClient | Bulk NDJSON + CSV + media status | | vectors.ts | VectorClient | KNN + hybrid search + similar-to (SQL-backed) | | s3.ts | S3Client | Native-fetch wrapper for the S3 protocol door (no boto3) | | system.ts | SystemClient | LLM config + test + jobs status | | streaming.ts | StreamingClient | NDJSON row streams + SSE consumers (watch/alerts) + Arrow IPC | | tenants.ts | TenantAdminClient | Tenant CRUD + quota + sharing agreements + platform admin | | backup.ts | BackupClient | Backup / restore / PITR (admin) | | tokens.ts | TokenClient | Dedup / uniqueness tokens (test-and-set) | | log.ts | LogClient | Ordered integrity log (append / head / load leaves) |

Streaming — async iterables

StreamingClient exposes every streaming surface as an AsyncIterable<T>:

import { StreamingClient } from "@zysec-ai/relata-sdk";

const streaming = StreamingClient.fromClient(relata);

// NDJSON row stream
for await (const row of streaming.queryRows("SELECT * FROM Person", { purpose: "analytics" })) {
  console.log(row);
}

// SSE watch
for await (const ev of streaming.watch("SELECT * FROM Person", "analytics")) {
  console.log(ev); // { event: "RowsAppended", ... }
}

// SSE alerts
for await (const alert of streaming.alerts()) {
  console.log(alert);
}

// Raw Arrow IPC byte stream
for await (const chunk of streaming.queryArrowRaw("SELECT * FROM Person", { purpose: "analytics" })) {
  // chunk: Uint8Array — feed to a columnar reader
}

SSE consumers reconnect with exponential backoff until you break out of the for await loop.

S3 door — native fetch only

The Python SDK ships boto3 / aiobotocore / httpx flavours; this TypeScript port ships only the native-fetch equivalent (S3Client.http(method, path, opts)) because boto3 is Python-only and the TS SDK has zero runtime dependencies. Convenience wrappers (listBuckets, createBucket, putObject, getObject, deleteObject) are included.

import { S3Client } from "@zysec-ai/relata-sdk";
const s3 = S3Client.fromClient(relata);
await s3.createBucket("acme-intel");
await s3.putObject("acme-intel", "report.pdf", pdfBytes, { contentType: "application/pdf" });
const obj = await s3.getObject("acme-intel", "report.pdf");
console.log(obj.body); // Uint8Array

Response models

| Model | Fields | |---|---| | QueryResult<T> | rows, queryId, elapsedMs, rowCount, columns | | HealthResponse | status, profile, nodeId | | StatusResponse | profile, role, queryQuota | | AuditCountResponse | entries, chainValid | | ClusterNode | nodeId, role, url | | IngestDocumentResponse | reportId, taskId, chunksIngested, warnings, schemaVersion, queueDepth | | VersionInfo | version, commit, profile, schemaVersion, features | | Stats | records, states, snapshotRows, logLeaves, tokens, raw | | ReadyReport | isReady, status, reason, detail |

Error handling

All errors extend RelataError. Import and catch by type:

import {
  PurposeError, AuthError, QuotaError, RateLimitedError, ForbiddenError,
  NotFoundError, ConflictError, ValidationError,
  BadRequestError, ServerError, NetworkError, TimeoutError,
} from "@zysec-ai/relata-sdk";

try {
  const r = await relata.query("SELECT * FROM Person LIMIT 10");
} catch (err) {
  if (err instanceof PurposeError) {
    console.error("Fix:", err.message);
  } else if (err instanceof RateLimitedError) {
    console.error(`Rate limited. Retry after: ${err.retryAfterSeconds}s`);
  } else if (err instanceof NotFoundError) {
    console.error(`Not found. Code: ${err.code}, requestId: ${err.requestId}`);
  } else if (err instanceof TimeoutError) {
    console.error(`Timed out after ${err.timeoutMs}ms`);
  } else if (err instanceof NetworkError) {
    console.error("Cannot reach server:", err.cause);
  } else {
    throw err;
  }
}

Every error carries the RFC 7807 problem+json fields (code, typeUrl, retryable, requestId) when the server emits them. RateLimitedError extends QuotaError so existing catch (e instanceof QuotaError) callers keep working.

| Error class | HTTP | Cause | |---|---|---| | PurposeError | 400 | Purpose missing or not in registry | | BadRequestError | 400 | SQL syntax error or bad parameter | | AuthError | 401 | Bearer token missing or invalid | | ForbiddenError | 403 | Cedar ACL denies access | | NotFoundError | 404 | Resource does not exist | | ConflictError | 409 | Version or uniqueness conflict | | ValidationError | 422 | Request body failed validation | | RateLimitedError | 429 | Per-principal cost quota or rate limit (extends QuotaError) | | QuotaError | 429 | Back-compat alias for RateLimitedError | | ServerError | 5xx | Server-side error | | NetworkError | 0 | Network failure (DNS, connection refused) | | TimeoutError | 0 | Request exceeded timeoutMs |

Async

TypeScript is async-native — every method returns a Promise. There are no separate Async* classes. Use async/await directly:

const relata = createClient("http://localhost:9090", { defaultPurpose: "analytics" });
const result = await relata.query("SELECT * FROM Person LIMIT 5");
for (const row of result.rows) console.log(row);

CLI helper

npx relata health --url http://localhost:9090
npx relata status
npx relata audit
npx relata nodes
npx relata query "SELECT * FROM Person LIMIT 5" --purpose analytics

Options: --url, --token, --purpose, --timeout, --json.

Exit codes: 0 success · 1 usage / SDK error · 2 audit-chain integrity failure.

Extended SQL dialect

Relata extends ANSI SQL with enterprise operators — usable from raw relata.query(sql) or via the builder:

| Operator | Description | |---|---| | AS OF 'timestamp' | Bi-temporal snapshot query | | WITH PROVENANCE | Attach PROV-O provenance to rows | | PATHS_BETWEEN(a, b, max_hops => 4) | Shortest graph paths | | NETWORK_EXPAND(seed_id => ..., hops => 3) | Network expansion | | MATCH_FACE(image_bytes => ..., threshold => 0.70) | Face recognition | | LOOKUP_IDENTITY(column, value) | IdentityIndex universal lookup | | HYBRID_SCORE(...) | Combined BM25 + vector similarity | | PREGEL_BFS(seed_id => ..., max_rounds => 4) | Iterative graph BFS | | GENERATE_REPORT(type => ..., period => ...) | Signed compliance report | | SIMILAR TO <Type> WHERE id = '...' | Multi-vector similarity | | ERASE SUBJECT '<id>' REASON '<r>' [CERTIFY] | GDPR Art. 17 crypto-shred |

See the SQL reference for full syntax.

Environment variables

| Variable | Default | Description | |---|---|---| | RELATA_TOKEN | — | Bearer token for authentication | | RELATA_PURPOSE | — | Default purpose (CLI helper) | | RELATA_URL | http://localhost:9090 | Server URL (examples / CLI) | | PROBE_IMAGE | — | Path to probe JPEG for face-search example |

Examples

See examples/ for runnable workflows:

| File | What it shows | |---|---| | basic-query.ts | Health check, raw SQL, typed query, fluent builder | | analytics.ts | Full analytics workflow (identity, cases, financials, graph) | | face-search.ts | MATCH_FACE operator with co-occurrence detection | | graph-traversal.ts | PATHS_BETWEEN, NETWORK_EXPAND, MATCH, Pregel BFS | | audit.ts | Audit chain verification, anomaly detection, compliance report |

Run any example:

RELATA_TOKEN=secret node --experimental-strip-types examples/basic-query.ts
# or
deno run --allow-net examples/basic-query.ts
# or
bun run examples/basic-query.ts

Deployment profiles

lite is kept as a silent legacy alias for free (ADR-204).

| Profile | Use case | Start command | |---|---|---| | free | Embedded / single-process / dev | RELATA_PROFILE=free relata serve | | server | Single-node production | RELATA_PROFILE=server relata serve | | cluster | Multi-node distributed (alpha) | RELATA_PROFILE=cluster relata serve |

The SDK works identically across all three.

Testing with an ephemeral server

src/_ephemeral.ts exports spawnEphemeral() — starts a relata serve process on a random port, waits for readiness, and returns { port, token, baseUrl, stop }.

import { spawnEphemeral } from './_ephemeral';

describe('integration', () => {
  let server: Awaited<ReturnType<typeof spawnEphemeral>>;
  beforeAll(async () => { server = await spawnEphemeral(); });
  afterAll(() => server.stop());

  it('health', async () => {
    const r = await fetch(`${server.baseUrl}/health`);
    expect(r.ok).toBe(true);
  });
});

Set RELATA_BIN to the binary path and RELATA_TEST_TOKEN to override the bearer token (defaults: relata / relata-test).

License

AGPL-3.0-only — see the root LICENSE file.