{"_id":"@aletheiadatabase/client","name":"@aletheiadatabase/client","dist-tags":{"latest":"0.2.0"},"versions":{"0.2.0":{"name":"@aletheiadatabase/client","version":"0.2.0","description":"Official TypeScript client SDK for AletheiaDB — typed bi-temporal graph + vector queries over the HTTP API.","license":"MIT OR Apache-2.0","type":"module","sideEffects":false,"publishConfig":{"access":"public"},"engines":{"node":">=18"},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./package.json":"./package.json"},"scripts":{"build":"tsup","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.examples.json","lint":"eslint .","test":"vitest run","test:watch":"vitest","smoke":"node scripts/smoke.mjs && node scripts/smoke.cjs","prepublishOnly":"npm run lint && npm run typecheck && npm run test && npm run build"},"keywords":["aletheiadb","graph-database","bi-temporal","temporal","vector-search","llm","client","sdk"],"devDependencies":{"@eslint/js":"^9.39.5","@types/node":"^20.19.43","eslint":"^9.13.0","tsup":"^8.3.0","typescript":"^5.6.0","typescript-eslint":"^8.11.0","vitest":"^2.1.0"},"repository":{"type":"git","url":"git+https://github.com/autumn-foundation/AletheiaDB.git","directory":"clients/typescript"},"_id":"@aletheiadatabase/client@0.2.0","gitHead":"ed94af010c0a69974e226fe3958d3fab729bf9dd","bugs":{"url":"https://github.com/autumn-foundation/AletheiaDB/issues"},"homepage":"https://github.com/autumn-foundation/AletheiaDB#readme","_nodeVersion":"20.17.0","_npmVersion":"10.8.2","dist":{"integrity":"sha512-yH5gzO2H5yUoZFciisl4D/P0lOQ3Cz2KYdV+CO0tvFWZgPI1jWsUPXy3cR53HNommficYQnhUROht6L/pOvn+g==","shasum":"ad181a213ffe8c9c1de516c538052fc2d87001df","tarball":"https://registry.npmjs.org/@aletheiadatabase/client/-/client-0.2.0.tgz","fileCount":9,"unpackedSize":507869,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIAvC/9UEhV8YOJWHKF2D7h4f6UV5vkGP7h0KL2AdzX3uAiEAvsnO2Z0eJmunVYaTIU+FCdV+Icb+z7epfrJNsaUmb2s="}]},"_npmUser":{"name":"madmax983","email":"markmasterson@gmail.com"},"directories":{},"maintainers":[{"name":"madmax983","email":"markmasterson@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/client_0.2.0_1785440657176_0.8882105876300381"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-30T19:44:17.057Z","0.2.0":"2026-07-30T19:44:17.365Z","modified":"2026-07-30T19:44:17.582Z"},"maintainers":[{"name":"madmax983","email":"markmasterson@gmail.com"}],"description":"Official TypeScript client SDK for AletheiaDB — typed bi-temporal graph + vector queries over the HTTP API.","homepage":"https://github.com/autumn-foundation/AletheiaDB#readme","keywords":["aletheiadb","graph-database","bi-temporal","temporal","vector-search","llm","client","sdk"],"repository":{"type":"git","url":"git+https://github.com/autumn-foundation/AletheiaDB.git","directory":"clients/typescript"},"bugs":{"url":"https://github.com/autumn-foundation/AletheiaDB/issues"},"license":"MIT OR Apache-2.0","readme":"# `@aletheiadb/client`\r\n\r\nThe official **TypeScript SDK** for [AletheiaDB](https://github.com/madmax983/AletheiaDB) — a bi-temporal graph + vector database. Fully typed nodes/edges/properties, ergonomic `asOf()` temporal calls, vector-elision-aware types, and the structured error contract surfaced as a typed error hierarchy with an opt-in retry policy.\r\n\r\n- Runs on **Node ≥ 18** and standard-`fetch` edge runtimes (no Node-only deps in the core).\r\n- **ESM + CJS** dual build with full type declarations.\r\n- **0 `any`** in the published type surface (lint-enforced).\r\n\r\n> **Status:** wraps the full autumn-server HTTP surface — node/edge/traverse/temporal + admin/health **and** the vector/hybrid/query/schema/stats/batch/lineage tools (all 46 tools, Issue #3627). See [Coverage](#coverage) and [`COMPATIBILITY.md`](./COMPATIBILITY.md).\r\n\r\n## Install\r\n\r\n```bash\r\nnpm install @aletheiadb/client\r\n```\r\n\r\n## Quickstart\r\n\r\n```ts\r\nimport { AletheiaClient } from '@aletheiadb/client';\r\n\r\nconst db = new AletheiaClient({\r\n  baseUrl: 'http://localhost:8080',\r\n  apiKey: process.env.ALETHEIA_API_KEY, // omit only against an anonymous-mode server\r\n});\r\n\r\n// Health + size\r\nconsole.log((await db.status()).status); // \"healthy\"\r\n\r\n// Create nodes and an edge\r\nconst alice = await db.createNode({ label: 'Person', properties: { name: 'Alice' } });\r\nconst bob = await db.createNode({ label: 'Person', properties: { name: 'Bob' } });\r\nawait db.createEdge({ sourceId: alice.id, targetId: bob.id, label: 'KNOWS' });\r\n\r\n// Traverse\r\nconst friends = await db.traverse({ startNodeId: alice.id, edgeLabel: 'KNOWS', depth: 2 });\r\nfor (const row of friends.results) console.log(row.node.properties.name);\r\n```\r\n\r\nTime-to-first-query target: **< 5 minutes** from `npm install` given a running server.\r\n\r\n## Bi-temporal `asOf` usage\r\n\r\nAletheiaDB tracks two independent time dimensions on every fact:\r\n\r\n- **valid time** — when the fact was true *in reality* (you control it).\r\n- **transaction time** — when the fact was *recorded* (system-assigned; read-only).\r\n\r\nEvery temporal parameter accepts a `Date`, an ISO 8601 string, **or** a number of epoch-**microseconds** — all coerced to the same wire value (millisecond precision for `Date`/string; supply a number for finer resolution).\r\n\r\n```ts\r\n// \"Who did Alice know on 2024-01-01?\" — a point-in-time traversal.\r\nconst asOf2024 = db.asOf({ validTime: '2024-01-01T00:00:00Z' });\r\nconst knownThen = await asOf2024.traverse({ startNodeId: alice.id, edgeLabel: 'KNOWS' });\r\n\r\n// Each dimension is independent — set one, the other, or both:\r\ndb.asOf({ transactionTime: new Date('2024-06-01') });          // tx-time only\r\ndb.asOf({ validTime: 1704067200000000, transactionTime: '…' }); // both\r\n\r\n// Back-date a write with valid_time:\r\nawait db.createEdge({\r\n  sourceId: alice.id, targetId: bob.id, label: 'KNOWS',\r\n  validTime: new Date('2020-06-01T00:00:00Z'),\r\n});\r\n\r\n// Point-in-time reads:\r\nawait db.getNodeAtTime({ nodeId: alice.id, validTime: '2024-01-01', transactionTime: '2024-06-01' });\r\nawait db.findNodesAtTime({ label: 'Person', propertyKey: 'name', propertyValue: 'Alice', validTime: '2024-01-01' });\r\n```\r\n\r\n## Vector elision (typed)\r\n\r\nBy default, embedding/vector properties come back as a compact **elided descriptor** rather than the raw float array (Issue #3220). The type is a discriminated union, so you cannot misread a descriptor as data:\r\n\r\n```ts\r\nimport { isElidedVector, isFullVector } from '@aletheiadb/client';\r\n\r\nconst node = await db.getNode(id);                       // elided by default\r\nconst emb = node.properties.embedding;\r\nif (isElidedVector(emb)) console.log('dim', emb.dim);     // { type:'vector', dim, elided:true }\r\n\r\nconst full = await db.getNode(id, { includeVectors: true });\r\nif (isFullVector(full.properties.embedding)) { /* number[] */ }\r\n```\r\n\r\n## Errors and retries\r\n\r\nEvery error maps to a typed subclass of `AletheiaError`, carrying `code`, `message`, `retriable`, and structured `details` (Issue #3234):\r\n\r\n```ts\r\nimport { NotFoundError, PermissionDeniedError, ConflictError, AletheiaError } from '@aletheiadb/client';\r\n\r\ntry {\r\n  await db.getNode(999_999);\r\n} catch (err) {\r\n  if (err instanceof NotFoundError) { /* ... */ }\r\n  else if (err instanceof AletheiaError) { console.log(err.code, err.retriable, err.details); }\r\n}\r\n```\r\n\r\n| Code | Class | Retriable |\r\n|------|-------|-----------|\r\n| `NOT_FOUND` | `NotFoundError` | no |\r\n| `INVALID_ARGUMENT` | `InvalidArgumentError` | no |\r\n| `CONSTRAINT_VIOLATION` | `ConstraintViolationError` | no |\r\n| `FAILED_PRECONDITION` | `FailedPreconditionError` | no |\r\n| `CONFLICT` | `ConflictError` | usually |\r\n| `UNAVAILABLE` | `UnavailableError` | yes |\r\n| `INTERNAL` | `InternalError` | no |\r\n| `UNAUTHENTICATED` | `UnauthenticatedError` | no |\r\n| `PERMISSION_DENIED` | `PermissionDeniedError` | no |\r\n| `RESOURCE_EXHAUSTED` | `ResourceExhaustedError` | sometimes |\r\n\r\nAn **unknown** code degrades to the base `AletheiaError` with `retriable === false`.\r\n\r\n### Error envelope\r\n\r\nBoth server surfaces emit the **same** nested envelope (Issue #3234, unified onto HTTP by #3629), so an in-band MCP error (HTTP 200) and a real non-2xx normalize identically:\r\n\r\n```json\r\n{ \"error\": { \"code\": \"NOT_FOUND\", \"message\": \"no such node\", \"retriable\": false, \"details\": {} },\r\n  \"trace_id\": \"0af7651916cd43dd8448eb211c80319c\" }\r\n```\r\n\r\n`trace_id` is a **top-level sibling** of `error` (never nested inside it) and surfaces as `err.traceId`. The legacy flat `{ \"success\": false, \"error\": \"…\", \"code\": \"…\" }` body has been removed server-side; the SDK still parses it, to the identical typed error, purely so a client pinned against a pre-#3629 server keeps working.\r\n\r\n`retriable` is taken verbatim from the server whenever it is stated — and every current server states it, including the deliberate `retriable: false` on a **write-class** timeout (the write may already have committed, so retrying could duplicate it) and on a tenant-quota breach, both of which are HTTP 429. Only when the field is absent — a bare status, a proxy's error page, an older server — does the SDK fall back to a default: `CONFLICT`/`UNAVAILABLE` by code, plus `RESOURCE_EXHAUSTED` **on an HTTP 429 specifically** (the transient-overload status). 413/422 stay non-retriable, and a 429 carrying a caller-fault code stays non-retriable.\r\n\r\nThe SDK does not read response headers, so `Retry-After` is not consulted; backoff is jittered exponential.\r\n\r\nThe built-in retry policy is **off by default**. When enabled it retries **only** `retriable` errors — never a non-retriable code — with bounded attempts and jittered exponential backoff:\r\n\r\n```ts\r\nconst db = new AletheiaClient({\r\n  baseUrl, apiKey,\r\n  retry: { enabled: true, maxAttempts: 3, baseDelayMs: 100, maxDelayMs: 2000 },\r\n});\r\n```\r\n\r\n## Auth & configuration\r\n\r\n```ts\r\nnew AletheiaClient({\r\n  baseUrl: 'http://localhost:8080',\r\n  apiKey: 'aletheia_sk_…',\r\n  authScheme: 'bearer',   // default; or 'x-api-key'\r\n  fetch: customFetch,     // inject a fetch for older Node / tests / polyfills\r\n  headers: { 'x-tenant': 'acme' },\r\n});\r\n```\r\n\r\n## Pagination & completeness\r\n\r\nReads that support it accept `limit`/`offset` (#3226), `useCursor`/`cursor` (#3360), and the token budget `maxResponseTokens`/`maxResponseBytes`/`priorityProperties` (#3353). On the nine budgetable **GET** reads (`getNode`, `listNodes`, `getEdge`, `listEdges`, `traverse`, `getNodeHistory`, `getSchema`, and the two adjacency reads) `priorityProperties` rides as a single comma-joined query param, which the server splits on `,` (#3638); the POST-body reads carry it as a JSON array. An empty array is omitted entirely. Responses surface `count`, `has_more`, `next_offset`, `truncated`, `sampled`, `cursor`, `snapshot_valid_time`/`snapshot_transaction_time`, and `budget`:\r\n\r\n```ts\r\nconst page1 = await db.listNodes({ label: 'Person', useCursor: true });\r\nif (page1.has_more) {\r\n  const page2 = await db.listNodes({ cursor: page1.cursor }); // pass cursor alone\r\n}\r\n```\r\n\r\n## Coverage\r\n\r\n**Wrapped:**\r\n\r\n- **Nodes:** `getNode`, `listNodes`, `countNodes`, `createNode`, `updateNode`, `deleteNode`, `deleteNodeCascade`, `retractNode`, `findNodesAtTime`\r\n- **Edges:** `getEdge`, `listEdges`, `countEdges`, `getOutgoingEdges`, `getIncomingEdges`, `createEdge`, `updateEdge`, `deleteEdge`, `retractEdge`\r\n- **Traversal / temporal:** `traverse`, `getNodeHistory`, `getEdgeHistory`, `getNodeAtTime`, `getEdgeAtTime`, `getNodeAtValidTime`, `getNodeAtTransactionTime`, `getEdgeAtValidTime`, `getEdgeAtTransactionTime`, `diffNodeVersions`, `diffEdgeVersions`, `listChanges`\r\n- **Vector:** `findSimilar`, `enableVectorIndex`, `listVectorIndexes`\r\n- **Hybrid / query:** `hybridQuery`, `query`\r\n- **Schema / stats / extent:** `getSchema`, `databaseStats`, `temporalExtent`\r\n- **Batch / lineage:** `applyBatch`, `lineageUpstream`, `lineageDownstream`\r\n- **Admin / health:** `status`, `createKey`, `listKeys`, `revokeKey`\r\n\r\nAll 46 tools are now live on the autumn HTTP surface (Issue #3627). `NotImplementedError` is retained as an exported type for backward compatibility but is no longer thrown by any client method.\r\n\r\n## Development\r\n\r\n```bash\r\nnpm install\r\nnpm run lint       # eslint (bans `any` in the public surface)\r\nnpm run typecheck  # tsc --noEmit (strict) for src + examples\r\nnpm run test       # vitest (record-replay fetch fixtures)\r\nnpm run build      # tsup -> dist (ESM + CJS + d.ts)\r\nnpm run smoke      # import the built ESM and CJS entry points\r\n```\r\n\r\nThe unit tests use **record-replay fetch fixtures** (an injected mock `fetch` returning canned responses shaped to `tests/parity/inventory.json` and the autumn-server `*_tools.rs` handlers), not a live server binary. Integration against a real server binary is a separate CI job wired as the HTTP routes stabilize.\r\n\r\n## License\r\n\r\nMIT OR Apache-2.0\r\n","readmeFilename":"README.md","_rev":"1-ad86e694760d9f36e308cabf280ca785"}