diff options
Diffstat (limited to 'packages/sdk/src/supabase-store.ts')
| -rw-r--r-- | packages/sdk/src/supabase-store.ts | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/packages/sdk/src/supabase-store.ts b/packages/sdk/src/supabase-store.ts new file mode 100644 index 0000000..acb9364 --- /dev/null +++ b/packages/sdk/src/supabase-store.ts @@ -0,0 +1,167 @@ +import type { SupabaseClient } from "@supabase/supabase-js"; +import type { MemoryNotFoundError, MemoryStore } from "./memory-store.js"; +import { failure, type Result, success } from "./result.js"; +import type { + Memory, + MemoryCreateInput, + MemoryFilter, + MemoryUpdateInput, + Tag, +} from "./types.js"; + +type MemoryRow = { + id: string; + user_id: string; + project_id: string; + folder_id: string | null; + content: string; + tags: Tag[]; + metadata: Record<string, unknown>; + embedding: unknown; + created_at: string; + updated_at: string; +}; + +function memoryNotFoundError(memoryId: string): MemoryNotFoundError { + return { type: "MEMORY_NOT_FOUND", memoryId }; +} + +function rowToMemory(row: MemoryRow): Memory { + return { + id: row.id, + content: row.content, + projectId: row.project_id, + folderId: row.folder_id, + tags: row.tags ?? [], + metadata: row.metadata ?? {}, + createdAt: new Date(row.created_at), + updatedAt: new Date(row.updated_at), + }; +} + +export class SupabaseStore implements MemoryStore { + private client: SupabaseClient; + private userId: string; + + constructor(client: SupabaseClient, userId: string) { + this.client = client; + this.userId = userId; + } + + async create(input: MemoryCreateInput): Promise<Memory> { + const { data, error } = await this.client + .from("memories") + .insert({ + user_id: this.userId, + project_id: input.projectId, + folder_id: input.folderId ?? null, + content: input.content, + tags: input.tags ?? [], + metadata: input.metadata ?? {}, + }) + .select() + .single(); + + if (error) { + throw new Error(`Failed to create memory: ${error.message}`); + } + + return rowToMemory(data as MemoryRow); + } + + async read(id: string): Promise<Result<Memory, MemoryNotFoundError>> { + const { data, error } = await this.client + .from("memories") + .select() + .eq("id", id) + .eq("user_id", this.userId) + .single(); + + if (error || !data) { + return failure(memoryNotFoundError(id)); + } + + return success(rowToMemory(data as MemoryRow)); + } + + async update( + id: string, + input: MemoryUpdateInput, + ): Promise<Result<Memory, MemoryNotFoundError>> { + const updates: Record<string, unknown> = {}; + + if (input.content !== undefined) { + updates.content = input.content; + } + + if (input.folderId !== undefined) { + updates.folder_id = input.folderId; + } + + if (input.tags !== undefined) { + updates.tags = input.tags; + } + + if (input.metadata !== undefined) { + updates.metadata = input.metadata; + } + + const { data, error } = await this.client + .from("memories") + .update(updates) + .eq("id", id) + .eq("user_id", this.userId) + .select() + .single(); + + if (error || !data) { + return failure(memoryNotFoundError(id)); + } + + return success(rowToMemory(data as MemoryRow)); + } + + async delete(id: string): Promise<Result<void, MemoryNotFoundError>> { + const { error, count } = await this.client + .from("memories") + .delete({ count: "exact" }) + .eq("id", id) + .eq("user_id", this.userId); + + if (error || count === 0) { + return failure(memoryNotFoundError(id)); + } + + return success(undefined); + } + + async list(filter?: MemoryFilter): Promise<Memory[]> { + let query = this.client + .from("memories") + .select() + .eq("user_id", this.userId); + + if (filter?.projectId) { + query = query.eq("project_id", filter.projectId); + } + + if (filter?.folderId) { + query = query.eq("folder_id", filter.folderId); + } + + if (filter?.tags && filter.tags.length > 0) { + query = query.contains( + "tags", + filter.tags.map((id) => ({ id })), + ); + } + + const { data, error } = await query; + + if (error) { + throw new Error(`Failed to list memories: ${error.message}`); + } + + return (data as MemoryRow[]).map(rowToMemory); + } +} |