From 6479d09196ee0d9d70c1dfd15f28adbd8951628b Mon Sep 17 00:00:00 2001 From: Endeavorance Date: Sun, 6 Apr 2025 19:28:42 -0400 Subject: [PATCH] Add biome --- biome.json | 34 +++ bun.lockb | Bin 10560 -> 14054 bytes package.json | 10 +- src/column-types.ts | 8 +- src/index.ts | 11 +- src/prequel.ts | 322 +++++++++++++------------- src/table.ts | 15 +- test/prequel.test.ts | 192 ++++++++-------- test/table.test.ts | 532 +++++++++++++++++++++---------------------- 9 files changed, 586 insertions(+), 538 deletions(-) create mode 100644 biome.json diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..7a8c1f6 --- /dev/null +++ b/biome.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false, + "ignore": ["dist"] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noArrayIndexKey": "off" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + } +} diff --git a/bun.lockb b/bun.lockb index da595480330b6c51423f09d0bc0ac074386801fe..eb2036d926d2799dfa49565536549ea0bafe92b6 100755 GIT binary patch delta 4245 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 delta 1926 zcmb_dZ%kWN6ujfp84A=mk-=^?$p<3kNrA%RaZZH=H&Y8ULLyRrpv2$ zp9lz}mCnY>lXa!0V1Dz2824kC1DpW5fg|yOog03EWjZl}boZn^jld7BmI0Nm>N- zOLEwBr72ZcpTsSgHqTfa zlm#m}9J*SBBW=S1X;`_}MpF)5nX}O%;G&HjS-KLoQyXB&PE%P;N%$bR<3s`m4aD^b z?g+Bc{t0;vf^D>G5wRBeDgCv`d=EOK7oB;<95xP#LuYr^p|f0$&W({} zZfu78|Liuxf%YH#{;S(%|LQhfaSqYruBcS#j4LKBRFfZ*8T2T=R$9i_M!|xZw9{dH zGwBqDHfa$pD8z)I(drlK z(^YddAx|8r-13z{2x(y<(si|^XQ^xSi3k0d2?aI$M7yM$176==92EYHe|Nrpq4#3h z9wr#`VnU(ufVVzffmgaZcAalNy#<7bwo%(CM7l=5zCZY8;^)pe%!JLEF$;YT+eT4_ z?Ok84_z&&R+YLg4Nic0IjgovdUSGOq@2GqKSn=WqZtymlJP>x)col>0DaW4avVLE` zzoj)RHv(!1R9uB>rMVh!eY&2n3*Q*OaOg-S2tm_Uy0SmA((~D5{!f_UiwHLev23Fi zxREaTvvnO$RlU{HsK~JpOoy~cwVf(zz53q?wPviuOocgEN}-wKz551-;)#Sd*xh%q xdw@o2t2t8yy;K`4Ng{Fh|L3vD^`FY&oHeoDwD4+xeyqKdD#NAom$XavKLL6CL-YUu diff --git a/package.json b/package.json index a065645..694bc37 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,13 @@ "types": "./dist/index.d.ts", "scripts": { "build": "bun run ./build.ts", - "clean": "rm -rf dist" + "clean": "rm -rf dist", + "fmt": "biome check --fix" }, "keywords": [], "author": "Endeavorance (https://endeavorance.camp)", "license": "CC BY-NC-SA 4.0", - "files": [ - "dist" - ], + "files": ["dist"], "type": "module", "devDependencies": { "@types/bun": "latest", @@ -20,5 +19,8 @@ }, "peerDependencies": { "typescript": "^5.0.0" + }, + "dependencies": { + "@biomejs/biome": "^1.9.4" } } diff --git a/src/column-types.ts b/src/column-types.ts index 4061d25..4a0f3ed 100644 --- a/src/column-types.ts +++ b/src/column-types.ts @@ -28,7 +28,7 @@ export const ColumnOf = { type: otherTable.primaryColumnType(), references: otherTable.reference(), nullable: false, - cascade + cascade, }; }, @@ -36,7 +36,6 @@ export const ColumnOf = { Text: { type: "TEXT", nullable: true, - }, Int: { @@ -94,7 +93,7 @@ export const ColumnOf = { return { type: "TEXT", default: defaultValue, - } + }; }, Int(defaultValue: number): ColumnShorthand { @@ -109,7 +108,6 @@ export const ColumnOf = { type: "REAL", default: defaultValue, } as const; - } + }, }, - } as const; diff --git a/src/index.ts b/src/index.ts index c624460..2aa8b5b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,13 @@ +import { ColumnOf } from "./column-types"; import type { Column, ColumnShorthand, DataType } from "./columns"; import { Prequel } from "./prequel"; import { Table } from "./table"; -import { ColumnOf } from "./column-types"; -export { Prequel, Table, ColumnOf, type DataType, type Column, type ColumnShorthand }; +export { + Prequel, + Table, + ColumnOf, + type DataType, + type Column, + type ColumnShorthand, +}; diff --git a/src/prequel.ts b/src/prequel.ts index 4e09fe2..4280a0a 100644 --- a/src/prequel.ts +++ b/src/prequel.ts @@ -4,194 +4,194 @@ import { SchemaError } from "./error"; import { Table } from "./table"; interface KVRow { - kv_id: number; - key: string; - value: string; + kv_id: number; + key: string; + value: string; } /** * Represents a key-value store using Prequel. */ export class PrequelKVStore { - private table: Table; + private table: Table; - /** - * Creates a new instance of the PrequelKVStore class. - * @param pq The Prequel instance. - * @param tableName The name of the table. - */ - constructor(pq: Prequel, tableName: string) { - this.table = pq.table(tableName, { - kv_id: { - type: "INTEGER", - primary: true, - autoincrement: true, - }, - key: "TEXT", - value: "TEXT", - }); - } + /** + * Creates a new instance of the PrequelKVStore class. + * @param pq The Prequel instance. + * @param tableName The name of the table. + */ + constructor(pq: Prequel, tableName: string) { + this.table = pq.table(tableName, { + kv_id: { + type: "INTEGER", + primary: true, + autoincrement: true, + }, + key: "TEXT", + value: "TEXT", + }); + } - /** - * Inserts or updates a key-value pair in the store. - * @param key The key. - * @param value The value. - */ - put(key: string, value: T): void { - const lookup = this.table.findOneWhere({ - key, - }); + /** + * Inserts or updates a key-value pair in the store. + * @param key The key. + * @param value The value. + */ + put(key: string, value: T): void { + const lookup = this.table.findOneWhere({ + key, + }); - if (lookup === null) { - this.table.insertPartial({ - key, - value: JSON.stringify(value), - }); - } else { - lookup.value = JSON.stringify(value); - this.table.update(lookup); - } - } + if (lookup === null) { + this.table.insertPartial({ + key, + value: JSON.stringify(value), + }); + } else { + lookup.value = JSON.stringify(value); + this.table.update(lookup); + } + } - /** - * Retrieves the value associated with the specified key from the store. - * @param key The key. - * @returns The value associated with the key, or null if the key is not found. - */ - get(key: string): T | null { - const lookup = this.table.findOneWhere({ key }); + /** + * Retrieves the value associated with the specified key from the store. + * @param key The key. + * @returns The value associated with the key, or null if the key is not found. + */ + get(key: string): T | null { + const lookup = this.table.findOneWhere({ key }); - if (lookup === null) { - return null; - } + if (lookup === null) { + return null; + } - return JSON.parse(lookup.value) as T; - } + return JSON.parse(lookup.value) as T; + } - /** - * Checks if a key exists in the table. - * @param key - The key to check. - * @returns `true` if the key exists, `false` otherwise. - */ - has(key: string): boolean { - return ( - this.table.findOneWhere({ - key, - }) !== null - ); - } + /** + * Checks if a key exists in the table. + * @param key - The key to check. + * @returns `true` if the key exists, `false` otherwise. + */ + has(key: string): boolean { + return ( + this.table.findOneWhere({ + key, + }) !== null + ); + } - /** - * Get an array of all of the keys in the KV store - * @returns The keys in the store. - */ - keys(): string[] { - return this.table.findAll().map((row) => row.key); - } + /** + * Get an array of all of the keys in the KV store + * @returns The keys in the store. + */ + keys(): string[] { + return this.table.findAll().map((row) => row.key); + } - /** - * Get an array of all of the values in the KV store - * @returns The values in the store. - */ - values(): unknown[] { - return this.table.findAll().map((row) => JSON.parse(row.value)); - } + /** + * Get an array of all of the values in the KV store + * @returns The values in the store. + */ + values(): unknown[] { + return this.table.findAll().map((row) => JSON.parse(row.value)); + } } /** * Represents a Prequel database instance. */ export class Prequel { - public db: Database; - public kv: PrequelKVStore; - public tables: Record> = {}; - public query: typeof Database.prototype.query; - public uncachedQuery: typeof Database.prototype.prepare; + public db: Database; + public kv: PrequelKVStore; + public tables: Record> = {}; + public query: typeof Database.prototype.query; + public uncachedQuery: typeof Database.prototype.prepare; - /** - * Creates a new Prequel database instance. - * @param filename The filename of the database. Defaults to ":memory:" if not provided. - */ - constructor(filename = ":memory:") { - this.db = new Database(filename); - this.query = this.db.query.bind(this.db); - this.uncachedQuery = this.db.prepare.bind(this.db); - this.db.exec("PRAGMA foreign_keys=ON"); - this.kv = new PrequelKVStore(this, "PrequelManagedKVStore"); - } + /** + * Creates a new Prequel database instance. + * @param filename The filename of the database. Defaults to ":memory:" if not provided. + */ + constructor(filename = ":memory:") { + this.db = new Database(filename); + this.query = this.db.query.bind(this.db); + this.uncachedQuery = this.db.prepare.bind(this.db); + this.db.exec("PRAGMA foreign_keys=ON"); + this.kv = new PrequelKVStore(this, "PrequelManagedKVStore"); + } - /** - * Enables Write-Ahead Logging (WAL) mode for the database. - */ - enableWAL() { - this.db.exec("PRAGMA journal_mode=WAL;"); - } + /** + * Enables Write-Ahead Logging (WAL) mode for the database. + */ + enableWAL() { + this.db.exec("PRAGMA journal_mode=WAL;"); + } - /** - * Closes the database connection. - */ - close() { - this.db.close(); - } + /** + * Closes the database connection. + */ + close() { + this.db.close(); + } - /** - * Checks if a table with the given name exists in the database. - * @param name The name of the table. - * @returns `true` if the table exists, `false` otherwise. - */ - hasTable(name: string): boolean { - return Object.keys(this.tables).includes(name); - } + /** + * Checks if a table with the given name exists in the database. + * @param name The name of the table. + * @returns `true` if the table exists, `false` otherwise. + */ + hasTable(name: string): boolean { + return Object.keys(this.tables).includes(name); + } - /** - * Creates a new table in the database. - * @param name The name of the table. - * @param cols The column definitions of the table. - * @returns The created table. - * @throws {SchemaError} If a table with the same name already exists. - */ - table( - name: string, - cols: ColumnShorthandMap, - ): Table { - if (this.hasTable(name)) { - throw new SchemaError("Duplicate table name", name); - } + /** + * Creates a new table in the database. + * @param name The name of the table. + * @param cols The column definitions of the table. + * @returns The created table. + * @throws {SchemaError} If a table with the same name already exists. + */ + table( + name: string, + cols: ColumnShorthandMap, + ): Table { + if (this.hasTable(name)) { + throw new SchemaError("Duplicate table name", name); + } - const newTable = new Table(this, name, cols); - this.tables[name] = newTable; - return newTable; - } + const newTable = new Table(this, name, cols); + this.tables[name] = newTable; + return newTable; + } - /** - * Attaches an existing table to the database. - * @param table The table to attach. - * @throws {SchemaError} If a table with the same name already exists. - */ - attachTable(table: Table) { - if (this.hasTable(table.name)) { - throw new SchemaError("Duplicate table name", table.name); - } + /** + * Attaches an existing table to the database. + * @param table The table to attach. + * @throws {SchemaError} If a table with the same name already exists. + */ + attachTable(table: Table) { + if (this.hasTable(table.name)) { + throw new SchemaError("Duplicate table name", table.name); + } - this.tables[table.name] = table; - } - /** - * Executes a function within a database transaction. - * If the function throws an error, the transaction is rolled back. - * Otherwise, the transaction is committed. - * @param transactionFn The function to execute within the transaction. - * @returns `true` if the transaction was committed successfully, otherwise `false`. - * @throws The error thrown by the transaction function if the transaction fails. - */ - transaction(transactionFn: () => T): T { - try { - this.db.exec("BEGIN TRANSACTION;"); - const result: T = transactionFn(); - this.db.exec("COMMIT;"); - return result; - } catch (error) { - this.db.exec("ROLLBACK;"); - throw error; - } - } + this.tables[table.name] = table; + } + /** + * Executes a function within a database transaction. + * If the function throws an error, the transaction is rolled back. + * Otherwise, the transaction is committed. + * @param transactionFn The function to execute within the transaction. + * @returns `true` if the transaction was committed successfully, otherwise `false`. + * @throws The error thrown by the transaction function if the transaction fails. + */ + transaction(transactionFn: () => T): T { + try { + this.db.exec("BEGIN TRANSACTION;"); + const result: T = transactionFn(); + this.db.exec("COMMIT;"); + return result; + } catch (error) { + this.db.exec("ROLLBACK;"); + throw error; + } + } } diff --git a/src/table.ts b/src/table.ts index 3c38411..892f115 100644 --- a/src/table.ts +++ b/src/table.ts @@ -37,9 +37,15 @@ function shapeForQuery(obj: Partial): ArbitraryRow { for (const key of keys) { const val = obj[key as keyof T]; - if (typeof val !== "string" && typeof val !== "number" && val !== null) { + if ( + typeof val === "string" && + typeof val === "number" && + typeof val === "boolean" && + typeof val === "bigint" && + val !== null + ) { throw new SchemaError( - `Invalid value in query: ${key}: ${val} (type: ${typeof val})`, + `Invalid value in query: ${key} -> ${val} (type: ${typeof val})`, ); } @@ -97,7 +103,7 @@ export class Table { for (const columnName of this._columnNames) { const columnDefinition = this._columnDefinitions[ - columnName as keyof typeof this._columnDefinitions + columnName as keyof typeof this._columnDefinitions ]; // Identify a primary column besides ROWID if specified @@ -160,7 +166,8 @@ export class Table { } public primaryColumnType(): DataType { - const primary = this._primaryColumnName as keyof typeof this._columnDefinitions; + const primary = this + ._primaryColumnName as keyof typeof this._columnDefinitions; return this._columnDefinitions[primary].type; } diff --git a/test/prequel.test.ts b/test/prequel.test.ts index 164e3c3..d79b07a 100644 --- a/test/prequel.test.ts +++ b/test/prequel.test.ts @@ -3,143 +3,143 @@ import { SchemaError } from "../src/error"; import { Prequel, Table } from "../src/index"; interface User { - user_id: number; - name: string; + user_id: number; + name: string; } test("constructor", () => { - const pq = new Prequel(); - const users = pq.table("Users", { - user_id: { - type: "INTEGER", - primary: true, - }, - name: "TEXT", - }); + const pq = new Prequel(); + const users = pq.table("Users", { + user_id: { + type: "INTEGER", + primary: true, + }, + name: "TEXT", + }); - users.insertPartial({ name: "Yondu" }); + users.insertPartial({ name: "Yondu" }); - expect(users.findAll().length).toBe(1); + expect(users.findAll().length).toBe(1); }); test(".hasTable() knows if a table exists in the DB", () => { - const pq = new Prequel(); + const pq = new Prequel(); - expect(pq.hasTable("Users")).toBeFalse(); - expect(pq.hasTable("Notfound")).toBeFalse(); + expect(pq.hasTable("Users")).toBeFalse(); + expect(pq.hasTable("Notfound")).toBeFalse(); - pq.table("Users", { - user_id: { - type: "INTEGER", - primary: true, - }, - name: "TEXT", - }); + pq.table("Users", { + user_id: { + type: "INTEGER", + primary: true, + }, + name: "TEXT", + }); - expect(pq.hasTable("Users")).toBeTrue(); - expect(pq.hasTable("Notfound")).toBeFalse(); + expect(pq.hasTable("Users")).toBeTrue(); + expect(pq.hasTable("Notfound")).toBeFalse(); }); test(".table() creates and attaches a table", () => { - const pq = new Prequel(); + const pq = new Prequel(); - // Already has one table because of KV - expect(Object.keys(pq.tables)).toHaveLength(1); + // Already has one table because of KV + expect(Object.keys(pq.tables)).toHaveLength(1); - const table = pq.table("Users", { - user_id: { - type: "INTEGER", - primary: true, - }, - name: "TEXT", - }); + const table = pq.table("Users", { + user_id: { + type: "INTEGER", + primary: true, + }, + name: "TEXT", + }); - expect(Object.keys(pq.tables)).toHaveLength(2); - expect(Object.values(pq.tables)).toContain(table); + expect(Object.keys(pq.tables)).toHaveLength(2); + expect(Object.values(pq.tables)).toContain(table); }); test(".table() throws when creating a duplicate table", () => { - const pq = new Prequel(); - pq.table("Users", { - user_id: { - type: "INTEGER", - primary: true, - }, - name: "TEXT", - }); + const pq = new Prequel(); + pq.table("Users", { + user_id: { + type: "INTEGER", + primary: true, + }, + name: "TEXT", + }); - const oopsie = () => { - pq.table("Users", { - user_id: { - type: "INTEGER", - primary: true, - }, - name: "TEXT", - }); - }; + const oopsie = () => { + pq.table("Users", { + user_id: { + type: "INTEGER", + primary: true, + }, + name: "TEXT", + }); + }; - expect(oopsie).toThrow(SchemaError); + expect(oopsie).toThrow(SchemaError); }); test(".attachTable() attaches a table", () => { - const pq = new Prequel(); - const table = new Table(pq.db, "Users", { - user_id: { - type: "INTEGER", - primary: true, - }, - name: "TEXT", - }); + const pq = new Prequel(); + const table = new Table(pq.db, "Users", { + user_id: { + type: "INTEGER", + primary: true, + }, + name: "TEXT", + }); - expect(pq.hasTable("Users")).toBeFalse(); + expect(pq.hasTable("Users")).toBeFalse(); - pq.attachTable(table); + pq.attachTable(table); - expect(pq.hasTable("Users")).toBeTrue(); - expect(Object.values(pq.tables)).toContain(table); + expect(pq.hasTable("Users")).toBeTrue(); + expect(Object.values(pq.tables)).toContain(table); }); test(".attachTable() throws on duplicate table names", () => { - const pq = new Prequel(); - pq.table("Users", { - user_id: { - type: "INTEGER", - primary: true, - }, - name: "TEXT", - }); + const pq = new Prequel(); + pq.table("Users", { + user_id: { + type: "INTEGER", + primary: true, + }, + name: "TEXT", + }); - const secondTable = new Table(pq.db, "Users", { - user_id: { - type: "INTEGER", - primary: true, - }, - name: "TEXT", - }); + const secondTable = new Table(pq.db, "Users", { + user_id: { + type: "INTEGER", + primary: true, + }, + name: "TEXT", + }); - const doAttachment = () => { - pq.attachTable(secondTable); - }; + const doAttachment = () => { + pq.attachTable(secondTable); + }; - expect(doAttachment).toThrow(SchemaError); + expect(doAttachment).toThrow(SchemaError); }); test("kv store can set and retrieve arbitrary values", () => { - const pq = new Prequel(); + const pq = new Prequel(); - pq.kv.put("answer", 42); - pq.kv.put<{ enabled: boolean }>("config", { enabled: true }); - pq.kv.put("list", ["a", "b", "c"]); + pq.kv.put("answer", 42); + pq.kv.put<{ enabled: boolean }>("config", { enabled: true }); + pq.kv.put("list", ["a", "b", "c"]); - expect(pq.kv.get("answer")).toEqual(42); - expect(pq.kv.get<{ enabled: boolean }>("config")).toEqual({ enabled: true }); - expect(pq.kv.get("list")).toEqual(["a", "b", "c"]); + expect(pq.kv.get("answer")).toEqual(42); + expect(pq.kv.get<{ enabled: boolean }>("config")).toEqual({ enabled: true }); + expect(pq.kv.get("list")).toEqual(["a", "b", "c"]); }); test(".query() creates a cached statement", () => { - const pq = new Prequel(); - const query = pq.query("SELECT 1"); + const pq = new Prequel(); + const query = pq.query("SELECT 1"); - expect(query).toBeDefined(); - expect(query).toBe(pq.query("SELECT 1")); + expect(query).toBeDefined(); + expect(query).toBe(pq.query("SELECT 1")); }); diff --git a/test/table.test.ts b/test/table.test.ts index f3c4a69..c555d07 100644 --- a/test/table.test.ts +++ b/test/table.test.ts @@ -3,414 +3,414 @@ import { expect, test } from "bun:test"; import { Prequel, Table } from "../src/index"; interface UserWithID { - user_id: number; - name: string; + user_id: number; + name: string; } interface UserWithBio { - user_id: number; - bio: { - name: string; - age: number; - }; + user_id: number; + bio: { + name: string; + age: number; + }; } interface Post { - post_id: number; - user_id: number; + post_id: number; + user_id: number; } interface UserWithNullable extends UserWithID { - bio: string | null; + bio: string | null; } function makeTestTable(): Table { - const db = new Database(); - return new Table(db, "Users", { - user_id: { - type: "INTEGER", - primary: true, - }, - name: "TEXT", - }); + const db = new Database(); + return new Table(db, "Users", { + user_id: { + type: "INTEGER", + primary: true, + }, + name: "TEXT", + }); } test("constructor", () => { - const db = new Database(); - const table = new Table(db, "Users", { - user_id: { - type: "INTEGER", - primary: true, - }, - name: "TEXT", - }); + const db = new Database(); + const table = new Table(db, "Users", { + user_id: { + type: "INTEGER", + primary: true, + }, + name: "TEXT", + }); - expect(table.name).toEqual("Users"); - expect(table.db).toBe(db); - expect(table.columns).toEqual(["user_id", "name"]); + expect(table.name).toEqual("Users"); + expect(table.db).toBe(db); + expect(table.columns).toEqual(["user_id", "name"]); }); test("constructor -> auto-attach to a prequel isntance", () => { - const db = new Prequel(); - const table = new Table(db, "Users", { - user_id: { - type: "INTEGER", - primary: true, - }, - name: "TEXT", - }); + const db = new Prequel(); + const table = new Table(db, "Users", { + user_id: { + type: "INTEGER", + primary: true, + }, + name: "TEXT", + }); - expect(Object.values(db.tables)).toContain(table); + expect(Object.values(db.tables)).toContain(table); }); test(".toString() resolves to the table name for embedding in queries", () => { - const table = makeTestTable(); - expect(`${table}`).toEqual('"Users"'); + const table = makeTestTable(); + expect(`${table}`).toEqual('"Users"'); }); test(".reference() creates a default reference to the primary column", () => { - const table = makeTestTable(); - expect(table.reference()).toEqual("Users(user_id)"); + const table = makeTestTable(); + expect(table.reference()).toEqual("Users(user_id)"); }); test(".reference() creates a reference to the specified column", () => { - const table = makeTestTable(); - expect(table.reference("name")).toEqual("Users(name)"); + const table = makeTestTable(); + expect(table.reference("name")).toEqual("Users(name)"); }); test(".insert() inserts a full row", () => { - const table = makeTestTable(); - table.insert({ - user_id: 100, - name: "Jack", - }); + const table = makeTestTable(); + table.insert({ + user_id: 100, + name: "Jack", + }); - const lookup = table.db.prepare( - `SELECT * FROM ${table} WHERE user_id = ?`, - ); - const found = lookup.get(100); - expect(found).toEqual({ - user_id: 100, - name: "Jack", - }); + const lookup = table.db.prepare( + `SELECT * FROM ${table} WHERE user_id = ?`, + ); + const found = lookup.get(100); + expect(found).toEqual({ + user_id: 100, + name: "Jack", + }); }); test(".insert() is able to insert nulls to nullable fields", () => { - const table = new Table(new Database(), "Users", { - user_id: { - type: "INTEGER", - primary: true, - }, - name: "TEXT", - bio: { - type: "TEXT", - nullable: true, - }, - }); + const table = new Table(new Database(), "Users", { + user_id: { + type: "INTEGER", + primary: true, + }, + name: "TEXT", + bio: { + type: "TEXT", + nullable: true, + }, + }); - table.insert({ - user_id: 1, - name: "Jack", - bio: null, - }); + table.insert({ + user_id: 1, + name: "Jack", + bio: null, + }); - const lookup = table.db.prepare( - `SELECT * FROM ${table} WHERE user_id = ?`, - ); - const found = lookup.get(1); + const lookup = table.db.prepare( + `SELECT * FROM ${table} WHERE user_id = ?`, + ); + const found = lookup.get(1); - expect(found?.bio).toBeNull(); + expect(found?.bio).toBeNull(); }); test(".insertPartial() inserts a partial row", () => { - const table = makeTestTable(); - table.insertPartial({ - name: "Jack", - }); + const table = makeTestTable(); + table.insertPartial({ + name: "Jack", + }); - const lookup = table.db.prepare( - `SELECT * FROM ${table} WHERE name = ?`, - ); - const found = lookup.get("Jack"); - expect(found).toEqual({ - user_id: 1, - name: "Jack", - }); + const lookup = table.db.prepare( + `SELECT * FROM ${table} WHERE name = ?`, + ); + const found = lookup.get("Jack"); + expect(found).toEqual({ + user_id: 1, + name: "Jack", + }); }); test(".insertWithout() inserts a partial row with type safety", () => { - const table = makeTestTable(); - table.insertWithout<"user_id">({ - name: "Jack", - }); + const table = makeTestTable(); + table.insertWithout<"user_id">({ + name: "Jack", + }); - const lookup = table.db.prepare( - `SELECT * FROM ${table} WHERE name = ?`, - ); - const found = lookup.get("Jack"); - expect(found).toEqual({ - user_id: 1, - name: "Jack", - }); + const lookup = table.db.prepare( + `SELECT * FROM ${table} WHERE name = ?`, + ); + const found = lookup.get("Jack"); + expect(found).toEqual({ + user_id: 1, + name: "Jack", + }); }); test(".count() gets the current number of rows in the table", () => { - const table = makeTestTable(); - table.insertPartial({ name: "Jack" }); - table.insertPartial({ name: "Kate" }); - table.insertPartial({ name: "Sayid" }); - expect(table.count()).toBe(3); + const table = makeTestTable(); + table.insertPartial({ name: "Jack" }); + table.insertPartial({ name: "Kate" }); + table.insertPartial({ name: "Sayid" }); + expect(table.count()).toBe(3); }); test(".countWhere() gets the current number of rows that match a condition", () => { - const table = makeTestTable(); - table.insertPartial({ name: "Jack" }); - table.insertPartial({ name: "Kate" }); - table.insertPartial({ name: "Sayid" }); - expect(table.countWhere({ name: "Jack" })).toBe(1); + const table = makeTestTable(); + table.insertPartial({ name: "Jack" }); + table.insertPartial({ name: "Kate" }); + table.insertPartial({ name: "Sayid" }); + expect(table.countWhere({ name: "Jack" })).toBe(1); }); test(".findAll() returns the full table", () => { - const table = makeTestTable(); - table.insertPartial({ name: "Jack" }); - table.insertPartial({ name: "Kate" }); - table.insertPartial({ name: "Sayid" }); - const allUsers = table.findAll(); + const table = makeTestTable(); + table.insertPartial({ name: "Jack" }); + table.insertPartial({ name: "Kate" }); + table.insertPartial({ name: "Sayid" }); + const allUsers = table.findAll(); - expect(allUsers).toHaveLength(3); - expect(allUsers[0]).toEqual({ user_id: 1, name: "Jack" }); - expect(allUsers[1]).toEqual({ user_id: 2, name: "Kate" }); - expect(allUsers[2]).toEqual({ user_id: 3, name: "Sayid" }); + expect(allUsers).toHaveLength(3); + expect(allUsers[0]).toEqual({ user_id: 1, name: "Jack" }); + expect(allUsers[1]).toEqual({ user_id: 2, name: "Kate" }); + expect(allUsers[2]).toEqual({ user_id: 3, name: "Sayid" }); }); test(".query() returns a prepared statement for the table", () => { - const table = makeTestTable(); - const query = table.query("SELECT * FROM Users WHERE name = ?"); - expect(query).toBeDefined(); + const table = makeTestTable(); + const query = table.query("SELECT * FROM Users WHERE name = ?"); + expect(query).toBeDefined(); }); test(".findOneWhere() returns a row based on basic criteria", () => { - const table = makeTestTable(); - table.insertPartial({ name: "Jack" }); - table.insertPartial({ name: "Kate" }); - table.insertPartial({ name: "Sayid" }); + const table = makeTestTable(); + table.insertPartial({ name: "Jack" }); + table.insertPartial({ name: "Kate" }); + table.insertPartial({ name: "Sayid" }); - const sayid = table.findOneWhere({ name: "Sayid" }); + const sayid = table.findOneWhere({ name: "Sayid" }); - expect(sayid).toEqual({ user_id: 3, name: "Sayid" }); + expect(sayid).toEqual({ user_id: 3, name: "Sayid" }); }); test(".findOneWhereOrFail() throws if it finds nothing", () => { - const table = makeTestTable(); + const table = makeTestTable(); - expect(() => { - table.findOneWhereOrFail({ name: "Sayid" }); - }).toThrow(); + expect(() => { + table.findOneWhereOrFail({ name: "Sayid" }); + }).toThrow(); }); test(".findAllWhere() returns a row based on basic criteria", () => { - const table = makeTestTable(); - table.insertPartial({ name: "Jack" }); - table.insertPartial({ name: "Jack" }); - table.insertPartial({ name: "Jack" }); + const table = makeTestTable(); + table.insertPartial({ name: "Jack" }); + table.insertPartial({ name: "Jack" }); + table.insertPartial({ name: "Jack" }); - const manyJacks = table.findAllWhere({ name: "Jack" }); + const manyJacks = table.findAllWhere({ name: "Jack" }); - expect(manyJacks).toHaveLength(3); - expect(manyJacks[0]).toEqual({ user_id: 1, name: "Jack" }); - expect(manyJacks[1]).toEqual({ user_id: 2, name: "Jack" }); - expect(manyJacks[2]).toEqual({ user_id: 3, name: "Jack" }); + expect(manyJacks).toHaveLength(3); + expect(manyJacks[0]).toEqual({ user_id: 1, name: "Jack" }); + expect(manyJacks[1]).toEqual({ user_id: 2, name: "Jack" }); + expect(manyJacks[2]).toEqual({ user_id: 3, name: "Jack" }); }); test(".findOneById() returns a single element by its primary column", () => { - const table = makeTestTable(); + const table = makeTestTable(); - table.insertPartial({ name: "Jack" }); - table.insertPartial({ name: "Kate" }); - table.insertPartial({ name: "Sayid" }); + table.insertPartial({ name: "Jack" }); + table.insertPartial({ name: "Kate" }); + table.insertPartial({ name: "Sayid" }); - const sayid = table.findOneById(3); - expect(sayid).toEqual({ user_id: 3, name: "Sayid" }); + const sayid = table.findOneById(3); + expect(sayid).toEqual({ user_id: 3, name: "Sayid" }); }); test(".findOneById() returns null when nothing was found", () => { - const table = makeTestTable(); - const nobody = table.findOneById(3); - expect(nobody).toBeNull(); + const table = makeTestTable(); + const nobody = table.findOneById(3); + expect(nobody).toBeNull(); }); test(".findOneByIdOrFail() returns a single element by its primary column", () => { - const table = makeTestTable(); + const table = makeTestTable(); - table.insertPartial({ name: "Jack" }); - table.insertPartial({ name: "Kate" }); - table.insertPartial({ name: "Sayid" }); + table.insertPartial({ name: "Jack" }); + table.insertPartial({ name: "Kate" }); + table.insertPartial({ name: "Sayid" }); - const sayid = table.findOneById(3); - expect(sayid).toEqual({ user_id: 3, name: "Sayid" }); + const sayid = table.findOneById(3); + expect(sayid).toEqual({ user_id: 3, name: "Sayid" }); }); test(".findOneByIdOrFail() throws an error when nothing is found", () => { - const table = makeTestTable(); - expect(() => { - table.findOneByIdOrFail(3); - }).toThrow(); + const table = makeTestTable(); + expect(() => { + table.findOneByIdOrFail(3); + }).toThrow(); }); test(".delete() deletes a full record", () => { - const table = makeTestTable(); - table.insertPartial({ name: "Jack" }); - table.insertPartial({ name: "Kate" }); - table.insertPartial({ name: "Sayid" }); + const table = makeTestTable(); + table.insertPartial({ name: "Jack" }); + table.insertPartial({ name: "Kate" }); + table.insertPartial({ name: "Sayid" }); - expect(table.size()).toBe(3); + expect(table.size()).toBe(3); - table.delete(table.findOneByIdOrFail(2)); + table.delete(table.findOneByIdOrFail(2)); - expect(table.size()).toBe(2); + expect(table.size()).toBe(2); - expect(table.findAll()).toEqual([ - { user_id: 1, name: "Jack" }, - { user_id: 3, name: "Sayid" }, - ]); + expect(table.findAll()).toEqual([ + { user_id: 1, name: "Jack" }, + { user_id: 3, name: "Sayid" }, + ]); }); test(".deleteById() deletes the element with the given primary value", () => { - const table = makeTestTable(); - table.insertPartial({ name: "Jack" }); - table.insertPartial({ name: "Kate" }); - table.insertPartial({ name: "Sayid" }); + const table = makeTestTable(); + table.insertPartial({ name: "Jack" }); + table.insertPartial({ name: "Kate" }); + table.insertPartial({ name: "Sayid" }); - expect(table.size()).toBe(3); + expect(table.size()).toBe(3); - table.deleteById(2); + table.deleteById(2); - expect(table.size()).toBe(2); + expect(table.size()).toBe(2); - expect(table.findAll()).toEqual([ - { user_id: 1, name: "Jack" }, - { user_id: 3, name: "Sayid" }, - ]); + expect(table.findAll()).toEqual([ + { user_id: 1, name: "Jack" }, + { user_id: 3, name: "Sayid" }, + ]); }); test(".deleteWhere() deletes all rows that match", () => { - const table = makeTestTable(); - table.insertPartial({ name: "Jack" }); - table.insertPartial({ name: "Jack" }); - table.insertPartial({ name: "Sayid" }); + const table = makeTestTable(); + table.insertPartial({ name: "Jack" }); + table.insertPartial({ name: "Jack" }); + table.insertPartial({ name: "Sayid" }); - expect(table.size()).toBe(3); + expect(table.size()).toBe(3); - table.deleteWhere({ name: "Jack" }); + table.deleteWhere({ name: "Jack" }); - expect(table.size()).toBe(1); + expect(table.size()).toBe(1); - expect(table.findAll()).toEqual([{ user_id: 3, name: "Sayid" }]); + expect(table.findAll()).toEqual([{ user_id: 3, name: "Sayid" }]); }); test(".deleteById() respects when references are set to cascade", () => { - const pq = new Prequel(); - const users = pq.table("Users", { - user_id: { - type: "INTEGER", - primary: true, - }, - name: "TEXT", - }); + const pq = new Prequel(); + const users = pq.table("Users", { + user_id: { + type: "INTEGER", + primary: true, + }, + name: "TEXT", + }); - const posts = pq.table("Posts", { - post_id: { - type: "INTEGER", - primary: true, - }, - user_id: { - type: "INTEGER", - references: users.reference(), - cascade: true, - }, - }); + const posts = pq.table("Posts", { + post_id: { + type: "INTEGER", + primary: true, + }, + user_id: { + type: "INTEGER", + references: users.reference(), + cascade: true, + }, + }); - const user = users.insertPartial({ name: "Bob" }); - posts.insertPartial({ - user_id: user.user_id, - }); + const user = users.insertPartial({ name: "Bob" }); + posts.insertPartial({ + user_id: user.user_id, + }); - users.deleteById(user.user_id); - expect(posts.size()).toBe(0); + users.deleteById(user.user_id); + expect(posts.size()).toBe(0); }); test(".deleteAll() deletes all rows in the table", () => { - const table = makeTestTable(); - table.insertPartial({ name: "Jack" }); - table.insertPartial({ name: "Kate" }); - table.insertPartial({ name: "Sayid" }); + const table = makeTestTable(); + table.insertPartial({ name: "Jack" }); + table.insertPartial({ name: "Kate" }); + table.insertPartial({ name: "Sayid" }); - expect(table.count()).toBe(3); + expect(table.count()).toBe(3); - table.deleteAll(); + table.deleteAll(); - expect(table.count()).toBe(0); + expect(table.count()).toBe(0); }); test(".update() updates a full record", () => { - const table = makeTestTable(); - const locke = table.insertPartial({ name: "Locke" }); - table.insertPartial({ name: "Kate" }); - table.insertPartial({ name: "Sayid" }); + const table = makeTestTable(); + const locke = table.insertPartial({ name: "Locke" }); + table.insertPartial({ name: "Kate" }); + table.insertPartial({ name: "Sayid" }); - locke.name = "Jeremy Bentham"; - table.update(locke); + locke.name = "Jeremy Bentham"; + table.update(locke); - const newLocke = table.findOneById(1); - expect(newLocke?.name).toEqual("Jeremy Bentham"); + const newLocke = table.findOneById(1); + expect(newLocke?.name).toEqual("Jeremy Bentham"); }); test(".update() throws if no primary column is set besides ROWID", () => { - const db = new Database(); - const table = new Table(db, "Users", { - user_id: "INTEGER", - name: "TEXT", - }); + const db = new Database(); + const table = new Table(db, "Users", { + user_id: "INTEGER", + name: "TEXT", + }); - const user = table.insert({ user_id: 1, name: "User" }); + const user = table.insert({ user_id: 1, name: "User" }); - const oops = () => { - table.update(user); - }; + const oops = () => { + table.update(user); + }; - expect(oops).toThrow(/primary column/); + expect(oops).toThrow(/primary column/); }); test(".updateWhere() updates rows based on the condition", () => { - const db = new Database(); - const table = new Table(db, "Users", { - user_id: "INTEGER", - name: "TEXT", - }); + const db = new Database(); + const table = new Table(db, "Users", { + user_id: "INTEGER", + name: "TEXT", + }); - table.insert({ user_id: 1, name: "Jack" }); - table.insert({ user_id: 2, name: "Kate" }); - table.insert({ user_id: 3, name: "Sayid" }); + table.insert({ user_id: 1, name: "Jack" }); + table.insert({ user_id: 2, name: "Kate" }); + table.insert({ user_id: 3, name: "Sayid" }); - table.updateWhere({ name: "Sawyer" }, { user_id: 2 }); - expect(table.findOneById(2)?.name).toEqual("Sawyer"); + table.updateWhere({ name: "Sawyer" }, { user_id: 2 }); + expect(table.findOneById(2)?.name).toEqual("Sawyer"); }); test(".updateAll() updates all rows in the table", () => { - const db = new Database(); - const table = new Table(db, "Users", { - user_id: "INTEGER", - name: "TEXT", - }); + const db = new Database(); + const table = new Table(db, "Users", { + user_id: "INTEGER", + name: "TEXT", + }); - table.insert({ user_id: 1, name: "Jack" }); - table.insert({ user_id: 2, name: "Kate" }); - table.insert({ user_id: 3, name: "Sayid" }); + table.insert({ user_id: 1, name: "Jack" }); + table.insert({ user_id: 2, name: "Kate" }); + table.insert({ user_id: 3, name: "Sayid" }); - table.updateAll({ name: "Sawyer" }); - expect(table.findAll().map((u) => u.name)).toEqual([ - "Sawyer", - "Sawyer", - "Sawyer", - ]); + table.updateAll({ name: "Sawyer" }); + expect(table.findAll().map((u) => u.name)).toEqual([ + "Sawyer", + "Sawyer", + "Sawyer", + ]); });