UNPKG

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
"use strict"; 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