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",
"version": "1.4.0",
"version": "2.0.0",
"exports": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {

View file

@ -23,6 +23,11 @@ export const ColumnOf = {
nullable: false,
},
Blob: {
type: "BLOB",
nullable: false,
},
ForeignKey(otherTable: Table<unknown>, cascade = false): ColumnShorthand {
return {
type: otherTable.primaryColumnType(),
@ -48,6 +53,11 @@ export const ColumnOf = {
nullable: true,
},
Blob: {
type: "BLOB",
nullable: true,
},
ForeignKey(otherTable: Table<unknown>, 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<unknown>, 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;

View file

@ -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";

View file

@ -106,7 +106,7 @@ export class Prequel {
public kv: PrequelKVStore;
public tables: Record<string, Table<unknown>> = {};
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");
}

View file

@ -12,7 +12,17 @@ import { Prequel } from "./prequel";
export type ExtractRowShape<T> = T extends Table<infer R> ? 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<T>(obj: Partial<T>): 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<RowShape> {
private _columnNames: string[];
private _createTableSQL: string;
private _primaryColumnName = "ROWID";
private _primaryColumnType: DataType | null = null;
private _insertQuery: Statement<RowShape, [ArbitraryRow]>;
private _updateQuery: Statement<RowShape, [ArbitraryRow]>;
private _findQuery: Statement<RowShape, [string]>;
@ -109,6 +114,7 @@ export class Table<RowShape> {
// 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<RowShape> {
}
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<RowShape> {
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.
*

View file

@ -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", () => {