sqlmongoose
Version:
Mongoose-like schemas and models for SQLite3
97 lines (96 loc) • 3.63 kB
JavaScript
export class Schema {
constructor(definition) {
this.definition = this.normalizeDefinition(definition);
this.hooks = {
preSave: [],
postSave: [],
preUpdate: [],
postUpdate: []
};
}
normalizeDefinition(definition) {
const normalized = {};
for (const [key, value] of Object.entries(definition)) {
if (typeof value === 'string') {
normalized[key] = { type: value };
}
else if (Array.isArray(value)) {
normalized[key] = { type: 'Array', of: value[0] };
}
else {
normalized[key] = value;
}
}
return normalized;
}
pre(hook, fn) {
if (hook === 'save')
this.hooks.preSave.push(fn);
if (hook === 'update')
this.hooks.preUpdate.push(fn);
}
post(hook, fn) {
if (hook === 'save')
this.hooks.postSave.push(fn);
if (hook === 'update')
this.hooks.postUpdate.push(fn);
}
async runValidation(data) {
for (const [field, config] of Object.entries(this.definition)) {
const fieldConfig = config;
if (fieldConfig.required && !data[field]) {
throw new Error(`Field ${field} is required`);
}
if (fieldConfig.validate && !await fieldConfig.validate(data[field])) {
throw new Error(`Validation failed for field ${field}`);
}
}
return true;
}
createTable(tableName) {
const queries = [];
// Convert schema types to SQLite types
const columns = Object.entries(this.definition).map(([fieldName, field]) => {
const fieldConfig = typeof field === 'string' ? { type: field } : (Array.isArray(field) ? { type: 'Array', of: field[0] } : field);
const type = this.getSqliteType(fieldConfig.type);
const required = fieldConfig.required ? 'NOT NULL' : 'NULL';
const unique = fieldConfig.unique ? 'UNIQUE' : '';
return `${fieldName} ${type} ${required} ${unique}`.trim();
});
queries.push(`CREATE TABLE IF NOT EXISTS ${tableName} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
${columns.join(',\n ')}
)`);
// Create indexes
Object.entries(this.definition).forEach(([fieldName, field]) => {
const fieldConfig = typeof field === 'string' ? { type: field } : (Array.isArray(field) ? { type: 'Array', of: field[0] } : field);
if (fieldConfig.index || fieldConfig.unique) {
queries.push(`CREATE ${fieldConfig.unique ? 'UNIQUE ' : ''}INDEX IF NOT EXISTS
idx_${tableName}_${fieldName} ON ${tableName}(${fieldName})`);
}
});
return queries;
}
getSqliteType(type) {
switch (type) {
case 'String': return 'TEXT';
case 'Number': return 'NUMERIC';
case 'Boolean': return 'INTEGER';
case 'Date': return 'TEXT';
case 'Array':
case 'Map':
case 'Mixed':
return 'TEXT'; // Stored as JSON
default: return 'TEXT';
}
}
getRelationships() {
const relationships = {};
Object.entries(this.definition).forEach(([field, config]) => {
if (typeof config === 'object' && !Array.isArray(config) && 'ref' in config && config.ref) {
relationships[field] = config.ref;
}
});
return relationships;
}
}