userdo
Version:
A Durable Object base class for building applications on Cloudflare Workers.
128 lines (127 loc) • 5.54 kB
JavaScript
import { GenericQuery } from './query.js';
export class GenericTable {
constructor(tableName, schema, storage, userId, getOrganizationContext, broadcast) {
this.tableName = tableName;
this.schema = schema;
this.storage = storage;
this.userId = userId;
this.getOrganizationContext = getOrganizationContext;
this.broadcast = broadcast;
}
get organizationContext() {
return this.getOrganizationContext();
}
async create(data) {
const validated = this.schema.parse(data);
const id = crypto.randomUUID();
const now = Date.now();
let insertSQL;
let params;
if (this.organizationContext) {
insertSQL = `INSERT INTO "${this.tableName}" (id, data, created_at, updated_at, user_id, organization_id) VALUES (?, ?, ?, ?, ?, ?)`;
params = [id, JSON.stringify(validated), now, now, this.userId, this.organizationContext];
}
else {
insertSQL = `INSERT INTO "${this.tableName}" (id, data, created_at, updated_at, user_id) VALUES (?, ?, ?, ?, ?)`;
params = [id, JSON.stringify(validated), now, now, this.userId];
}
this.storage.sql.exec(insertSQL, ...params);
const result = { ...validated, id, createdAt: new Date(now), updatedAt: new Date(now) };
// Broadcast table change
this.broadcast?.(`table:${this.tableName}`, { type: 'create', data: result });
return result;
}
async findById(id) {
let selectSQL;
let params;
if (this.organizationContext) {
selectSQL = `SELECT * FROM "${this.tableName}" WHERE id = ? AND user_id = ? AND organization_id = ? LIMIT 1`;
params = [id, this.userId, this.organizationContext];
}
else {
selectSQL = `SELECT * FROM "${this.tableName}" WHERE id = ? AND user_id = ? LIMIT 1`;
params = [id, this.userId];
}
const cursor = this.storage.sql.exec(selectSQL, ...params);
const results = cursor.toArray();
if (results.length === 0) {
return null;
}
const row = results[0];
const data = JSON.parse(row.data);
return {
...data,
id: row.id,
createdAt: new Date(row.created_at),
updatedAt: new Date(row.updated_at)
};
}
async update(id, updates) {
const existing = await this.findById(id);
if (!existing)
throw new Error('Record not found');
const merged = { ...existing, ...updates };
delete merged.id;
delete merged.createdAt;
delete merged.updatedAt;
const validated = this.schema.parse(merged);
const now = Date.now();
let updateSQL;
let params;
if (this.organizationContext) {
updateSQL = `UPDATE "${this.tableName}" SET data = ?, updated_at = ? WHERE id = ? AND user_id = ? AND organization_id = ?`;
params = [JSON.stringify(validated), now, id, this.userId, this.organizationContext];
}
else {
updateSQL = `UPDATE "${this.tableName}" SET data = ?, updated_at = ? WHERE id = ? AND user_id = ?`;
params = [JSON.stringify(validated), now, id, this.userId];
}
this.storage.sql.exec(updateSQL, ...params);
const result = { ...validated, id, createdAt: existing.createdAt, updatedAt: new Date(now) };
// Broadcast table change
this.broadcast?.(`table:${this.tableName}`, { type: 'update', data: result });
return result;
}
async delete(id) {
let deleteSQL;
let params;
if (this.organizationContext) {
deleteSQL = `DELETE FROM "${this.tableName}" WHERE id = ? AND user_id = ? AND organization_id = ?`;
params = [id, this.userId, this.organizationContext];
}
else {
deleteSQL = `DELETE FROM "${this.tableName}" WHERE id = ? AND user_id = ?`;
params = [id, this.userId];
}
this.storage.sql.exec(deleteSQL, ...params);
// Broadcast table change
this.broadcast?.(`table:${this.tableName}`, { type: 'delete', data: { id } });
}
where(path, operator, value) {
return new GenericQuery(this.tableName, this.storage, this.schema, this.userId, this.getOrganizationContext).where(path, operator, value);
}
orderBy(field, direction = 'asc') {
return new GenericQuery(this.tableName, this.storage, this.schema, this.userId, this.getOrganizationContext).orderBy(field, direction);
}
limit(count) {
return new GenericQuery(this.tableName, this.storage, this.schema, this.userId, this.getOrganizationContext).limit(count);
}
async getAll() {
return new GenericQuery(this.tableName, this.storage, this.schema, this.userId, this.getOrganizationContext).get();
}
async count() {
let sql;
let params;
if (this.organizationContext) {
sql = `SELECT COUNT(*) as count FROM "${this.tableName}" WHERE user_id = ? AND organization_id = ?`;
params = [this.userId, this.organizationContext];
}
else {
sql = `SELECT COUNT(*) as count FROM "${this.tableName}" WHERE user_id = ?`;
params = [this.userId];
}
const cursor = this.storage.sql.exec(sql, ...params);
const results = cursor.toArray();
return results.length > 0 ? Number(results[0].count) : 0;
}
}