From 1bfc20673d24e4d6d7f7f7be3ab33fb3a0ce45d4 Mon Sep 17 00:00:00 2001 From: Endeavorance Date: Mon, 7 Apr 2025 15:24:31 -0400 Subject: [PATCH] Add column presets --- package.json | 2 +- src/{column-types.ts => column-presets.ts} | 23 ++++++++++++++ src/index.ts | 2 +- src/prequel.ts | 4 +-- src/table.ts | 37 +++++++++------------- test/table.test.ts | 16 +++++----- 6 files changed, 50 insertions(+), 34 deletions(-) rename src/{column-types.ts => column-presets.ts} (85%) diff --git a/package.json b/package.json index 694bc37..67ff6ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@endeavorance/prequel", - "version": "1.4.0", + "version": "2.0.0", "exports": "./dist/index.js", "types": "./dist/index.d.ts", "scripts": { diff --git a/src/column-types.ts b/src/column-presets.ts similarity index 85% rename from src/column-types.ts rename to src/column-presets.ts index 4a0f3ed..ebfbebd 100644 --- a/src/column-types.ts +++ b/src/column-presets.ts @@ -23,6 +23,11 @@ export const ColumnOf = { nullable: false, }, + Blob: { + type: "BLOB", + nullable: false, + }, + ForeignKey(otherTable: Table, cascade = false): ColumnShorthand { return { type: otherTable.primaryColumnType(), @@ -48,6 +53,11 @@ export const ColumnOf = { nullable: true, }, + Blob: { + type: "BLOB", + nullable: true, + }, + ForeignKey(otherTable: Table, cascade = false): ColumnShorthand { return { type: otherTable.primaryColumnType(), @@ -77,6 +87,12 @@ export const ColumnOf = { unique: true, }, + Blob: { + type: "BLOB", + nullable: false, + unique: true, + }, + ForeignKey(otherTable: Table, cascade = false): ColumnShorthand { return { type: otherTable.primaryColumnType(), @@ -109,5 +125,12 @@ export const ColumnOf = { default: defaultValue, } as const; }, + + Blob(defaultValue: string): ColumnShorthand { + return { + type: "BLOB", + default: defaultValue, + } as const; + }, }, } as const; diff --git a/src/index.ts b/src/index.ts index 2aa8b5b..ec4937d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { ColumnOf } from "./column-types"; +import { ColumnOf } from "./column-presets"; import type { Column, ColumnShorthand, DataType } from "./columns"; import { Prequel } from "./prequel"; import { Table } from "./table"; diff --git a/src/prequel.ts b/src/prequel.ts index 4280a0a..0018eb4 100644 --- a/src/prequel.ts +++ b/src/prequel.ts @@ -106,7 +106,7 @@ export class Prequel { public kv: PrequelKVStore; public tables: Record> = {}; public query: typeof Database.prototype.query; - public uncachedQuery: typeof Database.prototype.prepare; + public prepare: typeof Database.prototype.prepare; /** * Creates a new Prequel database instance. @@ -115,7 +115,7 @@ export class Prequel { 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.prepare = this.db.prepare.bind(this.db); this.db.exec("PRAGMA foreign_keys=ON"); this.kv = new PrequelKVStore(this, "PrequelManagedKVStore"); } diff --git a/src/table.ts b/src/table.ts index 892f115..a8593b1 100644 --- a/src/table.ts +++ b/src/table.ts @@ -12,7 +12,17 @@ import { Prequel } from "./prequel"; export type ExtractRowShape = T extends Table ? R : never; /** Types that may appear in a row */ -type ColumnValue = string | number | null; +type ColumnValue = string | number | boolean | bigint | null; + +function isColumnValue(val: unknown): val is ColumnValue { + return ( + typeof val === "string" || + typeof val === "number" || + typeof val === "boolean" || + typeof val === "bigint" || + val === null + ); +} export class PrequelIDNotFoundError extends Error { constructor(tableName: string, id: ColumnValue) { @@ -37,13 +47,7 @@ function shapeForQuery(obj: Partial): ArbitraryRow { for (const key of keys) { const val = obj[key as keyof T]; - if ( - typeof val === "string" && - typeof val === "number" && - typeof val === "boolean" && - typeof val === "bigint" && - val !== null - ) { + if (val !== undefined && !isColumnValue(val)) { throw new SchemaError( `Invalid value in query: ${key} -> ${val} (type: ${typeof val})`, ); @@ -68,6 +72,7 @@ export class Table { private _columnNames: string[]; private _createTableSQL: string; private _primaryColumnName = "ROWID"; + private _primaryColumnType: DataType | null = null; private _insertQuery: Statement; private _updateQuery: Statement; private _findQuery: Statement; @@ -109,6 +114,7 @@ export class Table { // Identify a primary column besides ROWID if specified if (columnDefinition.primary && this._primaryColumnName === "ROWID") { this._primaryColumnName = columnName; + this._primaryColumnType = columnDefinition.type; } // Add non-primary columns to the generic update query @@ -166,9 +172,7 @@ export class Table { } public primaryColumnType(): DataType { - const primary = this - ._primaryColumnName as keyof typeof this._columnDefinitions; - return this._columnDefinitions[primary].type; + return this._primaryColumnType ?? "INTEGER"; } /** @@ -488,17 +492,6 @@ export class Table { this._truncateQuery.run(); } - /** - * Retrieves the size of the table. - * - * @deprecated Use `count()` instead. - * @returns {number} The number of rows in the table. - * @throws {Error} If the row count query fails. - */ - public size(): number { - return this.count(); - } - /** * Retrieves the size of the table. * diff --git a/test/table.test.ts b/test/table.test.ts index c555d07..dfe8bce 100644 --- a/test/table.test.ts +++ b/test/table.test.ts @@ -1,6 +1,6 @@ import { Database } from "bun:sqlite"; import { expect, test } from "bun:test"; -import { Prequel, Table } from "../src/index"; +import { ColumnOf, Prequel, Table } from "../src/index"; interface UserWithID { user_id: number; @@ -263,11 +263,11 @@ test(".delete() deletes a full record", () => { table.insertPartial({ name: "Kate" }); table.insertPartial({ name: "Sayid" }); - expect(table.size()).toBe(3); + expect(table.count()).toBe(3); table.delete(table.findOneByIdOrFail(2)); - expect(table.size()).toBe(2); + expect(table.count()).toBe(2); expect(table.findAll()).toEqual([ { user_id: 1, name: "Jack" }, @@ -281,11 +281,11 @@ test(".deleteById() deletes the element with the given primary value", () => { table.insertPartial({ name: "Kate" }); table.insertPartial({ name: "Sayid" }); - expect(table.size()).toBe(3); + expect(table.count()).toBe(3); table.deleteById(2); - expect(table.size()).toBe(2); + expect(table.count()).toBe(2); expect(table.findAll()).toEqual([ { user_id: 1, name: "Jack" }, @@ -299,11 +299,11 @@ test(".deleteWhere() deletes all rows that match", () => { table.insertPartial({ name: "Jack" }); table.insertPartial({ name: "Sayid" }); - expect(table.size()).toBe(3); + expect(table.count()).toBe(3); table.deleteWhere({ name: "Jack" }); - expect(table.size()).toBe(1); + expect(table.count()).toBe(1); expect(table.findAll()).toEqual([{ user_id: 3, name: "Sayid" }]); }); @@ -336,7 +336,7 @@ test(".deleteById() respects when references are set to cascade", () => { }); users.deleteById(user.user_id); - expect(posts.size()).toBe(0); + expect(posts.count()).toBe(0); }); test(".deleteAll() deletes all rows in the table", () => {