type-surrealdb
Version:
Typescript support and class utils for SurrealDB
184 lines (183 loc) • 7.71 kB
JavaScript
;
/* eslint-disable @typescript-eslint/no-explicit-any */
Object.defineProperty(exports, "__esModule", { value: true });
exports.TableSchema = void 0;
exports.Field = Field;
exports.Table = Table;
exports.generateSurqlSchema = generateSurqlSchema;
class TableSchema {
}
exports.TableSchema = TableSchema;
function initializeSchema(constructor) {
if (!constructor.SurrealdbSchema) {
constructor.SurrealdbSchema = {
tables: [],
properties: {},
};
}
else if (!constructor.SurrealdbSchema.properties) {
constructor.SurrealdbSchema.properties = {};
}
// Check if the class has a parent class with SurrealdbSchema
const parentConstructor = Object.getPrototypeOf(constructor);
if (parentConstructor === null || parentConstructor === void 0 ? void 0 : parentConstructor.SurrealdbSchema) {
// Merge the parent's properties into the child's properties
constructor.SurrealdbSchema.properties = Object.assign(Object.assign({}, parentConstructor.SurrealdbSchema.properties), constructor.SurrealdbSchema.properties);
}
// Process nested objects
for (const [_key, value] of Object.entries(constructor.SurrealdbSchema.properties)) {
if (value.type === 'object' && value.object) {
// Ensure that nested schemas are initialized
initializeSchema(value.object);
}
}
}
function Field(args) {
return (target, propertyKey) => {
if (!target || typeof propertyKey === 'undefined') {
throw new Error('Invalid target or propertyKey in Field decorator.');
}
let field;
if (typeof args === 'string') {
field = { type: args };
}
else {
field = Object.assign({}, args);
}
if (field.type === 'object' && 'object' in field) {
field.properties = field.object.SurrealdbSchema.properties || {};
}
initializeSchema(target.constructor);
target.constructor.SurrealdbSchema.properties[propertyKey] = field;
};
}
/**
* This will add the "name" field to the static schema object. While the name could be inferred from the class's
* constructor, it is required because obfuscating the JS bundle in production builds changes the class names, and thus
* produces inconsistent or duplicate SurrealDB table names.
* @param name The table name in SurrealDB. This can be different from the class name
*/
function Table(config) {
return (constructor) => {
var _a;
initializeSchema(constructor);
if (typeof config === 'string') {
constructor.SurrealdbSchema.tables = [config];
constructor.SurrealdbSchema.schemaMode = 'SCHEMALESS';
}
else if (Array.isArray(config)) {
constructor.SurrealdbSchema.tables = config;
constructor.SurrealdbSchema.schemaMode = 'SCHEMALESS';
}
else {
constructor.SurrealdbSchema.tables = config.tables;
constructor.SurrealdbSchema.schemaMode = (_a = config.schemaMode) !== null && _a !== void 0 ? _a : 'SCHEMALESS';
constructor.SurrealdbSchema.indexes = config.indexes;
}
};
}
function generateSurqlSchema(entities, comments = false) {
const schemaParts = [];
//
function addFieldDefinition(tableName, tableGeneric, fieldName, fieldConfig) {
var _a;
const fieldDefinition = [`DEFINE FIELD ${fieldName} ON ${tableName}`];
let fieldtype;
if (fieldConfig.primary) {
fieldtype = `record<${tableName}>`;
}
else if (fieldConfig.type === 'object' && 'properties' in fieldConfig) {
fieldtype = `object`;
}
else if ('typed' in fieldConfig && fieldConfig.typed) {
const fieldtyped = fieldConfig.typed === '$$generic' ? tableGeneric : fieldConfig.typed;
fieldtype = `${fieldConfig.type}<${fieldtyped}>`;
}
else {
fieldtype =
fieldConfig.type === '$$generic' ? (tableGeneric !== null && tableGeneric !== void 0 ? tableGeneric : 'any') : ((_a = fieldConfig.type) !== null && _a !== void 0 ? _a : 'any');
}
if (fieldConfig.optional) {
fieldDefinition.push(`TYPE option<${fieldtype}>`);
}
else {
fieldDefinition.push(`TYPE ${fieldtype}`);
}
if (fieldConfig.default) {
fieldDefinition.push(`DEFAULT ${fieldConfig.default}`);
}
if (fieldConfig.value) {
fieldDefinition.push(`VALUE ${fieldConfig.value}`);
}
if (fieldConfig.assertion) {
fieldDefinition.push(`ASSERT ${fieldConfig.assertion}`);
}
schemaParts.push(`${fieldDefinition.join(' ')};`);
// Recursively define nested fields
if (fieldConfig.type === 'object' && 'properties' in fieldConfig) {
for (const [nestedFieldName, nestedFieldConfig] of Object.entries(fieldConfig.properties)) {
addFieldDefinition(tableName, tableGeneric, `${fieldName}.${nestedFieldName}`, nestedFieldConfig);
}
}
}
//
function addIndexDefinition(tableName, fieldName, fieldConfig) {
if (fieldConfig.primary || fieldConfig.indexed || fieldConfig.unique) {
const indexDefinition = [
`DEFINE INDEX idx_${tableName}_${fieldName} ON ${tableName} FIELDS ${fieldName}`,
];
if (fieldConfig.primary || fieldConfig.unique)
indexDefinition.push('UNIQUE');
schemaParts.push(`${indexDefinition.join(' ')};`);
}
}
//
function addTableIndexes(tableName, indexes) {
indexes === null || indexes === void 0 ? void 0 : indexes.forEach(index => {
var _a;
const indexDefinition = [
`DEFINE INDEX ${(_a = index.name) !== null && _a !== void 0 ? _a : `idx_${tableName}_${index.fields.join('_')}`} ON ${tableName} FIELDS ${index.fields.join(', ')}`,
];
if (index.unique)
indexDefinition.push('UNIQUE');
schemaParts.push(`${indexDefinition.join(' ')};`);
});
}
// start schema generation
for (const entity of entities) {
const { tables, schemaMode, indexes, properties } = entity.SurrealdbSchema;
for (const table of tables) {
let tableName;
let tableGeneric;
if (typeof table === 'string') {
tableName = table;
tableGeneric = undefined;
}
else {
tableName = table.name;
tableGeneric = table.generic;
}
if (comments) {
schemaParts.push('-- ------------------------------');
schemaParts.push(`-- TABLE: ${tableName}`);
schemaParts.push('-- ------------------------------\n');
}
// Begin table definition
schemaParts.push(`DEFINE TABLE ${tableName} ${schemaMode};`);
if (comments)
schemaParts.push('');
// Define fields & indexes
for (const [fieldName, fieldConfig] of Object.entries(properties)) {
// Define field
addFieldDefinition(tableName, tableGeneric, fieldName, fieldConfig);
// Define index
addIndexDefinition(tableName, fieldName, fieldConfig);
}
// Define table indexes
addTableIndexes(tableName, indexes);
// Separate each table definition
schemaParts.push('\n');
}
}
return schemaParts.join('\n');
}