Deep Agents
AgentContextOrchestratorRetrievalText2SQLToolbox

Storage

Persist conversations with SQLite, PostgreSQL, or SQL Server

The storage system persists conversation history using a graph-based model. Messages are stored as nodes in a DAG (directed acyclic graph), with branches and checkpoints as pointers.

Built-in Stores

File-based persistence using Node.js 22+ native SQLite.

import { ContextEngine, SqliteContextStore } from '@deepagents/context';

const store = new SqliteContextStore('./chat.db');
const context = new ContextEngine({
  store,
  chatId: 'chat-001',
  userId: 'user-001',
});

Requirements:

  • Node.js 22+ (uses node:sqlite module)
  • No additional packages needed

Features:

  • Auto-creates database and schema on first use
  • FTS5 full-text search
  • Single file, easy to backup

Production-ready persistence with PostgreSQL.

import { ContextEngine, PostgresContextStore } from '@deepagents/context';

// Connection string
const store = new PostgresContextStore({
  pool: 'postgresql://user:password@localhost:5432/mydb',
});
await store.initialize();

// Or PoolConfig object
const store = new PostgresContextStore({
  pool: {
    host: 'localhost',
    port: 5432,
    user: 'postgres',
    password: 'secret',
    database: 'mydb',
  },
});
await store.initialize();

// Or pass an existing pg.Pool instance
import pg from 'pg';
const pool = new pg.Pool({ connectionString: 'postgresql://...' });
const store = new PostgresContextStore({ pool });
await store.initialize();

const context = new ContextEngine({
  store,
  chatId: 'chat-001',
  userId: 'user-001',
});

// Close connection when done
await store.close();

Requirements:

  • npm install pg
  • PostgreSQL 12+

Features:

  • Connection pooling via pg.Pool
  • Full-text search with tsvector
  • JSONB metadata support
  • Schema isolation with schema option (defaults to 'public')
  • Production-grade performance

Schema Option

Scope all tables under a specific PostgreSQL schema for multi-tenant isolation:

const store = new PostgresContextStore({
  pool: 'postgresql://user:password@localhost:5432/mydb',
  schema: 'tenant_one',
});
await store.initialize();

The store creates the schema if it does not exist and qualifies all table and index names with the schema prefix.

Initialization

Unlike SQLite, the PostgreSQL store requires explicit initialization after construction. Calling any store method before initialize() throws an error.

const store = new PostgresContextStore({ pool: '...' });
await store.initialize(); // required before any operations

Pool Ownership

When you pass an existing pg.Pool instance, the store does not close the pool on store.close(). When the store creates its own pool (from a connection string or config object), it closes the pool on store.close().

Enterprise persistence with Microsoft SQL Server.

import { ContextEngine, SqlServerContextStore } from '@deepagents/context';

// Connection string
const store = new SqlServerContextStore({
  pool: 'Server=localhost;Database=mydb;User Id=sa;Password=secret;TrustServerCertificate=true',
});

// Or config object
const store = new SqlServerContextStore({
  pool: {
    server: 'localhost',
    database: 'mydb',
    user: 'sa',
    password: 'secret',
    options: {
      trustServerCertificate: true,
    },
  },
});

const context = new ContextEngine({
  store,
  chatId: 'chat-001',
  userId: 'user-001',
});

// Close connection when done
await store.close();

Requirements:

  • npm install mssql
  • SQL Server 2016+

Features:

  • Connection pooling via mssql.ConnectionPool
  • Full-text search (when FTS is installed)
  • Falls back to LIKE search if FTS unavailable
  • Enterprise integration

Ephemeral storage for testing and development.

import { ContextEngine, InMemoryContextStore } from '@deepagents/context';

const store = new InMemoryContextStore();
const context = new ContextEngine({
  store,
  chatId: 'chat-001',
  userId: 'user-001',
});

Behavior:

  • Uses SQLite's in-memory mode (:memory:)
  • Data lost when process exits
  • No file created

Use cases:

  • Unit tests
  • Development/prototyping
  • Stateless serverless functions

Schema

All stores use the same graph-based schema. Select your database to see the exact DDL:

-- Context Store DDL for SQLite
-- This schema implements a DAG-based message history with branching and checkpoints.

-- Performance PRAGMAs (session-level, run on each connection)
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA cache_size = -64000;
PRAGMA temp_store = MEMORY;
PRAGMA mmap_size = 268435456;

-- Integrity
PRAGMA foreign_keys = ON;

-- Chats table
-- createdAt/updatedAt: DEFAULT for insert, inline SET for updates
CREATE TABLE IF NOT EXISTS chats (
  id TEXT PRIMARY KEY,
  userId TEXT NOT NULL,
  title TEXT,
  metadata TEXT,
  createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
  updatedAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)
);

CREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);
CREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);
-- Composite index for listChats(): WHERE userId = ? ORDER BY updatedAt DESC
CREATE INDEX IF NOT EXISTS idx_chats_userId_updatedAt ON chats(userId, updatedAt DESC);

-- Messages table (nodes in the DAG)
CREATE TABLE IF NOT EXISTS messages (
  id TEXT PRIMARY KEY,
  chatId TEXT NOT NULL,
  parentId TEXT,
  name TEXT NOT NULL,
  type TEXT,
  payload TEXT NOT NULL,
  createdAt INTEGER NOT NULL,
  FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
  FOREIGN KEY (parentId) REFERENCES messages(id)
);

CREATE INDEX IF NOT EXISTS idx_messages_chatId ON messages(chatId);
CREATE INDEX IF NOT EXISTS idx_messages_parentId ON messages(parentId);
-- Composite index for recursive CTE parent traversal in getMessageChain()
CREATE INDEX IF NOT EXISTS idx_messages_chatId_parentId ON messages(chatId, parentId);

-- Branches table (pointers to head messages)
CREATE TABLE IF NOT EXISTS branches (
  id TEXT PRIMARY KEY,
  chatId TEXT NOT NULL,
  name TEXT NOT NULL,
  headMessageId TEXT,
  isActive INTEGER NOT NULL DEFAULT 0,
  createdAt INTEGER NOT NULL,
  FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
  FOREIGN KEY (headMessageId) REFERENCES messages(id),
  UNIQUE(chatId, name)
);

CREATE INDEX IF NOT EXISTS idx_branches_chatId ON branches(chatId);
-- Composite index for getActiveBranch(): WHERE chatId = ? AND isActive = 1
CREATE INDEX IF NOT EXISTS idx_branches_chatId_isActive ON branches(chatId, isActive);

-- Checkpoints table (pointers to message nodes)
CREATE TABLE IF NOT EXISTS checkpoints (
  id TEXT PRIMARY KEY,
  chatId TEXT NOT NULL,
  name TEXT NOT NULL,
  messageId TEXT NOT NULL,
  createdAt INTEGER NOT NULL,
  FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
  FOREIGN KEY (messageId) REFERENCES messages(id),
  UNIQUE(chatId, name)
);

CREATE INDEX IF NOT EXISTS idx_checkpoints_chatId ON checkpoints(chatId);

-- FTS5 virtual table for full-text search
-- messageId/chatId/name are UNINDEXED (stored but not searchable, used for filtering/joining)
-- Only 'content' is indexed for full-text search
CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
  messageId UNINDEXED,
  chatId UNINDEXED,
  name UNINDEXED,
  content,
  tokenize='porter unicode61'
);

All table and index names are qualified with the configured schema (defaults to public). The example below uses public as the schema:

CREATE SCHEMA IF NOT EXISTS "public";

CREATE TABLE IF NOT EXISTS "public"."chats" (
  id TEXT PRIMARY KEY,
  userId TEXT NOT NULL,
  title TEXT,
  metadata JSONB,
  createdAt BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT,
  updatedAt BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT
);

CREATE INDEX IF NOT EXISTS "idx_public_chats_updatedAt" ON "public"."chats"(updatedAt);
CREATE INDEX IF NOT EXISTS "idx_public_chats_userId" ON "public"."chats"(userId);
CREATE INDEX IF NOT EXISTS "idx_public_chats_metadata" ON "public"."chats" USING GIN (metadata);

CREATE TABLE IF NOT EXISTS "public"."messages" (
  id TEXT PRIMARY KEY,
  chatId TEXT NOT NULL,
  parentId TEXT,
  name TEXT NOT NULL,
  type TEXT,
  payload JSONB NOT NULL,
  createdAt BIGINT NOT NULL,
  FOREIGN KEY (chatId) REFERENCES "public"."chats"(id) ON DELETE CASCADE,
  FOREIGN KEY (parentId) REFERENCES "public"."messages"(id)
);

CREATE INDEX IF NOT EXISTS "idx_public_messages_chatId" ON "public"."messages"(chatId);
CREATE INDEX IF NOT EXISTS "idx_public_messages_parentId" ON "public"."messages"(parentId);

CREATE TABLE IF NOT EXISTS "public"."branches" (
  id TEXT PRIMARY KEY,
  chatId TEXT NOT NULL,
  name TEXT NOT NULL,
  headMessageId TEXT,
  isActive BOOLEAN NOT NULL DEFAULT FALSE,
  createdAt BIGINT NOT NULL,
  FOREIGN KEY (chatId) REFERENCES "public"."chats"(id) ON DELETE CASCADE,
  FOREIGN KEY (headMessageId) REFERENCES "public"."messages"(id),
  UNIQUE(chatId, name)
);

CREATE INDEX IF NOT EXISTS "idx_public_branches_chatId" ON "public"."branches"(chatId);

CREATE TABLE IF NOT EXISTS "public"."checkpoints" (
  id TEXT PRIMARY KEY,
  chatId TEXT NOT NULL,
  name TEXT NOT NULL,
  messageId TEXT NOT NULL,
  createdAt BIGINT NOT NULL,
  FOREIGN KEY (chatId) REFERENCES "public"."chats"(id) ON DELETE CASCADE,
  FOREIGN KEY (messageId) REFERENCES "public"."messages"(id),
  UNIQUE(chatId, name)
);

CREATE INDEX IF NOT EXISTS "idx_public_checkpoints_chatId" ON "public"."checkpoints"(chatId);

CREATE TABLE IF NOT EXISTS "public"."messages_fts" (
  messageId TEXT PRIMARY KEY REFERENCES "public"."messages"(id) ON DELETE CASCADE,
  chatId TEXT NOT NULL,
  name TEXT NOT NULL,
  content TEXT NOT NULL,
  content_vector TSVECTOR
);

CREATE INDEX IF NOT EXISTS "idx_public_messages_fts_vector" ON "public"."messages_fts" USING GIN(content_vector);
CREATE INDEX IF NOT EXISTS "idx_public_messages_fts_chatId" ON "public"."messages_fts"(chatId);

CREATE OR REPLACE FUNCTION "public"."messages_fts_update_vector"() RETURNS TRIGGER AS $$
BEGIN
  NEW.content_vector := to_tsvector('english', NEW.content);
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS "public_messages_fts_vector_update" ON "public"."messages_fts";
CREATE TRIGGER "public_messages_fts_vector_update"
  BEFORE INSERT OR UPDATE ON "public"."messages_fts"
  FOR EACH ROW
  EXECUTE FUNCTION "public"."messages_fts_update_vector"();

All table and index names are qualified with the configured schema (defaults to dbo). The example below uses dbo as the schema:

IF OBJECT_ID('[dbo].[chats]', 'U') IS NULL
BEGIN
  CREATE TABLE [dbo].[chats] (
    id NVARCHAR(255) PRIMARY KEY,
    userId NVARCHAR(255) NOT NULL,
    title NVARCHAR(MAX),
    metadata NVARCHAR(MAX),
    createdAt BIGINT NOT NULL DEFAULT DATEDIFF_BIG(ms, '1970-01-01', GETUTCDATE()),
    updatedAt BIGINT NOT NULL DEFAULT DATEDIFF_BIG(ms, '1970-01-01', GETUTCDATE())
  );
END;

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_dbo_chats_updatedAt' AND object_id = OBJECT_ID('[dbo].[chats]'))
  CREATE INDEX [idx_dbo_chats_updatedAt] ON [dbo].[chats](updatedAt);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_dbo_chats_userId' AND object_id = OBJECT_ID('[dbo].[chats]'))
  CREATE INDEX [idx_dbo_chats_userId] ON [dbo].[chats](userId);

IF OBJECT_ID('[dbo].[messages]', 'U') IS NULL
BEGIN
  CREATE TABLE [dbo].[messages] (
    id NVARCHAR(255) PRIMARY KEY,
    chatId NVARCHAR(255) NOT NULL,
    parentId NVARCHAR(255),
    name NVARCHAR(255) NOT NULL,
    type NVARCHAR(255),
    payload NVARCHAR(MAX) NOT NULL,
    createdAt BIGINT NOT NULL,
    FOREIGN KEY (chatId) REFERENCES [dbo].[chats](id) ON DELETE CASCADE,
    FOREIGN KEY (parentId) REFERENCES [dbo].[messages](id)
  );
END;

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_dbo_messages_chatId' AND object_id = OBJECT_ID('[dbo].[messages]'))
  CREATE INDEX [idx_dbo_messages_chatId] ON [dbo].[messages](chatId);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_dbo_messages_parentId' AND object_id = OBJECT_ID('[dbo].[messages]'))
  CREATE INDEX [idx_dbo_messages_parentId] ON [dbo].[messages](parentId);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_dbo_messages_chatId_parentId' AND object_id = OBJECT_ID('[dbo].[messages]'))
  CREATE INDEX [idx_dbo_messages_chatId_parentId] ON [dbo].[messages](chatId, parentId);

IF OBJECT_ID('[dbo].[branches]', 'U') IS NULL
BEGIN
  CREATE TABLE [dbo].[branches] (
    id NVARCHAR(255) PRIMARY KEY,
    chatId NVARCHAR(255) NOT NULL,
    name NVARCHAR(255) NOT NULL,
    headMessageId NVARCHAR(255),
    isActive BIT NOT NULL DEFAULT 0,
    createdAt BIGINT NOT NULL,
    FOREIGN KEY (chatId) REFERENCES [dbo].[chats](id) ON DELETE CASCADE,
    FOREIGN KEY (headMessageId) REFERENCES [dbo].[messages](id),
    CONSTRAINT [UQ_dbo_branches_chatId_name] UNIQUE(chatId, name)
  );
END;

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_dbo_branches_chatId' AND object_id = OBJECT_ID('[dbo].[branches]'))
  CREATE INDEX [idx_dbo_branches_chatId] ON [dbo].[branches](chatId);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_dbo_branches_chatId_isActive' AND object_id = OBJECT_ID('[dbo].[branches]'))
  CREATE INDEX [idx_dbo_branches_chatId_isActive] ON [dbo].[branches](chatId, isActive);

IF OBJECT_ID('[dbo].[checkpoints]', 'U') IS NULL
BEGIN
  CREATE TABLE [dbo].[checkpoints] (
    id NVARCHAR(255) PRIMARY KEY,
    chatId NVARCHAR(255) NOT NULL,
    name NVARCHAR(255) NOT NULL,
    messageId NVARCHAR(255) NOT NULL,
    createdAt BIGINT NOT NULL,
    FOREIGN KEY (chatId) REFERENCES [dbo].[chats](id) ON DELETE CASCADE,
    FOREIGN KEY (messageId) REFERENCES [dbo].[messages](id),
    CONSTRAINT [UQ_dbo_checkpoints_chatId_name] UNIQUE(chatId, name)
  );
END;

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_dbo_checkpoints_chatId' AND object_id = OBJECT_ID('[dbo].[checkpoints]'))
  CREATE INDEX [idx_dbo_checkpoints_chatId] ON [dbo].[checkpoints](chatId);

IF OBJECT_ID('[dbo].[messages_fts]', 'U') IS NULL
BEGIN
  CREATE TABLE [dbo].[messages_fts] (
    messageId NVARCHAR(255) NOT NULL,
    chatId NVARCHAR(255) NOT NULL,
    name NVARCHAR(255) NOT NULL,
    content NVARCHAR(MAX) NOT NULL,
    CONSTRAINT [PK_dbo_messages_fts] PRIMARY KEY (messageId),
    FOREIGN KEY (messageId) REFERENCES [dbo].[messages](id) ON DELETE CASCADE
  );
END;

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_dbo_messages_fts_chatId' AND object_id = OBJECT_ID('[dbo].[messages_fts]'))
  CREATE INDEX [idx_dbo_messages_fts_chatId] ON [dbo].[messages_fts](chatId);

GO

IF SERVERPROPERTY('IsFullTextInstalled') = 1
BEGIN
  IF NOT EXISTS (SELECT * FROM sys.fulltext_catalogs WHERE name = 'dbo_context_store_catalog')
    CREATE FULLTEXT CATALOG [dbo_context_store_catalog];

  IF NOT EXISTS (SELECT * FROM sys.fulltext_indexes WHERE object_id = OBJECT_ID('[dbo].[messages_fts]'))
  BEGIN
    CREATE FULLTEXT INDEX ON [dbo].[messages_fts](content)
      KEY INDEX [PK_dbo_messages_fts]
      ON [dbo_context_store_catalog]
      WITH STOPLIST = SYSTEM;
  END;
END;

Prisma schema is portable across providers. Change datasource.provider to match your database:

datasource db {
  provider = "postgresql" // or "sqlite" or "sqlserver"
  url      = env("DATABASE_URL")
}

model Chat {
  id        String   @id
  userId    String
  title     String?
  metadata  Json?
  createdAt BigInt
  updatedAt BigInt

  messages    Message[]
  branches    Branch[]
  checkpoints Checkpoint[]
}

model Message {
  id        String  @id
  chatId    String
  parentId  String?
  name      String
  type      String?
  payload   Json
  createdAt BigInt

  chat     Chat      @relation(fields: [chatId], references: [id], onDelete: Cascade)
  parent   Message?  @relation("MessageParent", fields: [parentId], references: [id])
  children Message[] @relation("MessageParent")
  fts      MessageFts?
}

model Branch {
  id            String  @id
  chatId        String
  name          String
  headMessageId String?
  isActive      Boolean @default(false)
  createdAt     BigInt

  chat Chat @relation(fields: [chatId], references: [id], onDelete: Cascade)

  @@unique([chatId, name])
}

model Checkpoint {
  id        String @id
  chatId    String
  name      String
  messageId String
  createdAt BigInt

  chat Chat @relation(fields: [chatId], references: [id], onDelete: Cascade)

  @@unique([chatId, name])
}

model MessageFts {
  messageId String @id
  chatId    String
  name      String
  content   String

  message Message @relation(fields: [messageId], references: [id], onDelete: Cascade)
}

Type mappings by provider:

Prisma TypeSQLitePostgreSQLSQL Server
StringTEXTTEXTNVARCHAR(255)
BooleanINTEGER (0/1)BOOLEANBIT
JsonTEXTJSONBNVARCHAR(MAX)
BigIntINTEGERBIGINTBIGINT

How Storage Works

The Persistence Flow

// 1. Create context
const context = new ContextEngine({ store, chatId: 'chat-001', userId: 'user-001' });

// 2. Add messages (queued in memory)
context.set(user('Hello!'));
context.set(assistantText('Hi there!'));

// 3. Persist to store
const { headMessageId } = await context.save();
// headMessageId points to the last saved message

// 4. New session loads from store
const context2 = new ContextEngine({ store, chatId: 'chat-001', userId: 'user-001' });
const { messages } = await context2.resolve({ renderer: new XmlRenderer() });
// messages loaded from database

What Gets Persisted

Only message fragments (with type: 'message') are saved:

// Auto-persisted (message fragments set type: 'message')
context.set(user('Hello'));        // ✅ Saved
context.set(assistantText('Hi!')); // ✅ Saved

// Not persisted (context fragments stay in memory)
context.set(role('Be helpful'));   // ❌ Memory only
context.set(hint('Be concise'));   // ❌ Memory only

Store API

All stores implement the ContextStore abstract class. These methods are used internally by ContextEngine, but you can call them directly for advanced use cases.

Chat Operations

// Create or get chat
const chat = await store.upsertChat({
  id: 'chat-001',
  userId: 'user-001',
});

// Update metadata
await store.updateChat('chat-001', {
  title: 'Python Help',
  metadata: { tags: ['python', 'learning'] },
});

// List user's chats
const chats = await store.listChats({
  userId: 'user-001',
  limit: 20,
  offset: 0,
});

// Delete chat (cascades to messages, branches, checkpoints)
await store.deleteChat('chat-001');

Branch Operations

// List branches
const branches = await store.listBranches('chat-001');
// [{ name: 'main', headMessageId: '...', isActive: true, messageCount: 5 }]

// Get active branch
const active = await store.getActiveBranch('chat-001');

Search Operations

// Full-text search messages
const results = await store.searchMessages('chat-001', 'python tutorial', {
  limit: 10,
  roles: ['user', 'assistant'],  // Filter by message type
});

// results: [{ message: {...}, rank: 0.8, snippet: '...python...' }]

Graph Operations

// Get full conversation graph
const graph = await store.getGraph('chat-001');
// {
//   nodes: [{ id, parentId, role, content, createdAt }],
//   branches: [{ name, headMessageId, isActive }],
//   checkpoints: [{ name, messageId }],
// }

Storage Patterns

Multi-Tenant Setup

One database, multiple users. You can isolate tenants with the schema option:

const store = new PostgresContextStore({
  pool: process.env.DATABASE_URL,
  schema: `tenant_${tenantId}`,
});
await store.initialize();

const context = new ContextEngine({
  store,
  chatId: `user-${userId}-chat-${chatId}`,
  userId,
});

// List only this user's chats
const chats = await store.listChats({ userId });

Or use a single schema with user-scoped chat IDs:

const store = new PostgresContextStore({
  pool: process.env.DATABASE_URL,
});
await store.initialize();

const context = new ContextEngine({
  store,
  chatId: `user-${userId}-chat-${chatId}`,
  userId,
});

Per-User SQLite Files

Isolate conversations by user:

function getStoreForUser(userId: string) {
  return new SqliteContextStore(`./var/users/${userId}/context.db`);
}

const store = getStoreForUser('user-123');

Metadata Filtering

Store and query custom metadata:

// Set metadata on chat
await context.updateChat({
  metadata: {
    archived: false,
    tags: ['support', 'billing'],
  },
});

// Query by metadata
const activeCahts = await store.listChats({
  userId: 'user-001',
  metadata: { key: 'archived', value: false },
});

Custom Store Implementation

Implement ContextStore for other databases. The abstract class defines all required methods:

import { ContextStore } from '@deepagents/context';

class RedisContextStore extends ContextStore {
  // Implement all abstract methods:
  // createChat, upsertChat, getChat, updateChat, listChats, deleteChat
  // addMessage, getMessage, getMessageChain, hasChildren, getMessages
  // createBranch, getBranch, getActiveBranch, setActiveBranch, updateBranchHead, listBranches
  // createCheckpoint, getCheckpoint, listCheckpoints, deleteCheckpoint
  // searchMessages, getGraph
}

See the built-in implementations for reference:

  • packages/context/src/lib/store/sqlite.store.ts
  • packages/context/src/lib/store/postgres.store.ts
  • packages/context/src/lib/store/sqlserver.store.ts

Next Steps