diff --git a/src/index.ts b/src/index.ts index 3456f97..1112126 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,13 @@ import type { Column, ColumnShorthand, DataType } from "./columns"; -import { type ExtractRowShape, Instance } from "./instance"; +import { WrappedRow } from "./wrapped-row"; import { Prequel } from "./prequel"; import { Table } from "./table"; export { Prequel, Table, - Instance, + WrappedRow, type DataType, type Column, type ColumnShorthand, - type ExtractRowShape, }; diff --git a/src/instance.ts b/src/instance.ts deleted file mode 100644 index 49b322f..0000000 --- a/src/instance.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { Table } from "./table"; - -export type ExtractRowShape = T extends Table ? R : never; - -/** - * Represents an instance of a row in a table of a database. - * Wraps the raw Row data and provides methods for interacting with it. - */ -export abstract class Instance< - TableType extends Table, - SerializedInstanceType = void, -> { - protected row: ExtractRowShape; - protected table: TableType; - - constructor(table: TableType, row: ExtractRowShape) { - this.row = row; - this.table = table; - } - - /** - * Saves any changes made to the row back to the database. - */ - save() { - this.table.update(this.row); - } - - /** - * Serializes the instance into a generic format - */ - serialize(): SerializedInstanceType { - throw new Error("Not implemented"); - } - - /** - * @returns The row data as a JSON string. - */ - toJSON(): string { - return JSON.stringify(this.row); - } - - /** - * Exports the raw row data. - * @returns The raw row data. - */ - export(): ExtractRowShape { - return this.row; - } -} diff --git a/src/table.ts b/src/table.ts index 8aac434..b29dbf0 100644 --- a/src/table.ts +++ b/src/table.ts @@ -7,12 +7,15 @@ import { } from "./columns"; import { SchemaError } from "./error"; import { Prequel } from "./prequel"; +import type { WrappedRow } from "./wrapped-row"; + +export type ExtractRowShape = T extends Table ? R : never; /** Types that may appear in a row */ -type Value = string | number | null; +type ColumnValue = string | number | null; export class PrequelIDNotFoundError extends Error { - constructor(tableName: string, id: Value) { + constructor(tableName: string, id: ColumnValue) { super(`ID ${id} not found in ${tableName}`); } } @@ -25,11 +28,11 @@ export class PrequelEmptyResultsError extends Error { /** An arbitrarily shaped Row */ interface ArbitraryRow { - [key: string]: Value; + [key: string]: ColumnValue; } -function shapeForQuery(obj: Partial): Record { - const asInsertDataShape: Record = {}; +function shapeForQuery(obj: Partial): Record { + const asInsertDataShape: Record = {}; const keys = Object.keys(obj); @@ -43,7 +46,7 @@ function shapeForQuery(obj: Partial): Record { } const asQueryKey = `$${key}`; - asInsertDataShape[asQueryKey] = val as Value; + asInsertDataShape[asQueryKey] = val as ColumnValue; } return asInsertDataShape; @@ -282,7 +285,7 @@ export class Table { * @param id - The ID of the entry to check for existence. * @returns `true` if an entry with the specified ID exists, otherwise `false`. */ - public exists(id: Value): boolean { + public exists(id: ColumnValue): boolean { return this.findOneById(id) !== null; } @@ -321,7 +324,7 @@ export class Table { * @param id - The unique identifier of the row to find. * @returns The row shape if found, otherwise `null`. */ - public findOneById(id: Value): RowShape | null { + public findOneById(id: ColumnValue): RowShape | null { return this._findQuery.get(`${id}`); } @@ -333,7 +336,7 @@ export class Table { * * @throws {Error} If no row is found with the specified ID. */ - public findOneByIdOrFail(id: Value): RowShape { + public findOneByIdOrFail(id: ColumnValue): RowShape { const found = this.findOneById(id); if (!found) { @@ -429,7 +432,7 @@ export class Table { * @param id - The unique identifier of the record to be deleted. * @returns void */ - public deleteById = (id: Value): void => { + public deleteById = (id: ColumnValue): void => { if (this._primaryColumnName === "ROWID") { throw new Error( "Cannot use `Table.deleteById()` without setting a primary column", @@ -489,6 +492,11 @@ export class Table { return res.count; } + public save(wrapped: WrappedRow) { + const row = wrapped.unwrap(); + this.update(row); + } + public toString() { return `"${this._name}"`; } diff --git a/src/wrapped-row.ts b/src/wrapped-row.ts new file mode 100644 index 0000000..d093710 --- /dev/null +++ b/src/wrapped-row.ts @@ -0,0 +1,26 @@ +/** + * Represents an instance of a row in a table of a database. + * Wraps the raw Row data and provides methods for interacting with it. + */ +export class WrappedRow { + protected row: RowShape; + + constructor(row: RowShape) { + this.row = row; + } + + /** + * @returns The row data as a JSON string. + */ + toJSON(): string { + return JSON.stringify(this.row); + } + + /** + * Exports the raw row data. + * @returns The raw row data. + */ + unwrap(): RowShape { + return this.row; + } +} diff --git a/test/table.test.ts b/test/table.test.ts index 054c26b..59f91d4 100644 --- a/test/table.test.ts +++ b/test/table.test.ts @@ -7,6 +7,14 @@ interface UserWithID { name: string; } +interface UserWithBio { + user_id: number; + bio: { + name: string; + age: number; + }; +} + interface Post { post_id: number; user_id: number; diff --git a/test/instance.test.ts b/test/wrapped-row.ts similarity index 66% rename from test/instance.test.ts rename to test/wrapped-row.ts index 9b5fc97..d500c7b 100644 --- a/test/instance.test.ts +++ b/test/wrapped-row.ts @@ -1,6 +1,6 @@ import { Database } from "bun:sqlite"; import { expect, test } from "bun:test"; -import { Instance, Prequel, Table } from "../src/index"; +import { WrappedRow, Table } from "../src/index"; interface User { id: number; @@ -20,7 +20,7 @@ const table = new Table(db, "Users", { name: "TEXT", }); -class UserInstance extends Instance { +class UserWrappedRow extends WrappedRow { get name(): string { return this.row.name; } @@ -28,25 +28,19 @@ class UserInstance extends Instance { set name(val: string) { this.row.name = val; } - - serialize(): SerializedUser { - return { - name: this.row.name, - }; - } } -test("setting values on an instance", () => { +test("setting values on an WrappedRow", () => { table.insert({ id: 1, name: "Alice", }); const alice = table.findOneByIdOrFail(1); - const inst = new UserInstance(table, alice); + const inst = new UserWrappedRow(alice); inst.name = "Bob"; - inst.save(); + table.save(inst); const bob = table.findOneByIdOrFail(1); expect(bob.name).toEqual("Bob");