Add column presets
This commit is contained in:
parent
6479d09196
commit
1bfc20673d
6 changed files with 50 additions and 34 deletions
|
@ -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": {
|
||||||
|
|
|
@ -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;
|
|
@ -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";
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
37
src/table.ts
37
src/table.ts
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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", () => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue