tspace-mysql
Version:
Tspace MySQL is a promise-based ORM for Node.js, designed with modern TypeScript and providing type safety for schema databases.
773 lines • 25.4 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.D = exports.AfterRemove = exports.AfterUpdate = exports.AfterInsert = exports.BeforeRemove = exports.BeforeUpdate = exports.BeforeInsert = exports.Hooks = exports.Observer = exports.BelongsToMany = exports.BelongsTo = exports.HasMany = exports.HasOne = exports.Validate = exports.Transform = exports.Column = exports.SnakeCase = exports.CamelCase = exports.Pattern = exports.SoftDelete = exports.Timestamp = exports.UUID = exports.TablePlural = exports.TableSingular = exports.Table = exports.REFLECT_META = void 0;
require("reflect-metadata");
const pluralize_1 = __importDefault(require("pluralize"));
exports.REFLECT_META = {
RELATIONS: {
hasOne: 'relation:hasOne',
hasMany: 'relation:hasMany',
belongsTo: 'relation:belongsTo',
belongsToMany: 'relation:belongsToMany'
},
SCHEMA: 'model:schema',
VALIDATE_SCHEMA: 'model:validateSchema',
TABLE: 'model:table',
UUID: {
enabled: 'model:uuidEnabled',
column: 'model:uuidColumn'
},
OBSERVER: 'model:observer',
TIMESTAMP: {
enabled: 'model:timestampEnabled',
columns: 'model:timestampColumns'
},
SOFT_DELETE: {
enabled: 'model:softDeleteEnabled',
columns: 'model:softDeleteColumns'
},
PATTERN: 'model:pattern',
HOOKS: 'model:hooks',
TRANSFORM: 'model:transform',
BEFORE: {
INSERT: 'model:beforeInsert',
UPDATE: 'model:beforeUpdate',
REMOVE: 'model:beforeRemove'
},
AFTER: {
INSERT: 'model:afterInsert',
UPDATE: 'model:afterUpdate',
REMOVE: 'model:afterRemove'
}
};
/**
* Decorator to mark a class with a database table name.
*
* Attaches the given table name to the class using Reflect metadata.
* This can be retrieved later to map the class to the corresponding database table.
*
* @param {string} name - The name of the database table.
* @returns {ClassDecorator} A class decorator that sets the table name metadata.
*
* @example
* ```ts
* @Table('users')
* class User extends Model {}
* ```
*/
const Table = (name) => {
return (target) => {
Reflect.defineMetadata(exports.REFLECT_META.TABLE, name, target);
};
};
exports.Table = Table;
/**
* Decorator to automatically generate a singular table name from a class name.
*
* Converts the class name from PascalCase to snake_case, then converts it to singular form.
* The resulting table name is stored as metadata on the class.
*
* @returns {ClassDecorator} A class decorator that sets the singular table name metadata.
*
* @example
* ```ts
* @TableSingular()
* class Users extends Model {}
* ```
*/
const TableSingular = () => {
return (target) => {
const name = target.name
.replace(/([A-Z])/g, (str) => `_${str.toLowerCase()}`)
.slice(1);
const singular = pluralize_1.default.singular(name);
Reflect.defineMetadata(exports.REFLECT_META.TABLE, singular, target);
};
};
exports.TableSingular = TableSingular;
/**
* Decorator to automatically generate a plural table name from a class name.
*
* Converts the class name from PascalCase to snake_case, then converts it to plural form.
* The resulting table name is stored as metadata on the class.
*
* @returns {ClassDecorator} A class decorator that sets the plural table name metadata.
*
* @example
* ```ts
* @TablePlural()
* class User extends Model {}
*
* ```
*/
const TablePlural = () => {
return (target) => {
const name = target.name
.replace(/([A-Z])/g, (str) => `_${str.toLowerCase()}`)
.slice(1);
const plural = pluralize_1.default.plural(name);
Reflect.defineMetadata(exports.REFLECT_META.TABLE, plural, target);
};
};
exports.TablePlural = TablePlural;
/**
* Decorator to enable automatic UUID generation for a model.
*
* Stores metadata indicating UUID usage and optional column name.
*
* @param {string} [column] - Optional column name to store the UUID.
* @returns {ClassDecorator} A class decorator that enables UUID metadata.
*
* @example
* ```ts
* @UUID('id')
* class User extends Model {}
* ```
*/
const UUID = (column) => {
return (target) => {
Reflect.defineMetadata(exports.REFLECT_META.UUID.enabled, true, target);
Reflect.defineMetadata(exports.REFLECT_META.UUID.column, column, target);
};
};
exports.UUID = UUID;
/**
* Decorator to enable automatic timestamps on a model.
*
* Stores metadata indicating that `createdAt` and `updatedAt` columns should be handled.
*
* @param {{ createdAt: string; updatedAt: string }} [columns] - Optional custom column names for timestamps.
* @returns {ClassDecorator} A class decorator that enables timestamp metadata.
*
* @example
* ```ts
* @Timestamp({ createdAt: 'created_at', updatedAt: 'updated_at' })
* class User extends Model {}
* ```
*/
const Timestamp = (columns) => {
return (target) => {
Reflect.defineMetadata(exports.REFLECT_META.TIMESTAMP.enabled, true, target);
Reflect.defineMetadata(exports.REFLECT_META.TIMESTAMP.columns, columns, target);
};
};
exports.Timestamp = Timestamp;
/**
* Decorator to enable soft deletion on a model.
*
* Stores metadata indicating soft delete usage and optional column name.
*
* @param {string} [column] - Optional column name to track soft deletion.
* @returns {ClassDecorator} A class decorator that enables soft delete metadata.
*
* @example
* ```ts
* @SoftDelete('deleted_at')
* class User extends Model {}
* ```
*/
const SoftDelete = (column) => {
return (target) => {
Reflect.defineMetadata(exports.REFLECT_META.SOFT_DELETE.enabled, true, target);
Reflect.defineMetadata(exports.REFLECT_META.SOFT_DELETE.columns, column, target);
};
};
exports.SoftDelete = SoftDelete;
/**
* Decorator to set the naming pattern for a model.
*
* Can be `camelCase` or `snake_case`.
*
* @param {"camelCase" | "snake_case"} pattern - The naming convention to use.
* @returns {ClassDecorator} A class decorator that sets the naming pattern metadata.
*
* @example
* ```ts
* @Pattern('snake_case')
* class User extends Model {}
* ```
*/
const Pattern = (pattern) => {
return (target) => {
Reflect.defineMetadata(exports.REFLECT_META.PATTERN, pattern, target);
};
};
exports.Pattern = Pattern;
/**
* Decorator to set the model naming pattern to camelCase.
*
* @returns {ClassDecorator} A class decorator that sets camelCase naming metadata.
*
* @example
* ```ts
* @CamelCase()
* class User extends Model {}
* ```
*/
const CamelCase = () => {
return (target) => {
Reflect.defineMetadata(exports.REFLECT_META.PATTERN, "camelCase", target);
};
};
exports.CamelCase = CamelCase;
/**
* Decorator to set the model naming pattern to snake_case.
*
* @returns {ClassDecorator} A class decorator that sets snake_case naming metadata.
*
* @example
* ```ts
* @SnakeCase()
* class User extends Model {}
* ```
*/
const SnakeCase = () => {
return (target) => {
Reflect.defineMetadata(exports.REFLECT_META.PATTERN, "snake_case", target);
};
};
exports.SnakeCase = SnakeCase;
/**
* Decorator to define a database column for a model property.
*
* Accepts a `Blueprint` function that defines the column type, constraints, and other attributes.
* The resulting column schema is stored as metadata on the target class.
*
* @param {() => Blueprint} blueprint - A function returning a `Blueprint` instance describing the column.
* @returns {Function} A property decorator that registers the column schema.
*
* @throws {Error} If the property name cannot be determined.
*
* @example
* ```ts
* class User extends Model {
*
* @Column(() => Blueprint.int().notNull().primary().autoIncrement())
* public id!: number;
*
* @Column(() => Blueprint.varchar(50).null())
* public uuid!: string;
* }
*
* ```
*/
const Column = (blueprint) => {
return (target, propertyKey) => {
if (!propertyKey) {
throw new Error("Unable to determine property name for Column decorator");
}
const schema = Reflect.getMetadata(exports.REFLECT_META.SCHEMA, target) || {};
Reflect.defineMetadata(exports.REFLECT_META.SCHEMA, {
...schema,
[propertyKey]: blueprint()
}, target);
};
};
exports.Column = Column;
/**
* A decorator factory that registers custom `to` and `from` transform functions
* for a property. Useful for serialization/deserialization logic (e.g. ORM, DTO).
*
* @param {Object} options - Transform options.
* @param {(value: unknown) => any | Promise<any>} options.to - Function executed when transforming *to* database/output.
* @param {(value: unknown) => any | Promise<any>} options.from - Function executed when transforming *from* database/input.
* @returns {Function} A property decorator that stores transform metadata.
*
* @throws {Error} If the property name cannot be determined.
*
* @example
* ```ts
* class User {
* @Transform({
* to: (v) => JSON.stringify(v),
* from: (v) => JSON.parse(v),
* })
* profile;
* }
* ```
*/
const Transform = ({ to, from }) => {
return (target, propertyKey) => {
if (!propertyKey) {
throw new Error("Unable to determine property name for Transform decorator");
}
const schema = Reflect.getMetadata(exports.REFLECT_META.TRANSFORM, target) || {};
Reflect.defineMetadata(exports.REFLECT_META.TRANSFORM, {
...schema,
[propertyKey]: { to, from }
}, target);
};
};
exports.Transform = Transform;
/**
* Decorator to attach validation rules to a model property.
*
* Accepts a `TValidateSchemaDecorator` object describing the validation rules
* (e.g., type, required, length, regex match, uniqueness, or custom validation function).
* The validation schema is stored as metadata on the target class.
*
* @param {TValidateSchemaDecorator} validate - An object defining validation rules for the property.
* @returns {Function} A decorator that registers the validation schema.
*
* @example
* ```ts
* class User extends Model {
*
* @Column(() => Blueprint.int().notNull().primary().autoIncrement())
* public id!: number;
*
* @Column(() => Blueprint.varchar(50).null())
* public uuid!: string;
*
* @Column(() => Blueprint.varchar(50).null())
* @Validate({
* type: String,
* require: true,
* length: 50,
* match: /^[a-zA-Z0-9._]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
* unique: true,
* fn: async (email: string) => {
* const exists = await new User().where('email',email).exists();
* if(exists) return `This column "${email}" is dupicate`;
* return null;
* }
* })
* public email!: string;
* }
*
* ```
*/
const Validate = (validate) => {
return (target, propertyKey) => {
const existing = Reflect.getMetadata(exports.REFLECT_META.VALIDATE_SCHEMA, target) || {};
Reflect.defineMetadata(exports.REFLECT_META.VALIDATE_SCHEMA, {
...existing,
[propertyKey]: validate
}, target);
};
};
exports.Validate = Validate;
/**
* Decorator to define a HasOne relationship on a model property.
*
* @param {TRelationOptionsDecorator} options - Options describing the relation.
* @returns {Function} A decorator that registers the HasOne relationship.
*
* @example
* ```ts
* class Profile extends Model {}
*
* class User extends Model {
* @HasOne({ model: Profile })
* public profile!: Profile;
* }
*
* ```
*/
const HasOne = (fn) => {
return (target, propertyKey) => {
if (!propertyKey)
throw new Error("Unable to determine property name for HasOne decorator");
const existing = Reflect.getMetadata(exports.REFLECT_META.RELATIONS.hasOne, target) || [];
const options = typeof fn === 'function'
? { model: fn, name: propertyKey }
: { ...fn, name: fn.name ?? propertyKey };
Reflect.defineMetadata(exports.REFLECT_META.RELATIONS.hasOne, [...existing, options], target);
};
};
exports.HasOne = HasOne;
/**
* Decorator to define a HasMany relationship on a model property.
*
* @param {TRelationOptionsDecorator} options - Options describing the relation.
* @returns {Function} A decorator that registers the HasMany relationship.
*
* @example
* ```ts
* class Post extends Model {}
*
* class User extends Model {
* @HasMany({ model: Post })
* public posts!: Post[];
* }
*
* ```
*/
const HasMany = (fn) => {
return (target, propertyKey) => {
if (!propertyKey)
throw new Error("Unable to determine property name for HasMany decorator");
const existing = Reflect.getMetadata(exports.REFLECT_META.RELATIONS.hasMany, target) || [];
const options = typeof fn === 'function'
? { model: fn, name: propertyKey }
: { ...fn, name: fn.name ?? propertyKey };
Reflect.defineMetadata(exports.REFLECT_META.RELATIONS.hasMany, [...existing, options], target);
};
};
exports.HasMany = HasMany;
/**
* Decorator to define a BelongsTo relationship on a model property.
*
* @param {TRelationOptionsDecorator} options - Options describing the relation.
* @returns {Function} A decorator that registers the BelongsTo relationship.
*
* @example
* ```ts
* class User extends Model {}
*
* class Post extends Model {
* @BelongsTo({ model: User })
* public author!: User;
* }
*
* ```
*/
const BelongsTo = (fn) => {
return (target, propertyKey) => {
if (!propertyKey)
throw new Error("Unable to determine property name for BelongsTo decorator");
const existing = Reflect.getMetadata(exports.REFLECT_META.RELATIONS.belongsTo, target) || [];
const options = typeof fn === 'function'
? { model: fn, name: propertyKey }
: { ...fn, name: fn.name ?? propertyKey };
Reflect.defineMetadata(exports.REFLECT_META.RELATIONS.belongsTo, [...existing, options], target);
};
};
exports.BelongsTo = BelongsTo;
/**
* Decorator to define a BelongsToMany (many-to-many) relationship on a model property.
*
* @param {TRelationOptionsDecorator} options - Options describing the relation.
* @returns {Function} A decorator that registers the BelongsToMany relationship.
*
* @example
* ```ts
* class Role extends Model {}
*
* class User extends Model {
* @BelongsToMany({ model: Role, pivotTable: 'user_roles' })
* public roles!: Role[];
* }
*
* ```
*/
const BelongsToMany = (fn) => {
return (target, propertyKey) => {
if (!propertyKey)
throw new Error("Unable to determine property name for BelongsToMany decorator");
const existing = Reflect.getMetadata(exports.REFLECT_META.RELATIONS.belongsToMany, target) || [];
const options = typeof fn === 'function'
? { model: fn, name: propertyKey }
: { ...fn, name: fn.name ?? propertyKey };
Reflect.defineMetadata(exports.REFLECT_META.RELATIONS.belongsToMany, [...existing, options], target);
};
};
exports.BelongsToMany = BelongsToMany;
/**
* Decorator to attach an observer class to a model.
*
* The observer class should have `selected`, `created`, `updated`, and `deleted` methods.
*
* @param {new () => { selected: Function; created: Function; updated: Function; deleted: Function }} observer - Observer class constructor.
* @returns {ClassDecorator} A class decorator that sets the model observer.
*
* @example
* ```ts
* class UserObserver {
* selected() {}
* created() {}
* updated() {}
* deleted() {}
* }
*
* @Observer(UserObserver)
* class User extends Model {}
* ```
*/
const Observer = (observer) => {
return (target) => {
Reflect.defineMetadata(exports.REFLECT_META.OBSERVER, observer, target);
};
};
exports.Observer = Observer;
/**
* Decorator that registers a method as a generic lifecycle hook.
* Unlike specific hooks such as `@BeforeInsert` or `@AfterUpdate`,
* this decorator is **multi-purpose** and collects all tagged methods
* into a single metadata registry (`REFLECT_META.HOOKS`).
*
* These hook methods can later be invoked by the ORM/engine at any stage
* depending on your custom logic.
*
* @returns {Function} A method decorator.
*
* @throws {Error} If applied to a non-method class member.
*
* @example
* ```ts
* class User {
* @Hooks()
* logAction() {
* console.log("A lifecycle action happened");
* }
* }
* ```
*
* @example
* // Later, your ORM engine could do:
* const hooks = Reflect.getMetadata(REFLECT_META.HOOKS, userInstance) || [];
* for (const hook of hooks) hook.call(userInstance);
*/
const Hooks = () => {
return (target, propertyKey, descriptor) => {
if (!descriptor) {
descriptor = Object.getOwnPropertyDescriptor(target, propertyKey);
}
if (typeof descriptor.value !== 'function') {
throw new Error(`@Hooks() can only be applied to methods.`);
}
const original = descriptor.value;
const hooks = Reflect.getMetadata(exports.REFLECT_META.HOOKS, target) || [];
Reflect.defineMetadata(exports.REFLECT_META.HOOKS, [...hooks, original], target);
descriptor.value = function (...args) {
return original.apply(this, args);
};
return descriptor;
};
};
exports.Hooks = Hooks;
/**
* Decorator that registers a method to be executed **before an insert operation**.
* Works similarly to TypeORM's `@BeforeInsert`.
*
* The decorated method will be stored in metadata and executed later by the ORM engine.
*
* @returns {Function} A method decorator.
*
* @throws {Error} If applied to a non-function member.
*
* @example
* ```ts
* class User {
* @BeforeInsert()
* setCreatedAt() {
* this.createdAt = new Date();
* }
* }
* ```
*/
const BeforeInsert = () => {
return (target, propertyKey, descriptor) => {
if (!descriptor) {
descriptor = Object.getOwnPropertyDescriptor(target, propertyKey);
}
if (typeof descriptor.value !== 'function') {
throw new Error(`@BeforeInsert can only be applied to methods.`);
}
const original = descriptor.value;
const existing = Reflect.getMetadata(exports.REFLECT_META.BEFORE.INSERT, target) || [];
Reflect.defineMetadata(exports.REFLECT_META.BEFORE.INSERT, [...existing, original], target);
descriptor.value = function (...args) {
return original.apply(this, args);
};
return descriptor;
};
};
exports.BeforeInsert = BeforeInsert;
/**
* Decorator that registers a method to be executed **before an update operation**.
*
* @returns {Function} A method decorator.
*
* @throws {Error} If applied to a non-function member.
*
* @example
* ```ts
* class User {
* @BeforeUpdate()
* updateTimestamp() {
* this.updatedAt = new Date();
* }
* }
* ```
*/
const BeforeUpdate = () => {
return (target, propertyKey, descriptor) => {
if (!descriptor) {
descriptor = Object.getOwnPropertyDescriptor(target, propertyKey);
}
if (typeof descriptor.value !== 'function') {
throw new Error(`@BeforeUpdate can only be applied to methods.`);
}
const original = descriptor.value;
const existing = Reflect.getMetadata(exports.REFLECT_META.BEFORE.UPDATE, target) || [];
Reflect.defineMetadata(exports.REFLECT_META.BEFORE.UPDATE, [...existing, original], target);
descriptor.value = function (...args) {
return original.apply(this, args);
};
return descriptor;
};
};
exports.BeforeUpdate = BeforeUpdate;
/**
* Decorator that registers a method to be executed **before a remove/delete operation**.
*
* @returns {Function} A method decorator.
*
* @throws {Error} If applied to a non-function member.
*
* @example
* ```ts
* class User {
* @BeforeRemove()
* handleBeforeDelete() {
* console.log("User will be deleted");
* }
* }
* ```
*/
const BeforeRemove = () => {
return (target, propertyKey, descriptor) => {
if (!descriptor) {
descriptor = Object.getOwnPropertyDescriptor(target, propertyKey);
}
if (typeof descriptor.value !== 'function') {
throw new Error(`@BeforeRemove can only be applied to methods.`);
}
const original = descriptor.value;
const existing = Reflect.getMetadata(exports.REFLECT_META.BEFORE.REMOVE, target) || [];
Reflect.defineMetadata(exports.REFLECT_META.BEFORE.REMOVE, [...existing, original], target);
descriptor.value = function (...args) {
return original.apply(this, args);
};
return descriptor;
};
};
exports.BeforeRemove = BeforeRemove;
/**
* Decorator that registers a method to be executed **after an insert operation**.
*
* @returns {Function} A method decorator.
*
* @throws {Error} If applied to a non-function member.
*
* @example
* ```ts
* class User {
* @AfterInsert()
* notifyCreated() {
* console.log("User inserted");
* }
* }
* ```
*/
const AfterInsert = () => {
return (target, propertyKey, descriptor) => {
if (!descriptor) {
descriptor = Object.getOwnPropertyDescriptor(target, propertyKey);
}
if (typeof descriptor.value !== 'function') {
throw new Error(`@afterInsert can only be applied to methods.`);
}
const original = descriptor.value;
const existing = Reflect.getMetadata(exports.REFLECT_META.AFTER.INSERT, target) || [];
Reflect.defineMetadata(exports.REFLECT_META.AFTER.INSERT, [...existing, original], target);
descriptor.value = function (...args) {
return original.apply(this, args);
};
return descriptor;
};
};
exports.AfterInsert = AfterInsert;
/**
* Decorator that registers a method to be executed **after an update operation**.
*
* @returns {Function} A method decorator.
*
* @throws {Error} If applied to a non-function member.
*
* @example
* ```ts
* class User {
* @AfterUpdate()
* logUpdate() {
* console.log("User updated");
* }
* }
* ```
*/
const AfterUpdate = () => {
return (target, propertyKey, descriptor) => {
if (!descriptor) {
descriptor = Object.getOwnPropertyDescriptor(target, propertyKey);
}
if (typeof descriptor.value !== 'function') {
throw new Error(`@afterUpdate can only be applied to methods.`);
}
const original = descriptor.value;
const existing = Reflect.getMetadata(exports.REFLECT_META.AFTER.UPDATE, target) || [];
Reflect.defineMetadata(exports.REFLECT_META.AFTER.UPDATE, [...existing, original], target);
descriptor.value = function (...args) {
return original.apply(this, args);
};
return descriptor;
};
};
exports.AfterUpdate = AfterUpdate;
/**
* Decorator that registers a method to be executed **after a remove/delete operation**.
*
* @returns {Function} A method decorator.
*
* @throws {Error} If applied to a non-function member.
*
* @example
* ```ts
* class User {
* @AfterRemove()
* logDeletion() {
* console.log("User removed");
* }
* }
* ```
*/
const AfterRemove = () => {
return (target, propertyKey, descriptor) => {
if (!descriptor) {
descriptor = Object.getOwnPropertyDescriptor(target, propertyKey);
}
if (typeof descriptor.value !== 'function') {
throw new Error(`@afterRemove can only be applied to methods.`);
}
const original = descriptor.value;
const existing = Reflect.getMetadata(exports.REFLECT_META.AFTER.REMOVE, target) || [];
Reflect.defineMetadata(exports.REFLECT_META.AFTER.REMOVE, [...existing, original], target);
descriptor.value = function (...args) {
return original.apply(this, args);
};
return descriptor;
};
};
exports.AfterRemove = AfterRemove;
/**
* Collection of decorators used for defining
* database models, columns, relations, hooks, and validation.
*
* @namespace D
*/
exports.D = {
Table: exports.Table, TableSingular: exports.TableSingular, TablePlural: exports.TablePlural,
UUID: exports.UUID, SoftDelete: exports.SoftDelete, Timestamp: exports.Timestamp,
Pattern: exports.Pattern, CamelCase: exports.CamelCase, SnakeCase: exports.SnakeCase,
Column: exports.Column, Validate: exports.Validate,
// ------- Relations --------
HasOne: exports.HasOne, HasMany: exports.HasMany, BelongsTo: exports.BelongsTo, BelongsToMany: exports.BelongsToMany,
// ------- Hook -------------
Transform: exports.Transform, Observer: exports.Observer, Hooks: exports.Hooks,
BeforeInsert: exports.BeforeInsert, BeforeUpdate: exports.BeforeUpdate, BeforeRemove: exports.BeforeRemove,
AfterInsert: exports.AfterInsert, AfterUpdate: exports.AfterUpdate, AfterRemove: exports.AfterRemove
};
//# sourceMappingURL=Decorator.js.map