From 24ec3e277dba18fdc3060090b09ea058d78d6b96 Mon Sep 17 00:00:00 2001 From: moon Date: Wed, 20 Mar 2024 16:16:11 -0700 Subject: [PATCH] Add Sqljs adapter --- package-lock.json | 27 +- package.json | 17 +- src/index.ts | 1 + src/lib/adapters/sqlite.ts | 2 +- src/lib/adapters/sqlite/types.ts | 46 +++ src/lib/adapters/sqljs.ts | 559 ++++++++++++++++++++++++++++ src/lib/adapters/supabase.ts | 1 + src/lib/index.ts | 2 + src/test/createRuntime.ts | 24 ++ src/test/getOrCreateRelationship.ts | 66 ++-- 10 files changed, 704 insertions(+), 41 deletions(-) create mode 100644 src/lib/adapters/sqlite/types.ts create mode 100644 src/lib/adapters/sqljs.ts diff --git a/package-lock.json b/package-lock.json index c501e12..7c0e212 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bgent", - "version": "0.1.1", + "version": "0.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bgent", - "version": "0.1.1", + "version": "0.1.2", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -16,6 +16,7 @@ "better-sqlite3": "^9.4.3", "figlet": "^1.7.0", "inquirer": "^9.2.14", + "sql.js": "^1.10.2", "sqlite-vss": "^0.1.2", "unique-names-generator": "^4.7.1" }, @@ -32,6 +33,7 @@ "@types/better-sqlite3": "^7.6.9", "@types/jest": "^27.5.2", "@types/node": "20.9.4", + "@types/sql.js": "^1.4.9", "@typescript-eslint/eslint-plugin": "^7.0.1", "@typescript-eslint/parser": "^7.0.1", "dotenv": "^16.4.4", @@ -2349,6 +2351,12 @@ "@types/node": "*" } }, + "node_modules/@types/emscripten": { + "version": "1.39.10", + "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.10.tgz", + "integrity": "sha512-TB/6hBkYQJxsZHSqyeuO1Jt0AB/bW6G7rHt9g7lML7SOF6lbgcHvw/Lr+69iqN0qxgXLhWKScAon73JNnptuDw==", + "dev": true + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -2438,6 +2446,16 @@ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, + "node_modules/@types/sql.js": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@types/sql.js/-/sql.js-1.4.9.tgz", + "integrity": "sha512-ep8b36RKHlgWPqjNG9ToUrPiwkhwh0AEzy883mO5Xnd+cL6VBH1EvSjBAAuxLUFF2Vn/moE3Me6v9E1Lo+48GQ==", + "dev": true, + "dependencies": { + "@types/emscripten": "*", + "@types/node": "*" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -9218,6 +9236,11 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/sql.js": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.10.2.tgz", + "integrity": "sha512-jnKFtdHxuVUNgu1vHwFoTjjwfTuVDVqzGpw7H05Zq3YMNMDOpLFyFGvpgTRIQGd/mqcYntuMy7iygYCytD62jQ==" + }, "node_modules/sqlite-vss": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/sqlite-vss/-/sqlite-vss-0.1.2.tgz", diff --git a/package.json b/package.json index c637616..f12ce6c 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "bgent", - "version": "0.1.2", + "version": "0.1.3", "private": false, "description": "bgent. because agent was taken.", "type": "module", - "main": "dist/index.cjs.js", - "module": "dist/index.esm.js", - "jsnext:main": "dist/index.esm.js", + "main": "dist/index.cjs", + "module": "dist/index.mjs", + "jsnext:main": "dist/index.mjs", "types": "dist/index.d.ts", "scripts": { "build": "rimraf ./dist && rollup -c && tsc && npm run build:docs", @@ -19,8 +19,7 @@ "shell:cloud": "node --no-warnings scripts/shell.mjs", "test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --runInBand", "test:sqlite": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" TEST_DATABASE_CLIENT=sqlite jest --runInBand", - "test:failed": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest -f --runInBand", - "test:failed:sqlite": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" TEST_DATABASE_CLIENT=sqlite jest -f --runInBand", + "test:sqljs": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" TEST_DATABASE_CLIENT=sqljs jest --runInBand", "test:coverage": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --coverage", "reset-profile": "rimraf ~/.cjrc", "deploy": "wrangler deploy", @@ -30,8 +29,8 @@ "bgent": "./scripts/shell.mjs" }, "exports": { - "import": "./dist/index.esm.js", - "require": "./dist/index.cjs.js" + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" }, "license": "MIT", "devDependencies": { @@ -44,6 +43,7 @@ "@types/better-sqlite3": "^7.6.9", "@types/jest": "^27.5.2", "@types/node": "20.9.4", + "@types/sql.js": "^1.4.9", "@typescript-eslint/eslint-plugin": "^7.0.1", "@typescript-eslint/parser": "^7.0.1", "dotenv": "^16.4.4", @@ -72,6 +72,7 @@ "better-sqlite3": "^9.4.3", "figlet": "^1.7.0", "inquirer": "^9.2.14", + "sql.js": "^1.10.2", "sqlite-vss": "^0.1.2", "unique-names-generator": "^4.7.1" } diff --git a/src/index.ts b/src/index.ts index d4d7fac..6d71637 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,7 @@ export { addHeader, composeContext } from "./lib/context"; export { DatabaseAdapter } from "./lib/database"; export { SupabaseDatabaseAdapter } from "./lib/adapters/supabase"; export { SqliteDatabaseAdapter } from "./lib/adapters/sqlite"; +export { SqlJsDatabaseAdapter } from "./lib/adapters/sqljs"; import wait from "./lib/actions/wait"; export { wait }; diff --git a/src/lib/adapters/sqlite.ts b/src/lib/adapters/sqlite.ts index b01015f..05d1780 100644 --- a/src/lib/adapters/sqlite.ts +++ b/src/lib/adapters/sqlite.ts @@ -10,10 +10,10 @@ import { Account, } from "../types"; -import { Database } from "better-sqlite3"; import { sqliteTables } from "./sqlite/sqliteTables"; import crypto from "crypto"; +import { Database } from "better-sqlite3"; export class SqliteDatabaseAdapter extends DatabaseAdapter { db: Database; diff --git a/src/lib/adapters/sqlite/types.ts b/src/lib/adapters/sqlite/types.ts new file mode 100644 index 0000000..1919d19 --- /dev/null +++ b/src/lib/adapters/sqlite/types.ts @@ -0,0 +1,46 @@ +interface RunResult { + changes: number; + lastInsertRowid: number | bigint; +} +interface Statement { + database: Database; + source: string; + reader: boolean; + readonly: boolean; + busy: boolean; + + run(...params: BindParameters): RunResult; + get(...params: BindParameters): unknown; + all(...params: BindParameters): unknown[]; + iterate(...params: BindParameters): IterableIterator; + pluck(toggleState?: boolean): this; + expand(toggleState?: boolean): this; + raw(toggleState?: boolean): this; + bind(...params: BindParameters): this; + columns(): ColumnDefinition[]; + safeIntegers(toggleState?: boolean): this; +} +interface ColumnDefinition { + name: string; + column: string | null; + table: string | null; + database: string | null; + type: string | null; +} + +export interface Database { + memory: boolean; + readonly: boolean; + name: string; + open: boolean; + inTransaction: boolean; + + // eslint-disable-next-line @typescript-eslint/ban-types + prepare( + source: string, + ): BindParameters extends unknown[] + ? Statement + : Statement<[BindParameters]>; + exec(source: string): this; + [key: string]: unknown; +} diff --git a/src/lib/adapters/sqljs.ts b/src/lib/adapters/sqljs.ts new file mode 100644 index 0000000..c6c4d16 --- /dev/null +++ b/src/lib/adapters/sqljs.ts @@ -0,0 +1,559 @@ +// File: /src/lib/database/SqlJsDatabaseAdapter.ts +import crypto, { type UUID } from "crypto"; +import { type Database } from "sql.js"; +import { DatabaseAdapter } from "../database"; +import { + Account, + Actor, + GoalStatus, + type Goal, + type Memory, + type Relationship, +} from "../types"; +import { sqliteTables } from "./sqlite/sqliteTables"; + +export class SqlJsDatabaseAdapter extends DatabaseAdapter { + db: Database; + + constructor(db: Database) { + super(); + this.db = db; + + // Check if the 'accounts' table exists as a representative table + const tableExists = this.db.exec( + "SELECT name FROM sqlite_master WHERE type='table' AND name='accounts'", + )[0]; + + if (!tableExists) { + // If the 'accounts' table doesn't exist, create all the tables + this.db.exec(sqliteTables); + } + } + + async getAccountById(userId: UUID): Promise { + const sql = "SELECT * FROM accounts WHERE id = ?"; + const stmt = this.db.prepare(sql); + stmt.bind([userId]); + const account = stmt.getAsObject() as unknown as Account | undefined; + + if (account && typeof account.details === "string") { + account.details = JSON.parse(account.details); + } + + stmt.free(); + return account || null; + } + + async createAccount(account: Account): Promise { + const sql = ` + INSERT INTO accounts (id, name, email, avatar_url, details) + VALUES (?, ?, ?, ?, ?) + `; + const stmt = this.db.prepare(sql); + stmt.run([ + account.id ?? crypto.randomUUID(), + account.name, + account.email || "", + account.avatar_url || "", + JSON.stringify(account.details), + ]); + stmt.free(); + } + + async getActorDetails(params: { room_id: UUID }): Promise { + const sql = ` + SELECT a.id, a.name, a.details + FROM participants p + LEFT JOIN accounts a ON p.user_id = a.id + WHERE p.room_id = ? + `; + const stmt = this.db.prepare(sql); + stmt.bind([params.room_id]); + const rows: Actor[] = []; + while (stmt.step()) { + const row = stmt.getAsObject() as unknown as Actor; + rows.push({ + ...row, + details: + typeof row.details === "string" + ? JSON.parse(row.details) + : row.details, + }); + } + stmt.free(); + return rows; + } + + async createMemory(memory: Memory, tableName: string): Promise { + let isUnique = true; + if (memory.embedding) { + // Check if a similar memory already exists + const similarMemories = await this.searchMemoriesByEmbedding( + memory.embedding, + { + tableName, + room_id: memory.room_id, + match_threshold: 0.95, // 5% similarity threshold + count: 1, + }, + ); + + isUnique = similarMemories.length === 0; + } + + // Insert the memory with the appropriate 'unique' value + const sql = `INSERT INTO memories (id, type, content, embedding, user_id, room_id, \`unique\`) VALUES (?, ?, ?, ?, ?, ?, ?)`; + const stmt = this.db.prepare(sql); + stmt.run([ + crypto.randomUUID(), + tableName, + JSON.stringify(memory.content), + JSON.stringify(memory.embedding), + memory.user_id, + memory.room_id, + isUnique ? 1 : 0, + ]); + stmt.free(); + } + + async searchMemories(params: { + tableName: string; + room_id: UUID; + embedding: number[]; + match_threshold: number; + match_count: number; + unique: boolean; + }): Promise { + let sql = + ` + SELECT *` + + // TODO: Uncomment when we compile sql.js with vss + // `, (1 - vss_distance_l2(embedding, ?)) AS similarity` + + ` FROM memories + WHERE type = ? + AND room_id = ?`; + + if (params.unique) { + sql += " AND `unique` = 1"; + } + // TODO: Uncomment when we compile sql.js with vss + // sql += ` ORDER BY similarity DESC LIMIT ?`; + const stmt = this.db.prepare(sql); + stmt.bind([ + // JSON.stringify(params.embedding), + params.tableName, + params.room_id, + // params.match_count, + ]); + const memories: (Memory & { similarity: number })[] = []; + while (stmt.step()) { + const memory = stmt.getAsObject() as unknown as Memory & { + similarity: number; + }; + memories.push({ + ...memory, + content: JSON.parse(memory.content as unknown as string), + }); + } + stmt.free(); + return memories; + } + + async searchMemoriesByEmbedding( + _embedding: number[], + params: { + match_threshold?: number; + count?: number; + room_id?: UUID; + unique?: boolean; + tableName: string; + }, + ): Promise { + let sql = + `SELECT *` + + // TODO: Uncomment when we compile sql.js with vss + // `, (1 - vss_distance_l2(embedding, ?)) AS similarity`+ + ` FROM memories + WHERE type = ?`; + + if (params.unique) { + sql += " AND `unique` = 1"; + } + if (params.room_id) { + sql += " AND room_id = ?"; + } + // TODO: Uncomment when we compile sql.js with vss + // sql += ` ORDER BY similarity DESC`; + + if (params.count) { + sql += " LIMIT ?"; + } + + const stmt = this.db.prepare(sql); + const bindings = [ + // JSON.stringify(embedding), + params.tableName, + ]; + if (params.room_id) { + bindings.push(params.room_id); + } + if (params.count) { + bindings.push(params.count.toString()); + } + + stmt.bind(bindings); + const memories: (Memory & { similarity: number })[] = []; + while (stmt.step()) { + const memory = stmt.getAsObject() as unknown as Memory & { + similarity: number; + }; + memories.push({ + ...memory, + content: JSON.parse(memory.content as unknown as string), + }); + } + stmt.free(); + return memories; + } + + async getCachedEmbeddings(opts: { + query_table_name: string; + query_threshold: number; + query_input: string; + query_field_name: string; + query_field_sub_name: string; + query_match_count: number; + }): Promise< + { + embedding: number[]; + levenshtein_score: number; + }[] + > { + const sql = + ` + SELECT * + FROM memories + WHERE type = ?` + + // `AND vss_search(${opts.query_field_name}, ?) + // ORDER BY vss_search(${opts.query_field_name}, ?) DESC` + + ` LIMIT ? + `; + const stmt = this.db.prepare(sql); + stmt.bind([ + opts.query_table_name, + // opts.query_input, + // opts.query_input, + opts.query_match_count, + ]); + const memories: Memory[] = []; + while (stmt.step()) { + const memory = stmt.getAsObject() as unknown as Memory; + memories.push(memory); + } + stmt.free(); + + return memories.map((memory) => ({ + embedding: JSON.parse(memory.embedding as unknown as string), + levenshtein_score: 0, + })); + } + + async updateGoalStatus(params: { + goalId: UUID; + status: GoalStatus; + }): Promise { + const sql = "UPDATE goals SET status = ? WHERE id = ?"; + const stmt = this.db.prepare(sql); + stmt.run([params.status, params.goalId]); + stmt.free(); + } + + async log(params: { + body: { [key: string]: unknown }; + user_id: UUID; + room_id: UUID; + type: string; + }): Promise { + const sql = + "INSERT INTO logs (body, user_id, room_id, type) VALUES (?, ?, ?, ?)"; + const stmt = this.db.prepare(sql); + stmt.run([ + JSON.stringify(params.body), + params.user_id, + params.room_id, + params.type, + ]); + stmt.free(); + } + + async getMemories(params: { + room_id: UUID; + count?: number; + unique?: boolean; + tableName: string; + }): Promise { + if (!params.tableName) { + throw new Error("tableName is required"); + } + if (!params.room_id) { + throw new Error("room_id is required"); + } + let sql = `SELECT * FROM memories WHERE type = ? AND room_id = ?`; + + if (params.count) { + sql += " LIMIT ?"; + } + + const stmt = this.db.prepare(sql); + const bindings: (string | number)[] = [params.tableName, params.room_id]; + if (params.count) { + bindings.push(params.count.toString()); + } + + stmt.bind(bindings); + const memories: Memory[] = []; + while (stmt.step()) { + const memory = stmt.getAsObject() as unknown as Memory; + memories.push({ + ...memory, + content: JSON.parse(memory.content as unknown as string), + }); + } + stmt.free(); + return memories; + } + + async removeMemory(memoryId: UUID, tableName: string): Promise { + const sql = `DELETE FROM memories WHERE type = ? AND id = ?`; + const stmt = this.db.prepare(sql); + stmt.run([tableName, memoryId]); + stmt.free(); + } + + async removeAllMemories(room_id: UUID, tableName: string): Promise { + const sql = `DELETE FROM memories WHERE type = ? AND room_id = ?`; + const stmt = this.db.prepare(sql); + stmt.run([tableName, room_id]); + stmt.free(); + } + + async countMemories( + room_id: UUID, + unique = true, + tableName = "", + ): Promise { + if (!tableName) { + throw new Error("tableName is required"); + } + + let sql = `SELECT COUNT(*) as count FROM memories WHERE type = ? AND room_id = ?`; + if (unique) { + sql += " AND `unique` = 1"; + } + + const stmt = this.db.prepare(sql); + stmt.bind([tableName, room_id]); + + let count = 0; + if (stmt.step()) { + const result = stmt.getAsObject() as { count: number }; + count = result.count; + } + + stmt.free(); + console.log("*** count result:", count); + return count; + } + + async getGoals(params: { + room_id: UUID; + userId?: UUID | null; + onlyInProgress?: boolean; + count?: number; + }): Promise { + let sql = "SELECT * FROM goals WHERE room_id = ?"; + const bindings: (string | number)[] = [params.room_id]; + + if (params.userId) { + sql += " AND user_id = ?"; + bindings.push(params.userId); + } + + if (params.onlyInProgress) { + sql += " AND status = 'IN_PROGRESS'"; + } + + if (params.count) { + sql += " LIMIT ?"; + bindings.push(params.count.toString()); + } + + const stmt = this.db.prepare(sql); + stmt.bind(bindings); + const goals: Goal[] = []; + while (stmt.step()) { + const goal = stmt.getAsObject() as unknown as Goal; + goals.push({ + ...goal, + objectives: + typeof goal.objectives === "string" + ? JSON.parse(goal.objectives) + : goal.objectives, + }); + } + stmt.free(); + return goals; + } + + async updateGoal(goal: Goal): Promise { + const sql = + "UPDATE goals SET name = ?, status = ?, objectives = ? WHERE id = ?"; + const stmt = this.db.prepare(sql); + stmt.run([ + goal.name, + goal.status, + JSON.stringify(goal.objectives), + goal.id as string, + ]); + stmt.free(); + } + + async createGoal(goal: Goal): Promise { + const sql = + "INSERT INTO goals (id, room_id, user_id, name, status, objectives) VALUES (?, ?, ?, ?, ?, ?)"; + const stmt = this.db.prepare(sql); + stmt.run([ + goal.id ?? crypto.randomUUID(), + goal.room_id, + goal.user_id, + goal.name, + goal.status, + JSON.stringify(goal.objectives), + ]); + stmt.free(); + } + + async removeGoal(goalId: UUID): Promise { + const sql = "DELETE FROM goals WHERE id = ?"; + const stmt = this.db.prepare(sql); + stmt.run([goalId]); + stmt.free(); + } + + async removeAllGoals(room_id: UUID): Promise { + const sql = "DELETE FROM goals WHERE room_id = ?"; + const stmt = this.db.prepare(sql); + stmt.run([room_id]); + stmt.free(); + } + + async createRoom(name: string): Promise { + const roomId = crypto.randomUUID(); + try { + const sql = "INSERT INTO rooms (id, name) VALUES (?, ?)"; + const stmt = this.db.prepare(sql); + stmt.run([roomId, name]); + stmt.free(); + } catch (error) { + console.log("Error creating room", error); + } + return roomId as UUID; + } + + async removeRoom(roomId: UUID): Promise { + const sql = "DELETE FROM rooms WHERE id = ?"; + const stmt = this.db.prepare(sql); + stmt.run([roomId]); + stmt.free(); + } + + async getRoomsByParticipant(userId: UUID): Promise { + const sql = "SELECT room_id FROM participants WHERE user_id = ?"; + const stmt = this.db.prepare(sql); + stmt.bind([userId]); + const rows: { room_id: string }[] = []; + while (stmt.step()) { + const row = stmt.getAsObject() as unknown as { room_id: string }; + rows.push(row); + } + stmt.free(); + return rows.map((row) => row.room_id as UUID); + } + + async getRoomsByParticipants(userIds: UUID[]): Promise { + // Assuming userIds is an array of UUID strings, prepare a list of placeholders + const placeholders = userIds.map(() => "?").join(", "); + // Construct the SQL query with the correct number of placeholders + const sql = `SELECT room_id FROM participants WHERE user_id IN (${placeholders})`; + const stmt = this.db.prepare(sql); + // Execute the query with the userIds array spread into arguments + stmt.bind(userIds); + const rows: { room_id: string }[] = []; + while (stmt.step()) { + const row = stmt.getAsObject() as unknown as { room_id: string }; + rows.push(row); + } + stmt.free(); + // Map and return the room_id values as UUIDs + return rows.map((row) => row.room_id as UUID); + } + + async addParticipantToRoom(userId: UUID, roomId: UUID): Promise { + const sql = + "INSERT INTO participants (id, user_id, room_id) VALUES (?, ?, ?)"; + const stmt = this.db.prepare(sql); + stmt.run([crypto.randomUUID(), userId, roomId]); + stmt.free(); + } + + async removeParticipantFromRoom(userId: UUID, roomId: UUID): Promise { + const sql = "DELETE FROM participants WHERE user_id = ? AND room_id = ?"; + const stmt = this.db.prepare(sql); + stmt.run([userId, roomId]); + stmt.free(); + } + + async createRelationship(params: { + userA: UUID; + userB: UUID; + }): Promise { + if (!params.userA || !params.userB) { + throw new Error("userA and userB are required"); + } + const sql = + "INSERT INTO relationships (id, user_a, user_b, user_id) VALUES (?, ?, ?, ?)"; + const stmt = this.db.prepare(sql); + stmt.run([crypto.randomUUID(), params.userA, params.userB, params.userA]); + stmt.free(); + return true; + } + + async getRelationship(params: { + userA: UUID; + userB: UUID; + }): Promise { + const sql = + "SELECT * FROM relationships WHERE (user_a = ? AND user_b = ?) OR (user_a = ? AND user_b = ?)"; + const stmt = this.db.prepare(sql); + stmt.bind([params.userA, params.userB, params.userB, params.userA]); + const relationship = stmt.getAsObject() as unknown as + | Relationship + | undefined; + console.log("relationship", relationship); + stmt.free(); + return relationship || null; + } + + async getRelationships(params: { userId: UUID }): Promise { + const sql = "SELECT * FROM relationships WHERE (user_a = ? OR user_b = ?)"; + const stmt = this.db.prepare(sql); + stmt.bind([params.userId, params.userId]); + const relationships: Relationship[] = []; + while (stmt.step()) { + const relationship = stmt.getAsObject() as unknown as Relationship; + relationships.push(relationship); + } + stmt.free(); + return relationships; + } +} diff --git a/src/lib/adapters/supabase.ts b/src/lib/adapters/supabase.ts index 9c0b24a..71c7196 100644 --- a/src/lib/adapters/supabase.ts +++ b/src/lib/adapters/supabase.ts @@ -31,6 +31,7 @@ export class SupabaseDatabaseAdapter extends DatabaseAdapter { } async createAccount(account: Account): Promise { + // TODO: change to insert and run tests const { error } = await this.supabase.from("accounts").upsert([account]); if (error) { throw new Error(error.message); diff --git a/src/lib/index.ts b/src/lib/index.ts index 9358746..c05da80 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -52,6 +52,8 @@ export { export { SupabaseDatabaseAdapter } from "./adapters/supabase"; export { SqliteDatabaseAdapter } from "./adapters/sqlite"; +export { SqlJsDatabaseAdapter } from "./adapters/sqljs"; + export { DatabaseAdapter } from "./database"; // Export from ./src/runtime diff --git a/src/test/createRuntime.ts b/src/test/createRuntime.ts index 6fdb651..e05f784 100644 --- a/src/test/createRuntime.ts +++ b/src/test/createRuntime.ts @@ -14,6 +14,8 @@ import { } from "./constants"; import { User } from "./types"; import { load } from "../lib/adapters/sqlite/sqlite_vss"; +import { SqlJsDatabaseAdapter } from "../lib/adapters/sqljs"; +import initSqlJs from "sql.js"; export async function createRuntime({ env, @@ -52,6 +54,28 @@ export async function createRuntime({ } as Session; } break; + case "sqljs": + { + // SQLite adapter + const SQL = await initSqlJs({}); + const db = new SQL.Database(); + + adapter = new SqlJsDatabaseAdapter(db); + + // Load sqlite-vss + // load((adapter as SqliteDatabaseAdapter).db); + // Create a test user and session + user = { + id: zeroUuid, + email: "test@example.com", + } as User; + session = { + access_token: "test-access-token", + refresh_token: "test-refresh-token", + user: user, + } as Session; + } + break; case "supabase": default: { diff --git a/src/test/getOrCreateRelationship.ts b/src/test/getOrCreateRelationship.ts index 1466ad5..edf9653 100644 --- a/src/test/getOrCreateRelationship.ts +++ b/src/test/getOrCreateRelationship.ts @@ -11,42 +11,48 @@ export async function getOrCreateRelationship({ userB: UUID; }): Promise { // Check if a relationship already exists between userA and userB + let relationship: Relationship | null = null; try { - let relationship = await getRelationship({ runtime, userA, userB }); - // Check if a room already exists for the participants - const rooms = await runtime.databaseAdapter.getRoomsByParticipants([ + relationship = await getRelationship({ runtime, userA, userB }); + } catch (error) { + console.log("Error fetching relationship", error); + } + // Check if a room already exists for the participants + const rooms = await runtime.databaseAdapter.getRoomsByParticipants([ + userA, + userB, + ]); + + let roomId: UUID; + if (!rooms || rooms.length === 0) { + // If no room exists, create a new room for the relationship + roomId = await runtime.databaseAdapter.createRoom("Direct Message"); + + // Add participants to the newly created room + await runtime.databaseAdapter.addParticipantToRoom(userA, roomId); + await runtime.databaseAdapter.addParticipantToRoom(userB, roomId); + } else { + // If a room already exists, use the existing room + roomId = rooms[0]; + } + + if (!relationship) { + console.log("Creating relationship", userA, userB); + // Create the relationship + await runtime.databaseAdapter.createRelationship({ userA, userB, - ]); - - let roomId: UUID; - if (!rooms || rooms.length === 0) { - // If no room exists, create a new room for the relationship - roomId = await runtime.databaseAdapter.createRoom("Direct Message"); - - // Add participants to the newly created room - await runtime.databaseAdapter.addParticipantToRoom(userA, roomId); - await runtime.databaseAdapter.addParticipantToRoom(userB, roomId); - } else { - // If a room already exists, use the existing room - roomId = rooms[0]; - } + }); - if (!relationship) { - // Create the relationship - await runtime.databaseAdapter.createRelationship({ - userA, - userB, - }); + console.log("Fetching relationship", userA, userB); + + relationship = await getRelationship({ runtime, userA, userB }); - relationship = await getRelationship({ runtime, userA, userB }); + console.log("Fetched relationship", relationship); - if (!relationship) { - throw new Error("Failed to fetch the created relationship"); - } + if (!relationship) { + throw new Error("Failed to fetch the created relationship"); } - return { ...relationship, room_id: roomId }; - } catch (error) { - throw new Error(`Error creating relationship: ${JSON.stringify(error)}`); } + return { ...relationship, room_id: roomId }; }