diff --git a/package.json b/package.json index 079e9a2..e582f98 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bgent", - "version": "0.1.5", + "version": "0.1.6", "private": false, "description": "bgent. because agent was taken.", "type": "module", diff --git a/src/lib/adapters/sqlite/types.ts b/src/lib/adapters/sqlite/types.ts index 1919d19..c90fa2d 100644 --- a/src/lib/adapters/sqlite/types.ts +++ b/src/lib/adapters/sqlite/types.ts @@ -2,7 +2,7 @@ interface RunResult { changes: number; lastInsertRowid: number | bigint; } -interface Statement { +export interface Statement { database: Database; source: string; reader: boolean; diff --git a/src/lib/adapters/sqljs.ts b/src/lib/adapters/sqljs.ts index 2d5903f..270fe84 100644 --- a/src/lib/adapters/sqljs.ts +++ b/src/lib/adapters/sqljs.ts @@ -1,6 +1,5 @@ // File: /src/lib/database/SqlJsDatabaseAdapter.ts import crypto, { type UUID } from "crypto"; -import { type Database } from "sql.js"; import { DatabaseAdapter } from "../database"; import { Account, @@ -11,6 +10,7 @@ import { type Relationship, } from "../types"; import { sqliteTables } from "./sqlite/sqliteTables"; +import { Database } from "./sqljs/types"; export class SqlJsDatabaseAdapter extends DatabaseAdapter { db: Database; diff --git a/src/lib/adapters/sqljs/types.ts b/src/lib/adapters/sqljs/types.ts new file mode 100644 index 0000000..8baa00a --- /dev/null +++ b/src/lib/adapters/sqljs/types.ts @@ -0,0 +1,210 @@ +type SqlValue = number | string | Uint8Array | null; +type ParamsObject = Record; +type ParamsCallback = (obj: ParamsObject) => void; +type BindParams = SqlValue[] | ParamsObject | null; +interface QueryExecResult { + columns: string[]; + values: SqlValue[][]; +} + +declare class StatementIterator + implements Iterator, Iterable +{ + [Symbol.iterator](): Iterator; + getRemainingSQL(): string; + next(): StatementIteratorResult; +} +interface StatementIteratorResult { + /** `true` if there are no more available statements */ + done: boolean; + /** the next available Statement (as returned by `Database.prepare`) */ + value: Statement; +} +declare class Statement { + /** + * Bind values to the parameters, after having reseted the statement. If + * values is null, do nothing and return true. + * + * SQL statements can have parameters, named '?', '?NNN', ':VVV', + * '@VVV', '$VVV', where NNN is a number and VVV a string. This function + * binds these parameters to the given values. + * + * Warning: ':', '@', and '$' are included in the parameters names + * + * ### Value types + * + * |Javascript type|SQLite type| + * |-|-| + * |number|REAL, INTEGER| + * |boolean|INTEGER| + * |string|TEXT| + * |Array, Uint8Array|BLOB| + * |null|NULL| + * @see [https://sql.js.org/documentation/Statement.html#["bind"]](https://sql.js.org/documentation/Statement.html#%5B%22bind%22%5D) + * + * @param values The values to bind + */ + bind(values?: BindParams): boolean; + + /** + * Free the memory used by the statement + * @see [https://sql.js.org/documentation/Statement.html#["free"]](https://sql.js.org/documentation/Statement.html#%5B%22free%22%5D) + */ + free(): boolean; + + /** + * Free the memory allocated during parameter binding + * @see [https://sql.js.org/documentation/Statement.html#["freemem"]](https://sql.js.org/documentation/Statement.html#%5B%22freemem%22%5D) + */ + freemem(): void; + + /** + * Get one row of results of a statement. If the first parameter is not + * provided, step must have been called before. + * @see [https://sql.js.org/documentation/Statement.html#["get"]](https://sql.js.org/documentation/Statement.html#%5B%22get%22%5D) + * + * @param params If set, the values will be bound to the statement + * before it is executed + */ + get(params?: BindParams): SqlValue[]; + + /** + * Get one row of result as a javascript object, associating column + * names with their value in the current row + * @see [https://sql.js.org/documentation/Statement.html#["getAsObject"]](https://sql.js.org/documentation/Statement.html#%5B%22getAsObject%22%5D) + * + * @param params If set, the values will be bound to the statement, and + * it will be executed + */ + getAsObject(params?: BindParams): ParamsObject; + + /** + * Get the list of column names of a row of result of a statement. + * @see [https://sql.js.org/documentation/Statement.html#["getColumnNames"]](https://sql.js.org/documentation/Statement.html#%5B%22getColumnNames%22%5D) + */ + getColumnNames(): string[]; + + /** + * Get the SQLite's normalized version of the SQL string used in + * preparing this statement. The meaning of "normalized" is not + * well-defined: see + * [the SQLite documentation](https://sqlite.org/c3ref/expanded_sql.html). + * @see [https://sql.js.org/documentation/Statement.html#["getNormalizedSQL"]](https://sql.js.org/documentation/Statement.html#%5B%22getNormalizedSQL%22%5D) + */ + getNormalizedSQL(): string; + + /** + * Get the SQL string used in preparing this statement. + * @see [https://sql.js.org/documentation/Statement.html#["getSQL"]](https://sql.js.org/documentation/Statement.html#%5B%22getSQL%22%5D) + */ + getSQL(): string; + + /** + * Reset a statement, so that it's parameters can be bound to new + * values. It also clears all previous bindings, freeing the memory used + * by bound parameters. + * @see [https://sql.js.org/documentation/Statement.html#["reset"]](https://sql.js.org/documentation/Statement.html#%5B%22reset%22%5D) + */ + reset(): void; + + /** + * Shorthand for bind + step + reset Bind the values, execute the + * statement, ignoring the rows it returns, and resets it + * @param values Value to bind to the statement + */ + run(values?: BindParams): void; + + /** + * Execute the statement, fetching the the next line of result, that can + * be retrieved with `Statement.get`. + * @see [https://sql.js.org/documentation/Statement.html#["step"]](https://sql.js.org/documentation/Statement.html#%5B%22step%22%5D) + */ + step(): boolean; +} +export declare class Database { + constructor(data?: ArrayLike | Buffer | null); + + close(): void; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + create_function(name: string, func: (...args: any[]) => any): Database; + + each( + sql: string, + params: BindParams, + callback: ParamsCallback, + done: () => void + ): Database; + each(sql: string, callback: ParamsCallback, done: () => void): Database; + + /** + * Execute an SQL query, and returns the result. + * + * This is a wrapper against `Database.prepare`, `Statement.bind`, `Statement.step`, `Statement.get`, and `Statement.free`. + * + * The result is an array of result elements. There are as many result elements as the number of statements in your sql string (statements are separated by a semicolon) + * @see [https://sql.js.org/documentation/Database.html#["exec"]](https://sql.js.org/documentation/Database.html#%5B%22exec%22%5D) + * + * @param sql a string containing some SQL text to execute + * @param params When the SQL statement contains placeholders, you can + * pass them in here. They will be bound to the statement before it is + * executed. If you use the params argument as an array, you **cannot** + * provide an sql string that contains several statements (separated by + * `;`). This limitation does not apply to params as an object. + */ + exec(sql: string, params?: BindParams): QueryExecResult[]; + + /** + * Exports the contents of the database to a binary array + * @see [https://sql.js.org/documentation/Database.html#["export"]](https://sql.js.org/documentation/Database.html#%5B%22export%22%5D) + */ + export(): Uint8Array; + + /** + * Returns the number of changed rows (modified, inserted or deleted) by + * the latest completed `INSERT`, `UPDATE` or `DELETE` statement on the + * database. Executing any other type of SQL statement does not modify + * the value returned by this function. + * @see [https://sql.js.org/documentation/Database.html#["getRowsModified"]](https://sql.js.org/documentation/Database.html#%5B%22getRowsModified%22%5D) + */ + getRowsModified(): number; + + /** + * Analyze a result code, return null if no error occured, and throw an + * error with a descriptive message otherwise + * @see [https://sql.js.org/documentation/Database.html#["handleError"]](https://sql.js.org/documentation/Database.html#%5B%22handleError%22%5D) + */ + handleError(): null | never; + + /** + * Iterate over multiple SQL statements in a SQL string. This function + * returns an iterator over Statement objects. You can use a `for..of` + * loop to execute the returned statements one by one. + * @see [https://sql.js.org/documentation/Database.html#["iterateStatements"]](https://sql.js.org/documentation/Database.html#%5B%22iterateStatements%22%5D) + * + * @param sql a string of SQL that can contain multiple statements + */ + iterateStatements(sql: string): StatementIterator; + + /** + * Prepare an SQL statement + * @see [https://sql.js.org/documentation/Database.html#["prepare"]](https://sql.js.org/documentation/Database.html#%5B%22prepare%22%5D) + * + * @param sql a string of SQL, that can contain placeholders (`?`, `:VVV`, `:AAA`, `@AAA`) + * @param params values to bind to placeholders + */ + prepare(sql: string, params?: BindParams): Statement; + + /** + * Execute an SQL query, ignoring the rows it returns. + * @see [https://sql.js.org/documentation/Database.html#["run"]](https://sql.js.org/documentation/Database.html#%5B%22run%22%5D) + * + * @param sql a string containing some SQL text to execute + * @param params When the SQL statement contains placeholders, you can + * pass them in here. They will be bound to the statement before it is + * executed. If you use the params argument as an array, you **cannot** + * provide an sql string that contains several statements (separated by + * `;`). This limitation does not apply to params as an object. + */ + run(sql: string, params?: BindParams): Database; +} diff --git a/src/lib/runtime.ts b/src/lib/runtime.ts index ef6fc72..15d5ccf 100644 --- a/src/lib/runtime.ts +++ b/src/lib/runtime.ts @@ -552,7 +552,7 @@ export class BgentRuntime { }; const actionPromises = this.actions.map(async (action: Action) => { - const result = await action.validate(this, message); + const result = await action.validate(this, message, initialState); if (result) { return action; } diff --git a/src/test/createRuntime.ts b/src/test/createRuntime.ts index e05f784..dbf8772 100644 --- a/src/test/createRuntime.ts +++ b/src/test/createRuntime.ts @@ -1,5 +1,3 @@ -import { Session, createClient } from "@supabase/supabase-js"; -import Database from "better-sqlite3"; import { SqliteDatabaseAdapter } from "../lib/adapters/sqlite"; import { SupabaseDatabaseAdapter } from "../lib/adapters/supabase"; import { zeroUuid } from "../lib/constants"; @@ -15,7 +13,6 @@ import { 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, @@ -32,11 +29,17 @@ export async function createRuntime({ }) { let adapter: DatabaseAdapter; let user: User; - let session: Session; + let session: { + user: User; + }; switch (env?.TEST_DATABASE_CLIENT as string) { case "sqlite": { + const module = await import("better-sqlite3"); + + const Database = module.default; + // SQLite adapter adapter = new SqliteDatabaseAdapter(new Database(":memory:")); @@ -48,14 +51,16 @@ export async function createRuntime({ email: "test@example.com", } as User; session = { - access_token: "test-access-token", - refresh_token: "test-refresh-token", user: user, - } as Session; + }; } break; case "sqljs": { + const module = await import("sql.js"); + + const initSqlJs = module.default; + // SQLite adapter const SQL = await initSqlJs({}); const db = new SQL.Database(); @@ -70,15 +75,17 @@ export async function createRuntime({ 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: { + const module = await import("@supabase/supabase-js"); + + const { createClient } = module; + const supabase = createClient( env?.SUPABASE_URL ?? SUPABASE_URL, env?.SUPABASE_SERVICE_API_KEY ?? SUPABASE_ANON_KEY, @@ -90,7 +97,7 @@ export async function createRuntime({ }); user = data.user as User; - session = data.session as Session; + session = data.session as unknown as { user: User }; if (!session) { const response = await supabase.auth.signUp({ @@ -111,7 +118,7 @@ export async function createRuntime({ } user = response.data.user as User; - session = response.data.session as Session; + session = response.data.session as unknown as { user: User }; } adapter = new SupabaseDatabaseAdapter(