Add column presets

This commit is contained in:
Endeavorance 2025-04-07 15:24:31 -04:00
parent 6479d09196
commit 1bfc20673d
6 changed files with 50 additions and 34 deletions

View file

@ -1,6 +1,6 @@
{ {
"name": "@endeavorance/prequel", "name": "@endeavorance/prequel",
"version": "1.4.0", "version": "2.0.0",
"exports": "./dist/index.js", "exports": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"scripts": { "scripts": {

View file

@ -23,6 +23,11 @@ export const ColumnOf = {
nullable: false, nullable: false,
}, },
Blob: {
type: "BLOB",
nullable: false,
},
ForeignKey(otherTable: Table<unknown>, cascade = false): ColumnShorthand { ForeignKey(otherTable: Table<unknown>, cascade = false): ColumnShorthand {
return { return {
type: otherTable.primaryColumnType(), type: otherTable.primaryColumnType(),
@ -48,6 +53,11 @@ export const ColumnOf = {
nullable: true, nullable: true,
}, },
Blob: {
type: "BLOB",
nullable: true,
},
ForeignKey(otherTable: Table<unknown>, cascade = false): ColumnShorthand { ForeignKey(otherTable: Table<unknown>, cascade = false): ColumnShorthand {
return { return {
type: otherTable.primaryColumnType(), type: otherTable.primaryColumnType(),
@ -77,6 +87,12 @@ export const ColumnOf = {
unique: true, unique: true,
}, },
Blob: {
type: "BLOB",
nullable: false,
unique: true,
},
ForeignKey(otherTable: Table<unknown>, cascade = false): ColumnShorthand { ForeignKey(otherTable: Table<unknown>, cascade = false): ColumnShorthand {
return { return {
type: otherTable.primaryColumnType(), type: otherTable.primaryColumnType(),
@ -109,5 +125,12 @@ export const ColumnOf = {
default: defaultValue, default: defaultValue,
} as const; } as const;
}, },
Blob(defaultValue: string): ColumnShorthand {
return {
type: "BLOB",
default: defaultValue,
} as const;
},
}, },
} as const; } as const;

View file

@ -1,4 +1,4 @@
import { ColumnOf } from "./column-types"; import { ColumnOf } from "./column-presets";
import type { Column, ColumnShorthand, DataType } from "./columns"; import type { Column, ColumnShorthand, DataType } from "./columns";
import { Prequel } from "./prequel"; import { Prequel } from "./prequel";
import { Table } from "./table"; import { Table } from "./table";

View file

@ -106,7 +106,7 @@ export class Prequel {
public kv: PrequelKVStore; public kv: PrequelKVStore;
public tables: Record<string, Table<unknown>> = {}; public tables: Record<string, Table<unknown>> = {};
public query: typeof Database.prototype.query; public query: typeof Database.prototype.query;
public uncachedQuery: typeof Database.prototype.prepare; public prepare: typeof Database.prototype.prepare;
/** /**
* Creates a new Prequel database instance. * Creates a new Prequel database instance.
@ -115,7 +115,7 @@ export class Prequel {
constructor(filename = ":memory:") { constructor(filename = ":memory:") {
this.db = new Database(filename); this.db = new Database(filename);
this.query = this.db.query.bind(this.db); 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.db.exec("PRAGMA foreign_keys=ON");
this.kv = new PrequelKVStore(this, "PrequelManagedKVStore"); this.kv = new PrequelKVStore(this, "PrequelManagedKVStore");
} }

View file

@ -12,7 +12,17 @@ import { Prequel } from "./prequel";
export type ExtractRowShape<T> = T extends Table<infer R> ? R : never; export type ExtractRowShape<T> = T extends Table<infer R> ? R : never;
/** Types that may appear in a row */ /** 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 { export class PrequelIDNotFoundError extends Error {
constructor(tableName: string, id: ColumnValue) { constructor(tableName: string, id: ColumnValue) {
@ -37,13 +47,7 @@ function shapeForQuery<T>(obj: Partial<T>): ArbitraryRow {
for (const key of keys) { for (const key of keys) {
const val = obj[key as keyof T]; const val = obj[key as keyof T];
if ( if (val !== undefined && !isColumnValue(val)) {
typeof val === "string" &&
typeof val === "number" &&
typeof val === "boolean" &&
typeof val === "bigint" &&
val !== null
) {
throw new SchemaError( throw new SchemaError(
`Invalid value in query: ${key} -> ${val} (type: ${typeof val})`, `Invalid value in query: ${key} -> ${val} (type: ${typeof val})`,
); );
@ -68,6 +72,7 @@ export class Table<RowShape> {
private _columnNames: string[]; private _columnNames: string[];
private _createTableSQL: string; private _createTableSQL: string;
private _primaryColumnName = "ROWID"; private _primaryColumnName = "ROWID";
private _primaryColumnType: DataType | null = null;
private _insertQuery: Statement<RowShape, [ArbitraryRow]>; private _insertQuery: Statement<RowShape, [ArbitraryRow]>;
private _updateQuery: Statement<RowShape, [ArbitraryRow]>; private _updateQuery: Statement<RowShape, [ArbitraryRow]>;
private _findQuery: Statement<RowShape, [string]>; private _findQuery: Statement<RowShape, [string]>;
@ -109,6 +114,7 @@ export class Table<RowShape> {
// Identify a primary column besides ROWID if specified // Identify a primary column besides ROWID if specified
if (columnDefinition.primary && this._primaryColumnName === "ROWID") { if (columnDefinition.primary && this._primaryColumnName === "ROWID") {
this._primaryColumnName = columnName; this._primaryColumnName = columnName;
this._primaryColumnType = columnDefinition.type;
} }
// Add non-primary columns to the generic update query // Add non-primary columns to the generic update query
@ -166,9 +172,7 @@ export class Table<RowShape> {
} }
public primaryColumnType(): DataType { public primaryColumnType(): DataType {
const primary = this return this._primaryColumnType ?? "INTEGER";
._primaryColumnName as keyof typeof this._columnDefinitions;
return this._columnDefinitions[primary].type;
} }
/** /**
@ -488,17 +492,6 @@ export class Table<RowShape> {
this._truncateQuery.run(); 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. * Retrieves the size of the table.
* *

View file

@ -1,6 +1,6 @@
import { Database } from "bun:sqlite"; import { Database } from "bun:sqlite";
import { expect, test } from "bun:test"; import { expect, test } from "bun:test";
import { Prequel, Table } from "../src/index"; import { ColumnOf, Prequel, Table } from "../src/index";
interface UserWithID { interface UserWithID {
user_id: number; user_id: number;
@ -263,11 +263,11 @@ test(".delete() deletes a full record", () => {
table.insertPartial({ name: "Kate" }); table.insertPartial({ name: "Kate" });
table.insertPartial({ name: "Sayid" }); table.insertPartial({ name: "Sayid" });
expect(table.size()).toBe(3); expect(table.count()).toBe(3);
table.delete(table.findOneByIdOrFail(2)); table.delete(table.findOneByIdOrFail(2));
expect(table.size()).toBe(2); expect(table.count()).toBe(2);
expect(table.findAll()).toEqual([ expect(table.findAll()).toEqual([
{ user_id: 1, name: "Jack" }, { 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: "Kate" });
table.insertPartial({ name: "Sayid" }); table.insertPartial({ name: "Sayid" });
expect(table.size()).toBe(3); expect(table.count()).toBe(3);
table.deleteById(2); table.deleteById(2);
expect(table.size()).toBe(2); expect(table.count()).toBe(2);
expect(table.findAll()).toEqual([ expect(table.findAll()).toEqual([
{ user_id: 1, name: "Jack" }, { user_id: 1, name: "Jack" },
@ -299,11 +299,11 @@ test(".deleteWhere() deletes all rows that match", () => {
table.insertPartial({ name: "Jack" }); table.insertPartial({ name: "Jack" });
table.insertPartial({ name: "Sayid" }); table.insertPartial({ name: "Sayid" });
expect(table.size()).toBe(3); expect(table.count()).toBe(3);
table.deleteWhere({ name: "Jack" }); table.deleteWhere({ name: "Jack" });
expect(table.size()).toBe(1); expect(table.count()).toBe(1);
expect(table.findAll()).toEqual([{ user_id: 3, name: "Sayid" }]); 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); users.deleteById(user.user_id);
expect(posts.size()).toBe(0); expect(posts.count()).toBe(0);
}); });
test(".deleteAll() deletes all rows in the table", () => { test(".deleteAll() deletes all rows in the table", () => {