Clone
1
API Reference
Shinwoo PARK edited this page 2026-05-11 15:02:55 +09:00

API Reference

This page documents the public package API exported by identitydb.

It focuses on the surface most application developers actually use:

  • IdentityDB
  • connection config types
  • space, topic, fact, and query methods
  • extractor interfaces and built-in extractors
  • embedding provider interfaces
  • main return types

For a guided walkthrough, start with Getting Started.

Package entry point

import {
  IdentityDB,
  NaiveExtractor,
  LlmFactExtractor,
  type IdentityDBConnectionConfig,
  type EmbeddingProvider,
  type AddFactInput,
  type Topic,
  type Fact,
  type Space,
} from 'identitydb';

The package currently re-exports:

  • database adapter config types from ./adapters
  • IdentityDB from ./core/identity-db
  • schema initialization from ./core/migrations
  • extractor helpers and built-in extractors
  • API, database, and domain types

Connection types

IdentityDB connects through a discriminated client config.

IdentityDBConnectionConfig

type IdentityDBConnectionConfig =
  | SqliteConnectionConfig
  | PostgresConnectionConfig
  | MysqlConnectionConfig;

SqliteConnectionConfig

interface SqliteConnectionConfig {
  client: 'sqlite';
  filename: string;
  readonly?: boolean;
}

PostgresConnectionConfig

interface PostgresConnectionConfig {
  client: 'postgres';
  connectionString?: string;
  host?: string;
  port?: number;
  database?: string;
  user?: string;
  password?: string;
  ssl?: boolean;
}

MysqlConnectionConfig

interface MysqlConnectionConfig {
  client: 'mysql' | 'mariadb';
  uri?: string;
  host?: string;
  port?: number;
  database?: string;
  user?: string;
  password?: string;
}

Example

const db = await IdentityDB.connect({
  client: 'sqlite',
  filename: ':memory:',
});

Core class: IdentityDB

IdentityDB.connect(config)

static connect(config: IdentityDBConnectionConfig): Promise<IdentityDB>

Creates a new database-backed IdentityDB instance.

initialize()

initialize(): Promise<void>

Creates or updates the schema required by IdentityDB.

Current tables:

  • spaces
  • topics
  • facts
  • fact_topics
  • topic_relations
  • topic_aliases
  • fact_embeddings

close()

close(): Promise<void>

Closes the underlying DB connection or pool.


Space management

Spaces are hard-isolated memory graphs. The same topic name can exist independently in multiple spaces.

When spaceName is omitted, IdentityDB uses the default space for backward compatibility.

upsertSpace(input)

upsertSpace(input: UpsertSpaceInput): Promise<Space>

Creates a new space or updates an existing one by normalized name.

UpsertSpaceInput

interface UpsertSpaceInput {
  name: string;
  description?: string | null;
  metadata?: JsonValue | null;
}

getSpaceByName(name)

getSpaceByName(name: string): Promise<Space | null>

Returns a space by name, or null if it does not exist.

listSpaces()

listSpaces(): Promise<Space[]>

Returns all spaces ordered by normalized name.

Space

interface Space {
  id: string;
  name: string;
  normalizedName: string;
  description: string | null;
  metadata: JsonValue | null;
  createdAt: string;
  updatedAt: string;
}

Example

await db.upsertSpace({ name: 'customer-a' });
await db.upsertSpace({ name: 'customer-b' });

const spaces = await db.listSpaces();
const customerA = await db.getSpaceByName('customer-a');

Topics and facts

upsertTopic(input)

upsertTopic(input: UpsertTopicInput): Promise<Topic>

Creates or updates a canonical topic within a space.

UpsertTopicInput

interface UpsertTopicInput {
  spaceName?: string;
  name: string;
  category?: TopicCategory;
  granularity?: TopicGranularity;
  description?: string | null;
  metadata?: JsonValue | null;
}

addFact(input)

addFact(input: AddFactInput): Promise<Fact>

Adds a structured fact and links it to one or more topics.

AddFactInput

interface AddFactInput {
  spaceName?: string;
  statement: string;
  summary?: string | null;
  source?: string | null;
  confidence?: number | null;
  metadata?: JsonValue | null;
  topics: TopicLinkInput[];
}

TopicLinkInput

interface TopicLinkInput {
  spaceName?: string;
  name: string;
  category?: TopicCategory;
  granularity?: TopicGranularity;
  description?: string | null;
  metadata?: JsonValue | null;
  role?: string | null;
}

Example

const fact = await db.addFact({
  spaceName: 'customer-a',
  statement: 'TypeScript is used in the API layer.',
  topics: [
    {
      name: 'TypeScript',
      category: 'entity',
      granularity: 'concrete',
      role: 'object',
    },
    {
      name: 'API layer',
      category: 'concept',
      granularity: 'mixed',
      role: 'context',
    },
  ],
});

Free-form ingestion

Use ingestion when your application starts from plain text instead of pre-structured topic objects.

ingestStatement(statement, options)

ingestStatement(statement: string, options: IngestStatementOptions): Promise<Fact>

Behavior:

  • runs the configured extractor
  • normalizes and deduplicates extracted topics
  • writes the fact into the requested space
  • optionally performs duplicate-aware semantic reuse before inserting
  • optionally indexes the created fact with embeddings after insert

IngestStatementOptions

interface IngestStatementOptions {
  extractor: FactExtractor;
  embeddingProvider?: EmbeddingProvider;
  duplicateThreshold?: number;
  spaceName?: string;
}

Extractor interfaces

FactExtractor

interface FactExtractor {
  extract(input: string): Promise<ExtractedFact>;
}

ExtractedFact

interface ExtractedFact {
  statement?: string;
  summary?: string | null;
  source?: string | null;
  confidence?: number | null;
  metadata?: JsonValue | null;
  topics: TopicLinkInput[];
}

Built-in extractor: NaiveExtractor

class NaiveExtractor implements FactExtractor

Deterministic rule-based extractor that looks for:

  • I
  • 4-digit years like 2025
  • capitalized tokens like TypeScript, Bun, or PostgreSQL

Good for tests, local demos, and environments where you do not want an LLM dependency.

Built-in extractor: LlmFactExtractor

class LlmFactExtractor implements FactExtractor {
  constructor(options: LlmFactExtractorOptions)
}

Wraps any model that can satisfy a very small text-generation contract.

LlmTextGenerationModel

interface LlmTextGenerationModel {
  generateText(prompt: string): Promise<string>;
}

LlmFactExtractorOptions

interface LlmFactExtractorOptions {
  model: LlmTextGenerationModel;
  instructions?: string;
  promptBuilder?: (input: string, instructions?: string) => string;
}

parseLlmExtractedFactResponse(response)

parseLlmExtractedFactResponse(response: string): ExtractedFact

Parses a raw LLM response into an ExtractedFact.

Notes:

  • expects JSON output
  • tolerates fenced json code blocks
  • throws IdentityDBError on invalid JSON or invalid topic payloads

Example

const extractor = new LlmFactExtractor({
  model: {
    async generateText(prompt) {
      return callYourModel(prompt);
    },
  },
  instructions: 'Prefer product, technology, and time topics.',
});

await db.ingestStatement('I have worked with Bun and TypeScript since 2025.', {
  extractor,
  spaceName: 'customer-a',
});

Topic hierarchy and aliases

linkTopics(input)

linkTopics(input: LinkTopicsInput): Promise<void>

Creates a parent_of relation between two topics inside one space.

LinkTopicsInput

interface LinkTopicsInput {
  spaceName?: string;
  parentName: string;
  childName: string;
}

addTopicAlias(canonicalName, alias, options?)

addTopicAlias(canonicalName: string, alias: string, options?: SpaceScopedInput): Promise<void>

Registers an alias for a canonical topic within one space.

resolveTopic(name, options?)

resolveTopic(name: string, options?: SpaceScopedInput): Promise<Topic | null>

Resolves a canonical topic by either its canonical name or one of its aliases.

getTopicAliases(name, options?)

getTopicAliases(name: string, options?: SpaceScopedInput): Promise<string[]>

Lists aliases for the canonical topic identified by name.

getTopicChildren(name, options?)

getTopicChildren(name: string, options?: SpaceScopedInput): Promise<Topic[]>

Returns direct children of a topic.

getTopicParents(name, options?)

getTopicParents(name: string, options?: SpaceScopedInput): Promise<Topic[]>

Returns direct parents of a topic.

getTopicLineage(name, options?)

getTopicLineage(name: string, options?: SpaceScopedInput): Promise<Topic[]>

Returns ancestor topics reachable through repeated parent traversal.

Example

await db.linkTopics({
  spaceName: 'customer-a',
  parentName: 'programming language',
  childName: 'TypeScript',
});

await db.addTopicAlias('TypeScript', 'TS', { spaceName: 'customer-a' });

const topic = await db.resolveTopic('TS', { spaceName: 'customer-a' });
const children = await db.getTopicChildren('programming language', { spaceName: 'customer-a' });
const lineage = await db.getTopicLineage('TypeScript', { spaceName: 'customer-a' });

Topic and fact queries

getTopicFacts(name, options?)

getTopicFacts(name: string, options?: SpaceScopedInput): Promise<Fact[]>

Returns facts linked to a topic.

getTopicFactsLinkedTo(name, linkedTopicName, options?)

getTopicFactsLinkedTo(
  name: string,
  linkedTopicName: string,
  options?: SpaceScopedInput,
): Promise<Fact[]>

Returns facts that connect two topics.

findFactsConnectingTopics(names, options?)

findFactsConnectingTopics(names: string[], options?: SpaceScopedInput): Promise<Fact[]>

Generalized version of getTopicFactsLinkedTo() for any number of topics.

getTopicByName(name, options?)

getTopicByName(name: string, options?: TopicLookupOptions): Promise<Topic | null>
getTopicByName(name: string, options: { includeFacts: true; spaceName?: string }): Promise<TopicWithFacts | null>

Looks up a topic by canonical name or alias.

TopicLookupOptions

interface TopicLookupOptions {
  spaceName?: string;
  includeFacts?: boolean;
}

listTopics(options?)

listTopics(options?: ListTopicsOptions): Promise<Topic[]>
listTopics(options: { includeFacts: true; limit?: number; spaceName?: string }): Promise<TopicWithFacts[]>

Lists topics in one space, optionally hydrating each topic with facts.

ListTopicsOptions

interface ListTopicsOptions {
  spaceName?: string;
  includeFacts?: boolean;
  limit?: number;
}

findConnectedTopics(name, options?)

findConnectedTopics(name: string, options?: SpaceScopedInput): Promise<ConnectedTopic[]>

Returns topics that share facts with the requested topic.


Semantic search and duplicate detection

EmbeddingProvider

interface EmbeddingProvider {
  model: string;
  dimensions: number;
  embed(input: string): Promise<number[]>;
  embedMany?(inputs: string[]): Promise<number[][]>;
}

IdentityDB stays provider-agnostic. You can wrap hosted APIs, local models, or custom services as long as they implement this interface.

indexFactEmbeddings(input)

indexFactEmbeddings(input: IndexFactEmbeddingsInput): Promise<void>

Indexes all facts in a space for one embedding model.

indexFactEmbedding(factId, input)

indexFactEmbedding(factId: string, input: IndexFactEmbeddingsInput): Promise<void>

Indexes one fact by ID.

IndexFactEmbeddingsInput

interface IndexFactEmbeddingsInput {
  spaceName?: string;
  provider: EmbeddingProvider;
}

searchFacts(input)

searchFacts(input: SearchFactsInput): Promise<ScoredFact[]>

Runs semantic retrieval over facts in one space.

SearchFactsInput

interface SearchFactsInput {
  spaceName?: string;
  query: string;
  provider: EmbeddingProvider;
  topicNames?: string[];
  limit?: number;
  minimumScore?: number;
}

findSimilarFacts(input)

findSimilarFacts(input: FindSimilarFactsInput): Promise<ScoredFact[]>

Finds facts similar to a provided statement. This is also used by duplicate-aware ingestion.

FindSimilarFactsInput

interface FindSimilarFactsInput {
  spaceName?: string;
  statement: string;
  provider: EmbeddingProvider;
  topicNames?: string[];
  limit?: number;
  minimumScore?: number;
}

Example

const provider: EmbeddingProvider = {
  model: 'example-embedding-v1',
  dimensions: 3,
  async embed(input) {
    return input.toLowerCase().includes('typescript') ? [1, 0, 0] : [0, 1, 0];
  },
};

await db.indexFactEmbeddings({
  provider,
  spaceName: 'customer-a',
});

const results = await db.searchFacts({
  query: 'TypeScript experience',
  provider,
  limit: 5,
  spaceName: 'customer-a',
});

Main return types

Topic

interface Topic {
  id: string;
  spaceId: string;
  name: string;
  normalizedName: string;
  category: TopicCategory;
  granularity: TopicGranularity;
  description: string | null;
  metadata: JsonValue | null;
  createdAt: string;
  updatedAt: string;
}

FactTopic

interface FactTopic extends Topic {
  role: string | null;
  position: number;
}

Fact

interface Fact {
  id: string;
  spaceId: string;
  statement: string;
  summary: string | null;
  source: string | null;
  confidence: number | null;
  metadata: JsonValue | null;
  createdAt: string;
  updatedAt: string;
  topics: FactTopic[];
}

TopicWithFacts

interface TopicWithFacts extends Topic {
  facts: Fact[];
}

ConnectedTopic

interface ConnectedTopic extends Topic {
  sharedFactCount: number;
}

Domain enums

type TopicCategory = 'entity' | 'concept' | 'temporal' | 'custom';
type TopicGranularity = 'abstract' | 'concrete' | 'mixed';

Errors

The package exposes custom error classes:

  • IdentityDBError
  • IdentityDBConfigurationError

Common cases:

  • empty fact statement
  • no topics on a fact
  • invalid alias collisions
  • invalid extractor output
  • invalid LLM JSON payloads
  • embedding dimension mismatches
  • unsupported connection configuration

Space behavior summary

A practical rule of thumb:

  • Writes like addFact(), upsertTopic(), linkTopics(), and addTopicAlias() can create or use spaces on demand
  • Reads scoped to a missing explicit space usually return null or []
  • when spaceName is omitted, the package falls back to the default space

That means this is valid and isolated:

await db.addFact({
  spaceName: 'A',
  statement: 'TypeScript is preferred by team A.',
  topics: [{ name: 'TypeScript' }],
});

await db.addFact({
  spaceName: 'B',
  statement: 'TypeScript is not used by team B.',
  topics: [{ name: 'TypeScript' }],
});

The two TypeScript topics live in different memory graphs and do not merge automatically.