This commit is contained in:
Endeavorance 2025-01-01 12:18:54 -05:00
parent 13c3d86a10
commit 20ac2da081
6 changed files with 47 additions and 10 deletions

View file

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

View file

@ -105,6 +105,8 @@ export class Prequel {
public db: Database; public db: Database;
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 uncachedQuery: typeof Database.prototype.prepare;
/** /**
* Creates a new Prequel database instance. * Creates a new Prequel database instance.
@ -112,6 +114,8 @@ 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.uncachedQuery = 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

@ -27,12 +27,10 @@ export class PrequelEmptyResultsError extends Error {
} }
/** An arbitrarily shaped Row */ /** An arbitrarily shaped Row */
interface ArbitraryRow { type ArbitraryRow = Record<string, ColumnValue>;
[key: string]: ColumnValue;
}
function shapeForQuery<T>(obj: Partial<T>): Record<string, ColumnValue> { function shapeForQuery<T>(obj: Partial<T>): ArbitraryRow {
const asInsertDataShape: Record<string, ColumnValue> = {}; const asInsertDataShape: ArbitraryRow = {};
const keys = Object.keys(obj); const keys = Object.keys(obj);
@ -72,6 +70,8 @@ export class Table<RowShape> {
private _sizeQuery: Statement<{ count: number }, []>; private _sizeQuery: Statement<{ count: number }, []>;
private _truncateQuery: Statement<void, []>; private _truncateQuery: Statement<void, []>;
public query: typeof Database.prototype.query;
constructor( constructor(
db: Database | Prequel, db: Database | Prequel,
name: string, name: string,
@ -85,6 +85,7 @@ export class Table<RowShape> {
this._name = name; this._name = name;
this._columnDefinitions = normalizeColumns(cols); this._columnDefinitions = normalizeColumns(cols);
this._columnNames = Object.keys(this._columnDefinitions); this._columnNames = Object.keys(this._columnDefinitions);
this.query = this._db.query.bind(this._db);
const columnSQLParts: string[] = []; const columnSQLParts: string[] = [];
const updateColumnSQLParts: string[] = []; const updateColumnSQLParts: string[] = [];
@ -211,6 +212,12 @@ export class Table<RowShape> {
return query.get(asInsertDataShape) as RowShape; return query.get(asInsertDataShape) as RowShape;
} }
public insertWithout<OmitKeys extends keyof RowShape>(
rowWithKeysOmitted: Omit<RowShape, OmitKeys>,
): RowShape {
return this.insertPartial(rowWithKeysOmitted as Partial<RowShape>);
}
/** /**
* Updates a row in the table with the provided data. * Updates a row in the table with the provided data.
* *

View file

@ -135,3 +135,11 @@ test("kv store can set and retrieve arbitrary values", () => {
expect(pq.kv.get<{ enabled: boolean }>("config")).toEqual({ enabled: true }); expect(pq.kv.get<{ enabled: boolean }>("config")).toEqual({ enabled: true });
expect(pq.kv.get<string[]>("list")).toEqual(["a", "b", "c"]); expect(pq.kv.get<string[]>("list")).toEqual(["a", "b", "c"]);
}); });
test(".query() creates a cached statement", () => {
const pq = new Prequel();
const query = pq.query("SELECT 1");
expect(query).toBeDefined();
expect(query).toBe(pq.query("SELECT 1"));
});

View file

@ -138,6 +138,22 @@ test(".insertPartial() inserts a partial row", () => {
}); });
}); });
test(".insertWithout() inserts a partial row with type safety", () => {
const table = makeTestTable();
table.insertWithout<"user_id">({
name: "Jack",
});
const lookup = table.db.prepare<UserWithID, string>(
`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", () => { test(".count() gets the current number of rows in the table", () => {
const table = makeTestTable(); const table = makeTestTable();
table.insertPartial({ name: "Jack" }); table.insertPartial({ name: "Jack" });
@ -167,6 +183,12 @@ test(".findAll() returns the full table", () => {
expect(allUsers[2]).toEqual({ user_id: 3, name: "Sayid" }); 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();
});
test(".findOneWhere() returns a row based on basic criteria", () => { test(".findOneWhere() returns a row based on basic criteria", () => {
const table = makeTestTable(); const table = makeTestTable();
table.insertPartial({ name: "Jack" }); table.insertPartial({ name: "Jack" });

View file

@ -7,10 +7,6 @@ interface User {
name: string; name: string;
} }
interface SerializedUser {
name: string;
}
const db = new Database(); const db = new Database();
const table = new Table<User>(db, "Users", { const table = new Table<User>(db, "Users", {
id: { id: {