Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* @openthreads/core
*
* Core abstractions for OpenThreads.
*/

// ---- Data Model Types ----
export type { Channel, CreateChannelInput, Platform } from './types/channel.js';
export type { Recipient, CreateRecipientInput } from './types/recipient.js';
Expand Down Expand Up @@ -63,3 +69,33 @@ export {
hasA2HMessages,
hasChatSDKMessages,
} from './utils/message-classifier.js';

// ---- Token Management (Issue #5) ----
export type {
TokenRecord,
ChannelApiKeyRecord,
TokenValidationResult,
ChannelApiKeyValidationResult,
ThreadRecord,
ThreadKind,
CreateThreadOptions,
CreateVirtualThreadOptions,
TurnRecord,
CreateTurnOptions,
} from './types/index.js';

export {
generateTokenId as generateEphemeralTokenId,
generateChannelApiKeyId,
} from './utils/id.js';

export { InMemoryStorageAdapter } from './storage/in-memory.js';

export { TokenManager, DEFAULT_TOKEN_TTL_MS } from './token/index.js';
export type { TokenManagerOptions, GenerateEphemeralTokenOptions } from './token/index.js';

export { ThreadManager } from './thread/index.js';
export type { ThreadManagerOptions } from './thread/index.js';

export { TurnManager } from './turn/index.js';
export type { TurnManagerOptions } from './turn/index.js';
107 changes: 107 additions & 0 deletions packages/core/src/storage/in-memory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* In-memory StorageAdapter implementation.
*
* Intended for testing and development. Not suitable for production use
* because state is lost on restart and is not shared between processes.
*/

import type {
StorageAdapter,
TokenRecord,
ChannelApiKeyRecord,
ThreadRecord,
TurnRecord,
} from '../types/index.js';

export class InMemoryStorageAdapter implements StorageAdapter {
private readonly tokens = new Map<string, TokenRecord>();
private readonly channelApiKeys = new Map<string, ChannelApiKeyRecord>();
private readonly threads = new Map<string, ThreadRecord>();
private readonly turns = new Map<string, TurnRecord>();

// ------ Token operations -----------------------------------------------

async saveToken(token: TokenRecord): Promise<void> {
this.tokens.set(token.id, { ...token });
}

async getToken(tokenId: string): Promise<TokenRecord | null> {
return this.tokens.get(tokenId) ?? null;
}

async deleteToken(tokenId: string): Promise<void> {
this.tokens.delete(tokenId);
}

// ------ Channel API key operations -------------------------------------

async saveChannelApiKey(key: ChannelApiKeyRecord): Promise<void> {
this.channelApiKeys.set(key.id, { ...key });
}

async getChannelApiKey(keyId: string): Promise<ChannelApiKeyRecord | null> {
return this.channelApiKeys.get(keyId) ?? null;
}

async deleteChannelApiKey(keyId: string): Promise<void> {
this.channelApiKeys.delete(keyId);
}

// ------ Thread operations ----------------------------------------------

async saveThread(thread: ThreadRecord): Promise<void> {
this.threads.set(thread.id, { ...thread });
}

async getThread(threadId: string): Promise<ThreadRecord | null> {
return this.threads.get(threadId) ?? null;
}

async getThreadByNativeId(channelId: string, nativeThreadId: string): Promise<ThreadRecord | null> {
for (const thread of this.threads.values()) {
if (thread.channelId === channelId && thread.nativeThreadId === nativeThreadId) {
return { ...thread };
}
}
return null;
}

async getMainThread(channelId: string, targetId: string): Promise<ThreadRecord | null> {
for (const thread of this.threads.values()) {
if (thread.channelId === channelId && thread.targetId === targetId && thread.kind === 'main') {
return { ...thread };
}
}
return null;
}

async getThreadsByChannelAndTarget(channelId: string, targetId: string): Promise<ThreadRecord[]> {
const results: ThreadRecord[] = [];
for (const thread of this.threads.values()) {
if (thread.channelId === channelId && thread.targetId === targetId) {
results.push({ ...thread });
}
}
return results;
}

// ------ Turn operations ------------------------------------------------

async saveTurn(turn: TurnRecord): Promise<void> {
this.turns.set(turn.id, { ...turn });
}

async getTurn(turnId: string): Promise<TurnRecord | null> {
return this.turns.get(turnId) ?? null;
}

async listTurnsByThread(threadId: string): Promise<TurnRecord[]> {
const results: TurnRecord[] = [];
for (const turn of this.turns.values()) {
if (turn.threadId === threadId) {
results.push({ ...turn });
}
}
return results.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
}
}
Loading
Loading