it was weird. i pressed delete on a subfolder, i think one of the pages.off folders that i was using. and then, suddenly, nvim on windows 7 decided to delete every file in the directory. they weren't shred off the space time continuum, but just marked deleted. i had to pay $80 to get access to a software that could see them. bleh! just seeing all my work, a little over a week, was pretty heart shattering. but i remembered that long ago, a close friend said i could call them whenever i was feeling sad. i finally took them up on that offer. the first time i've ever called someone for emotional support. but it's ok. i got it back. and the site framework is better than ever. i'm gonna commit and push more often. the repo is private anyways.
101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
// Guard against reloads and bundler duplication.
|
|
// @ts-ignore
|
|
const map = globalThis[Symbol.for("clover.db")] ??= new Map<
|
|
string,
|
|
WrappedDatabase
|
|
>();
|
|
|
|
export function getDb(file: string) {
|
|
let db: WrappedDatabase | null = map.get(file);
|
|
if (db) return db;
|
|
const fileWithExt = file.includes(".") ? file : file + ".sqlite";
|
|
db = new WrappedDatabase(
|
|
new DatabaseSync(path.join(".clover/", fileWithExt)),
|
|
);
|
|
map.set(file, db);
|
|
return db;
|
|
}
|
|
|
|
export class WrappedDatabase {
|
|
node: DatabaseSync;
|
|
stmtTableMigrate: WeakRef<StatementSync> | null = null;
|
|
|
|
constructor(node: DatabaseSync) {
|
|
this.node = node;
|
|
this.node.exec(`
|
|
create table if not exists clover_migrations (
|
|
key text not null primary key,
|
|
version integer not null
|
|
);
|
|
`);
|
|
}
|
|
|
|
// TODO: add migration support
|
|
// the idea is you keep `schema` as the new schema but can add
|
|
// migrations to the mix really easily.
|
|
table(name: string, schema: string) {
|
|
let s = this.stmtTableMigrate?.deref();
|
|
s ?? (this.stmtTableMigrate = new WeakRef(
|
|
s = this.node.prepare(`
|
|
insert or ignore into clover_migrations
|
|
(key, version) values (?, ?);
|
|
`),
|
|
));
|
|
const { changes, lastInsertRowid } = s.run(name, 1);
|
|
console.log(changes, lastInsertRowid);
|
|
if (changes === 1) {
|
|
this.node.exec(schema);
|
|
}
|
|
}
|
|
|
|
prepare<Args extends unknown[] = [], Result = unknown>(
|
|
query: string,
|
|
): Stmt<Args, Result> {
|
|
return new Stmt(this.node.prepare(query));
|
|
}
|
|
}
|
|
|
|
export class Stmt<Args extends unknown[] = unknown[], Row = unknown> {
|
|
#node: StatementSync;
|
|
#class: any | null = null;
|
|
constructor(node: StatementSync) {
|
|
this.#node = node;
|
|
}
|
|
|
|
/** Get one row */
|
|
get(...args: Args): Row | null {
|
|
const item = this.#node.get(...args as any) as Row;
|
|
if (!item) return null;
|
|
const C = this.#class;
|
|
if (C) Object.setPrototypeOf(item, C.prototype);
|
|
return item;
|
|
}
|
|
getNonNull(...args: Args) {
|
|
const item = this.get(...args);
|
|
if (!item) throw new Error("Query returned no result");
|
|
return item;
|
|
}
|
|
iter(...args: Args): Iterator<Row> {
|
|
return this.array(...args)[Symbol.iterator]();
|
|
}
|
|
/** Get all rows */
|
|
array(...args: Args): Row[] {
|
|
const array = this.#node.all(...args as any) as Row[];
|
|
const C = this.#class;
|
|
if (C) array.forEach((item) => Object.setPrototypeOf(item, C.prototype));
|
|
return array;
|
|
}
|
|
/** Return the number of changes / row ID */
|
|
run(...args: Args) {
|
|
return this.#node.run(...args as any);
|
|
}
|
|
|
|
as<R>(Class: { new (): R }): Stmt<Args, R> {
|
|
this.#class = Class;
|
|
return this as any;
|
|
}
|
|
}
|
|
|
|
import { DatabaseSync, StatementSync } from "node:sqlite";
|
|
import * as fs from "./fs.ts";
|
|
import * as path from "node:path";
|