@cheetah.js/orm 0.1.37 → 0.1.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -298,7 +298,7 @@
298
298
  "import { Property, PropertyOptions } from './property.decorator';\n\nexport function PrimaryKey(options?: Omit<PropertyOptions, 'isPrimary'>): PropertyDecorator {\n const isPrimary = true;\n return Property({ ...options, isPrimary });\n}",
299
299
  "import { PROPERTIES_RELATIONS } from '../constants';\nimport { EntityName, Relationship } from '../driver/driver.interface';\nimport { Metadata } from '@cheetah.js/core';\nimport { toSnakeCase } from '../utils';\n\nexport function OneToMany<T>(entity: () => EntityName<T>, fkKey: (string & keyof T) | ((e: T) => any)): PropertyDecorator {\n return (target, propertyKey) => {\n const existing: Relationship<T>[] = Metadata.get(PROPERTIES_RELATIONS, target.constructor) || [];\n const options = {relation: 'one-to-many', propertyKey, isRelation: true, entity, fkKey, type: Metadata.getType(target, propertyKey), originalEntity: target.constructor}\n options['columnName'] = `${toSnakeCase(propertyKey as string)}_id`;\n // @ts-ignore\n existing.push(options);\n Metadata.set(PROPERTIES_RELATIONS, existing, target.constructor);\n };\n}\n\nexport function ManyToOne<T>(entity: () => EntityName<T>): PropertyDecorator {\n return (target, propertyKey) => {\n const existing: Relationship<T>[] = Metadata.get(PROPERTIES_RELATIONS, target.constructor) || [];\n const options = {relation: 'many-to-one', propertyKey, isRelation: true, entity, type: Metadata.getType(target, propertyKey), originalEntity: target.constructor}\n options['columnName'] = `${toSnakeCase(propertyKey as string)}_id`;\n // @ts-ignore\n existing.push(options);\n Metadata.set(PROPERTIES_RELATIONS, existing, target.constructor);\n };\n}",
300
300
  "import { EVENTS_METADATA } from '../constants';\n\nexport function BeforeCreate() {\n return function (target, propertyName) {\n const metadata = Reflect.getMetadata(EVENTS_METADATA, target.constructor) || [];\n Reflect.defineMetadata(EVENTS_METADATA, [...metadata, { type: 'beforeCreate', propertyName }], target.constructor);\n };\n}\n\nexport function AfterCreate() {\n return function (target, propertyName) {\n const metadata = Reflect.getMetadata(EVENTS_METADATA, target.constructor) || [];\n Reflect.defineMetadata(EVENTS_METADATA, [...metadata, { type: 'afterCreate', propertyName }], target.constructor);\n };\n}\n\nexport function BeforeUpdate() {\n return function (target, propertyName) {\n const metadata = Reflect.getMetadata(EVENTS_METADATA, target.constructor) || [];\n Reflect.defineMetadata(EVENTS_METADATA, [...metadata, { type: 'beforeUpdate', propertyName }], target.constructor);\n };\n}\n\nexport function AfterUpdate() {\n return function (target, propertyName) {\n const metadata = Reflect.getMetadata(EVENTS_METADATA, target.constructor) || [];\n Reflect.defineMetadata(EVENTS_METADATA, [...metadata, { type: 'afterUpdate', propertyName }], target.constructor);\n };\n}",
301
- "import { ClassType, EnumOptions } from '../driver/driver.interface';\nimport { Property } from '@cheetah.js/orm/decorators/property.decorator';\n\nexport function Enum(options: EnumOptions<any> | (() => ClassType)): PropertyDecorator {\n const isEnum = true;\n //@ts-ignore\n let enumItems: string[]|number[] = typeof options === 'function' ? options() : (typeof options.items === 'function' ? options.items() : options.items);\n if (typeof enumItems === 'object') {\n enumItems = Object.keys(enumItems).map(key => enumItems[key]);\n }\n return Property({ ...options, isEnum, enumItems, dbType: 'enum' });\n}",
301
+ "import { Property } from './property.decorator';\nimport { ClassType, EnumOptions } from '../driver/driver.interface';\n\nexport function Enum(options: EnumOptions<any> | (() => ClassType)): PropertyDecorator {\n const isEnum = true;\n //@ts-ignore\n let enumItems: string[]|number[] = typeof options === 'function' ? options() : (typeof options.items === 'function' ? options.items() : options.items);\n if (typeof enumItems === 'object') {\n enumItems = Object.keys(enumItems).map(key => enumItems[key]);\n }\n return Property({ ...options, isEnum, enumItems, dbType: 'enum' });\n}",
302
302
  "import { Metadata, Service } from \"@cheetah.js/core\";\nimport { PropertyOptions } from \"../decorators/property.decorator\";\nimport { ColumnsInfo, Relationship, SnapshotIndexInfo, SnapshotTable } from \"../driver/driver.interface\";\nimport { getDefaultLength, toSnakeCase } from \"../utils\";\n\nexport type Property = {\n options: PropertyOptions;\n type: Function;\n};\n\nexport type Options = {\n properties: { [key: string]: Property };\n hideProperties: string[];\n indexes?: SnapshotIndexInfo[];\n relations: Relationship<any>[];\n tableName: string;\n hooks?: { type: string; propertyName: string }[];\n schema?: string;\n};\n\n@Service()\nexport class EntityStorage {\n static instance: EntityStorage;\n\n private entities: Map<Function, Options> = new Map();\n\n constructor() {\n EntityStorage.instance = this;\n }\n\n add(\n entity: { target: Function; options: any },\n properties: {\n [key: string]: Property;\n },\n relations: Relationship<any>[],\n hooks: { type: string; propertyName: string }[]\n ) {\n const entityName = entity.options?.tableName || toSnakeCase(entity.target.name);\n\n const indexes = Metadata.get(\"indexes\", entity.target) || [];\n this.entities.set(entity.target, {\n properties: properties,\n hideProperties: Object.entries(properties)\n .filter(([key, value]) => value.options.hidden)\n .map(([key]) => key),\n relations,\n indexes: indexes.map((index: { name: string; properties: string[] }) => {\n return {\n table: entityName,\n indexName: index.name.replace(\"[TABLE]\", entityName),\n columnName: index.properties.join(\",\"),\n };\n }),\n hooks,\n tableName: entityName,\n ...entity.options,\n });\n }\n\n get(entity: Function) {\n return this.entities.get(entity);\n }\n\n entries() {\n return this.entities.entries();\n }\n\n static getInstance() {\n return EntityStorage.instance;\n }\n\n async snapshot(values: Options): Promise<SnapshotTable> {\n return {\n tableName: values.tableName,\n schema: values.schema || \"public\",\n indexes: values.indexes || [],\n columns: this.snapshotColumns(values),\n };\n }\n\n private snapshotColumns(values: Options): ColumnsInfo[] {\n let properties: ColumnsInfo[] = Object.entries(values.properties).map(([key, value]) => {\n return {\n name: value.options.columnName,\n type: value.options.dbType ?? value.type.name,\n nullable: value.options?.nullable,\n default: value.options?.default,\n autoIncrement: value.options?.autoIncrement,\n primary: value.options?.isPrimary,\n unique: value.options?.unique,\n length: value.options?.length,\n isEnum: value.options?.isEnum,\n precision: value.options?.precision,\n scale: value.options?.scale,\n enumItems: value.options?.enumItems,\n };\n });\n // @ts-ignore\n let relations: ColumnsInfo[] =\n values.relations &&\n values.relations.map((relation) => {\n const type = this.getFkType(relation);\n\n return {\n name: relation.columnName as string,\n type,\n nullable: relation.nullable,\n unique: relation.unique,\n length: relation.length || getDefaultLength(type),\n default: relation.default,\n autoIncrement: this.getFkIncrement(relation),\n primary: relation.isPrimary,\n precision: relation.precision,\n scale: relation.scale,\n foreignKeys: [\n {\n referencedColumnName: this.getFkKey(relation),\n referencedTableName: this.get(relation.entity() as any)!.tableName,\n },\n ],\n };\n });\n\n if (!relations) {\n relations = [];\n }\n if (!properties) {\n properties = [];\n }\n\n return [...properties, ...relations];\n }\n\n private snapshotIndexes(values: Options): SnapshotIndexInfo[] {\n return Object.entries(values.properties).map(([key, value]) => {\n return {\n indexName: key,\n columnName: key,\n table: values.tableName,\n };\n });\n }\n\n private getFkType(relation: Relationship<any>): any {\n const entity = this.get(relation.entity() as any);\n if (!entity) {\n return \"unknown\";\n }\n\n return entity.properties[this.getFkKey(relation)].type.name;\n }\n\n private getFkIncrement(relation: Relationship<any>): any {\n const entity = this.get(relation.entity() as any);\n if (!entity) {\n return \"unknown\";\n }\n\n return entity.properties[this.getFkKey(relation)].options.autoIncrement;\n }\n\n /**\n * If fkKey is null, return the primary key of the entity\n * @private\n * @param relationShip\n */\n private getFkKey(relationShip: Relationship<any>): string | number {\n // se for nullable, deverá retornar o primary key da entidade target\n if (typeof relationShip.fkKey === \"undefined\") {\n const entity = this.entities.get(relationShip.entity() as any);\n const property = Object.entries(entity!.properties).find(([key, value]) => value.options.isPrimary === true);\n if (!property) {\n throw new Error(`Entity ${entity!.tableName} does not have a primary key`);\n }\n\n return property[0];\n }\n\n // se o fkKey é uma função, ele retornará a propriedade da entidade que é a chave estrangeira\n // precisamos pegar o nome dessa propriedade\n if (typeof relationShip.fkKey === \"string\") {\n return relationShip.fkKey;\n }\n\n const match = /\\.(?<propriedade>[\\w]+)/.exec(relationShip.fkKey.toString());\n return match ? match.groups!.propriedade : \"\";\n }\n}\n",
303
303
  "import { HttpException } from \"@cheetah.js/core\";\n\ntype VoExtended<T, Vo> = Vo extends ValueObject<T, Vo> ? Vo : ValueObject<T, Vo>;\ntype DatabaseValues = {\n max?: number;\n min?: number;\n precision?: number;\n scale?: number;\n}\n\n\nexport abstract class ValueObject<T, Vo> {\n /**\n * Validates the value of the Value Object.\n * It is abstract so that each Value Object can implement its own validation.\n * It is protected from being called directly.\n *\n * @param value\n * @protected\n */\n protected abstract validate(value: T): boolean;\n\n /**\n * The maximum length of the Value Object.\n * It value is optional and will be used in database if this Value Object is used as a column.\n * If the value is string, it will be the maximum number of characters.\n * If the value is number, it will be the maximum number.\n */\n protected max?: number;\n\n /**\n * The minimum length of the Value Object.\n * It value is optional.\n * If the value is string, it will be the minimum number of characters.\n * If the value is number, it will be the minimum number.\n */\n protected min?: number;\n\n /**\n * The precision of the Value Object.\n * It value is optional and will be used in database if this Value Object is used as a column.\n * It is the number of digits in a number.\n */\n protected precision?: number;\n\n /**\n * The scale of the Value Object.\n * It value is optional and will be used in database if this Value Object is used as a column.\n * It is the number of digits to the right of the decimal point in a number.\n */\n protected scale?: number;\n\n /**\n * Valor do Value Object.\n * É privado para não ser alterado diretamente.\n * O valor também é imutável.\n *\n * @private\n */\n private value: T;\n\n constructor(value: T, skipValidation = false) {\n if (!skipValidation && (!this.validate(value) || !this.validateDatabase(value))) {\n throw new HttpException(`Invalid value for ${this.constructor.name}`, 400);\n }\n\n this.setValue(value);\n }\n\n /**\n * Creates a Value Object instance from a value.\n *\n * @example\n * Email.from('test@test.com');\n *\n * @param value\n */\n static from<T, Vo>(this: new (value: T) => VoExtended<T, Vo>, value: T): VoExtended<T, Vo> {\n return new this(value);\n }\n\n /**\n * Returns the scalar value of the Value Object.\n *\n */\n public getValue(): T {\n return this.value;\n }\n\n /**\n * Compares the value of the Value Object with another Value Object.\n *\n * @param vo\n */\n public equals(vo: ValueObject<T, Vo>): boolean {\n return this.getValue() === vo.getValue();\n }\n\n /**\n * Returns the database settings of the Value Object.\n * \n * @returns \n */\n public getDatabaseValues(): DatabaseValues {\n return {\n max: this.max,\n min: this.min,\n precision: this.precision,\n scale: this.scale,\n };\n }\n\n /**\n * Sets the value of the Value Object.\n *\n * @param value\n * @private\n */\n private setValue(value: T) {\n this.value = value;\n }\n\n /**\n * Validates the value of the Value Object.\n * It is private so that it can only be called by the constructor.\n *\n * @param value\n * @returns\n */\n private validateDatabase<T>(value: T): boolean {\n if (typeof value === \"string\") {\n if (this.max !== undefined && value.length > this.max) {\n throw new HttpException(`Value exceeds maximum length of ${this.max}`, 400);\n }\n\n if (this.min !== undefined && value.length < this.min) {\n throw new HttpException(`Value is less than minimum length of ${this.min}`, 400);\n }\n } else if (typeof value === \"number\") {\n if (this.max !== undefined && value > this.max) {\n throw new HttpException(`Value exceeds maximum value of ${this.max}`, 400);\n }\n\n if (this.min !== undefined && value < this.min) {\n throw new HttpException(`Value is less than minimum value of ${this.min}`, 400);\n }\n\n if (this.precision !== undefined) {\n const totalDigits = value.toString().replace(\".\", \"\").length;\n if (totalDigits > this.precision) {\n throw new HttpException(`Value exceeds precision of ${this.precision}`, 400);\n }\n }\n\n if (this.scale !== undefined) {\n const decimalDigits = (value.toString().split(\".\")[1] || \"\").length;\n if (decimalDigits > this.scale) {\n throw new HttpException(`Value exceeds scale of ${this.scale}`, 400);\n }\n }\n }\n\n return true;\n }\n}\n",
304
304
  "import { SqlBuilder } from '../SqlBuilder';\nimport { FilterQuery, FindOneOption, FindOptions, ValueOrInstance } from '../driver/driver.interface';\nimport { EntityStorage, Property } from './entities';\n\nexport abstract class BaseEntity {\n private _oldValues: any = {};\n private _changedValues: any = {};\n private $_isPersisted: boolean = false;\n\n constructor() {\n return new Proxy(this, {\n set(target: any, p: string, newValue: any): boolean {\n\n if (p.startsWith('$')) {\n target[p] = newValue;\n return true;\n }\n\n // se oldvalue não existir, é porque é a primeira vez que o atributo está sendo setado\n if (!target._oldValues[p]) {\n target._oldValues[p] = newValue;\n }\n\n // se o valor for diferente do valor antigo, é porque o valor foi alterado\n if (target._oldValues[p] !== newValue) {\n target._changedValues[p] = newValue;\n }\n\n target[p] = newValue;\n\n return true;\n },\n })\n }\n\n /**\n * Gets current entity's Repository.\n */\n static createQueryBuilder<T>(\n this: { new(): T } & typeof BaseEntity,\n ): SqlBuilder<T> {\n return new SqlBuilder<T>(this);\n }\n\n /**\n * Gets current entity's Repository.\n */\n private createQueryBuilder<T>(): SqlBuilder<T> {\n // @ts-ignore\n return new SqlBuilder<T>(this.constructor);\n }\n\n static async find<T, Hint extends string = never>(\n this: { new(): T } & typeof BaseEntity,\n where: FilterQuery<T>,\n options?: FindOptions<T, Hint>\n ): Promise<T[]> {\n return this.createQueryBuilder<T>()\n .select(options?.fields as any)\n .setStrategy(options?.loadStrategy)\n .load(options?.load as any[])\n .where(where)\n .limit(options?.limit)\n .offset(options?.offset)\n .orderBy(options?.orderBy as string[])\n .executeAndReturnAll();\n }\n\n static async findOne<T, Hint extends string = never>(\n this: { new(): T } & typeof BaseEntity,\n where: FilterQuery<T>,\n options?: FindOneOption<T, Hint>\n ): Promise<T | undefined> {\n return this.createQueryBuilder<T>()\n .select(options?.fields as any)\n .setStrategy(options?.loadStrategy)\n .load(options?.load as any[])\n .where(where)\n .executeAndReturnFirst();\n }\n\n /**\n * Find a record in the database based on the provided query where and return it, or throw an error if not found.\n *\n * @param {FilterQuery<T>} where - The query where used to search for the record.\n * @param options\n * @return {Promise<T>} - A promise that resolves with the found record.\n */\n static async findOneOrFail<T, Hint extends string = never>(\n this: { new(): T } & typeof BaseEntity,\n where: FilterQuery<T>,\n options?: FindOneOption<T, Hint>\n ): Promise<T> {\n return this.createQueryBuilder<T>()\n // @ts-ignore\n .select(options?.fields)\n .setStrategy(options?.loadStrategy)\n .load(options?.load as any[])\n .where(where)\n .orderBy(options?.orderBy as string[])\n .executeAndReturnFirstOrFail();\n }\n\n static async findAll<\n T extends object,\n Hint extends string = never\n >(\n this: { new(): T } & typeof BaseEntity,\n options: FindOptions<T, Hint>\n ): Promise<T[]> {\n const builder = this.createQueryBuilder<T>()\n .select(options.fields as any)\n .setStrategy(options?.loadStrategy)\n .load(options?.load as any[])\n .offset(options?.offset)\n .limit(options.limit)\n .orderBy(options?.orderBy as string[]);\n\n return builder.executeAndReturnAll();\n }\n\n static async create<T extends BaseEntity>(\n this: { new(): T } & typeof BaseEntity,\n where: Partial<{ [K in keyof T]: ValueOrInstance<T[K]> }>,\n ): Promise<T> {\n return this.createQueryBuilder<T>()\n .insert(where)\n .executeAndReturnFirstOrFail();\n }\n\n public async save() {\n const qb = this.createQueryBuilder()\n\n if (this.$_isPersisted) {\n qb.update(this._changedValues);\n qb.setInstance(this)\n // @ts-ignore\n qb.where({id: this._oldValues.id})\n } else {\n qb.insert(this._oldValues)\n }\n\n await qb.execute()\n qb.callHook('afterCreate', this)\n qb.callHook('afterUpdate', this)\n this._oldValues = {\n ...this._oldValues,\n ...this._changedValues,\n }\n this._changedValues = {}\n }\n\n public toJSON(): Record<string, any> {\n let data: any = {}\n let storage = EntityStorage.getInstance()\n let entity = storage.get(this.constructor)\n let allProperties = new Map<string, Property>(Object.entries(entity.properties).map(([key, value]) => [key, value]))\n\n for (const key in this) {\n // if starts with $ or _, ignore\n if (key.startsWith('$') || key.startsWith('_')) {\n continue;\n }\n\n if (!allProperties.has(key)) {\n continue;\n }\n\n if (entity.hideProperties.includes(key)) {\n continue;\n }\n\n data[key] = this[key]\n }\n return data;\n }\n}",
@@ -1,4 +1,4 @@
1
- import { Property } from '@cheetah.js/orm/decorators/property.decorator';
1
+ import { Property } from './property.decorator';
2
2
  export function Enum(options) {
3
3
  const isEnum = true;
4
4
  //@ts-ignore
@@ -1 +1 @@
1
- {"version":3,"file":"enum.decorator.js","sourceRoot":"","sources":["../../src/decorators/enum.decorator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,+CAA+C,CAAC;AAEzE,MAAM,UAAU,IAAI,CAAC,OAA6C;IAChE,MAAM,MAAM,GAAG,IAAI,CAAC;IACpB,YAAY;IACZ,IAAI,SAAS,GAAsB,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACvJ,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE;QACjC,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;KAC/D;IACD,OAAO,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AACrE,CAAC"}
1
+ {"version":3,"file":"enum.decorator.js","sourceRoot":"","sources":["../../src/decorators/enum.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD,MAAM,UAAU,IAAI,CAAC,OAA6C;IAChE,MAAM,MAAM,GAAG,IAAI,CAAC;IACpB,YAAY;IACZ,IAAI,SAAS,GAAsB,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACvJ,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE;QACjC,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;KAC/D;IACD,OAAO,QAAQ,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AACrE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cheetah.js/orm",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
4
4
  "description": "A simple ORM for Cheetah.js",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",