@rtpaulino/entity 0.24.0 → 0.24.1

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.
@@ -24,6 +24,12 @@ export interface EntitySchemaConfig<T = any> {
24
24
  * Each function receives the entity instance and returns problems
25
25
  */
26
26
  validators?: Array<(instance: T) => Problem[] | Promise<Problem[]>>;
27
+ /**
28
+ * Whether to register this entity in the EntityRegistry.
29
+ * For dynamic schemas, defaults to false to prevent memory leaks.
30
+ * Set to true if you need discriminated entity deserialization.
31
+ */
32
+ register?: boolean;
27
33
  }
28
34
  /**
29
35
  * Wrapper object returned by EntitySchema.define() with EntityUtils methods
@@ -1 +1 @@
1
- {"version":3,"file":"entity-definition.d.ts","sourceRoot":"","sources":["../../src/lib/entity-definition.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"entity-definition.d.ts","sourceRoot":"","sources":["../../src/lib/entity-definition.ts"],"names":[],"mappings":"AACA,OAAO,EAML,eAAe,EACf,KAAK,OAAO,EACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,0BAA0B,EAC1B,kCAAkC,EACnC,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,GAAG;IACzC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,OAAO,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IACtC;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC5C;;;OAGG;IACH,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC,KAAK,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACpE;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAEjC;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAEjC;;OAEG;IACH,SAAS,CACP,IAAI,EAAE,OAAO,GACZ,OAAO,CACN;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;KAAE,GAC/C;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,SAAS,CAAC;QAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;KAAE,CAC3D,CAAC;IAEF;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjD;;OAEG;IACH,gBAAgB,CACd,IAAI,EAAE,OAAO,GACZ,OAAO,CACN;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;KAAE,GACxD;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,SAAS,CAAC;QAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;KAAE,CAC3D,CAAC;IAEF;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC;IAEhC;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAE/C;;OAEG;IACH,UAAU,CACR,QAAQ,EAAE,CAAC,EACX,IAAI,EAAE,OAAO,GACZ,OAAO,CACN;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;KAAE,GAC/C;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;KAAE,CACnD,CAAC;IAEF;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAE1C;;OAEG;IACH,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC;IAE5B;;OAEG;IACH,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAEjE;;OAEG;IACH,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACpC;AA0ED;;GAEG;AACH,qBAAa,YAAY;IACvB;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,MAAM,GAAG,GAAG,EAClC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC5B,mBAAmB,CAAC,CAAC,CAAC;CAuF1B;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW;IACtB;;;;;;;;;;OAUG;;IAGH;;;;;;;;;;OAUG;;IAGH;;;;;;;;;;OAUG;;IAGH;;;;;;;;;OASG;;IAGH;;;;;;;;;;OAUG;;IAGH;;;;;;;;;;OAUG;;IAGH;;;;;;;;;OASG;;IAGH;;;;;;;;;;OAUG;;IAGH;;;;;;;;;;OAUG;;IAGH;;;;;;;;;OASG;;IAGH;;;;;;;;;;;;;OAaG;;IAGH;;;;;;;;;OASG;;CAEJ,CAAC"}
@@ -20,8 +20,10 @@ import { zodPropertyOptions } from './zod-property.js';
20
20
  name: entityName
21
21
  };
22
22
  Reflect.defineMetadata(ENTITY_OPTIONS_METADATA_KEY, entityOptions, entityClass);
23
- // 3. Register in EntityRegistry
24
- EntityRegistry.register(entityName, entityClass);
23
+ // 3. Register in EntityRegistry if enabled
24
+ if (config.register) {
25
+ EntityRegistry.register(entityName, entityClass);
26
+ }
25
27
  // 4. Set property keys
26
28
  const propertyKeys = Object.keys(config.properties);
27
29
  Reflect.defineMetadata(PROPERTY_METADATA_KEY, propertyKeys, entityClass.prototype);
@@ -80,7 +82,8 @@ import { zodPropertyOptions } from './zod-property.js';
80
82
  name: config.name,
81
83
  options: config.options,
82
84
  properties: config.properties,
83
- validators: config.validators
85
+ validators: config.validators,
86
+ register: config.register ?? false
84
87
  });
85
88
  // Create wrapper with EntityUtils methods
86
89
  const wrapper = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/entity-definition.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n ENTITY_METADATA_KEY,\n ENTITY_OPTIONS_METADATA_KEY,\n ENTITY_VALIDATOR_METADATA_KEY,\n PROPERTY_METADATA_KEY,\n PROPERTY_OPTIONS_METADATA_KEY,\n PropertyOptions,\n type AnyCtor,\n} from './types.js';\nimport { EntityOptions } from './entity.js';\nimport { EntityRegistry } from './entity-registry.js';\nimport { EntityUtils } from './entity-utils.js';\nimport { Problem } from './problem.js';\nimport {\n stringPropertyOptions,\n enumPropertyOptions,\n numberPropertyOptions,\n intPropertyOptions,\n booleanPropertyOptions,\n datePropertyOptions,\n bigIntPropertyOptions,\n entityPropertyOptions,\n arrayPropertyOptions,\n passthroughPropertyOptions,\n discriminatedEntityPropertyOptions,\n} from './property.js';\nimport { zodPropertyOptions } from './zod-property.js';\n\n/**\n * Configuration for defining an entity schema\n */\nexport interface EntitySchemaConfig<T = any> {\n /**\n * Name for the entity (required for schema-based entities)\n */\n name: string;\n /**\n * Entity options (collection, stringifiable, etc.)\n */\n options?: Omit<EntityOptions, 'name'>;\n /**\n * Property definitions - key is property name, value is property options\n */\n properties: Record<string, PropertyOptions>;\n /**\n * Validator functions that will be attached to the entity class\n * Each function receives the entity instance and returns problems\n */\n validators?: Array<(instance: T) => Problem[] | Promise<Problem[]>>;\n}\n\n/**\n * Wrapper object returned by EntitySchema.define() with EntityUtils methods\n */\nexport interface EntitySchemaWrapper<T> {\n /**\n * The generated entity class\n */\n readonly entityClass: AnyCtor<T>;\n\n /**\n * Parse data into an entity instance\n */\n parse(data: unknown): Promise<T>;\n\n /**\n * Parse data into an entity instance without throwing\n */\n safeParse(\n data: unknown,\n ): Promise<\n | { success: true; data: T; problems: Problem[] }\n | { success: false; data: undefined; problems: Problem[] }\n >;\n\n /**\n * Parse only present properties without throwing\n */\n parsePartial(data: unknown): Promise<Partial<T>>;\n\n /**\n * Parse only present properties without throwing\n */\n safeParsePartial(\n data: unknown,\n ): Promise<\n | { success: true; data: Partial<T>; problems: Problem[] }\n | { success: false; data: undefined; problems: Problem[] }\n >;\n\n /**\n * Serialize entity instance to plain object\n */\n serialize(instance: T): unknown;\n\n /**\n * Update entity instance with new data\n */\n update(instance: T, data: unknown): Promise<T>;\n\n /**\n * Update entity instance with new data without throwing\n */\n safeUpdate(\n instance: T,\n data: unknown,\n ): Promise<\n | { success: true; data: T; problems: Problem[] }\n | { success: false; data: T; problems: Problem[] }\n >;\n\n /**\n * Validate entity instance\n */\n validate(instance: T): Promise<Problem[]>;\n\n /**\n * Check if two instances are equal\n */\n equals(a: T, b: T): boolean;\n\n /**\n * Get differences between two instances\n */\n diff(a: T, b: T): Record<string, { from: unknown; to: unknown }>;\n\n /**\n * Get changed values as partial object\n */\n getChanges(a: T, b: T): Partial<T>;\n}\n\n/**\n * Define entity metadata on an existing class programmatically\n * This sets the same metadata that @Entity() and @Property() decorators would set\n *\n * @param entityClass - The class to define as an entity\n * @param config - Configuration with properties, options, and validators\n * @internal\n */\nfunction defineEntity<T>(\n entityClass: AnyCtor<T>,\n config: EntitySchemaConfig<T>,\n): void {\n const entityName = config.name;\n\n // 1. Mark as entity\n Reflect.defineMetadata(ENTITY_METADATA_KEY, true, entityClass);\n\n // 2. Set entity options with resolved name\n const entityOptions: EntityOptions = {\n ...config.options,\n name: entityName,\n };\n Reflect.defineMetadata(\n ENTITY_OPTIONS_METADATA_KEY,\n entityOptions,\n entityClass,\n );\n\n // 3. Register in EntityRegistry\n EntityRegistry.register(entityName, entityClass);\n\n // 4. Set property keys\n const propertyKeys = Object.keys(config.properties);\n Reflect.defineMetadata(\n PROPERTY_METADATA_KEY,\n propertyKeys,\n entityClass.prototype,\n );\n\n // 5. Set property options\n Reflect.defineMetadata(\n PROPERTY_OPTIONS_METADATA_KEY,\n config.properties,\n entityClass.prototype,\n );\n\n // 6. Set up validators if provided\n if (config.validators && config.validators.length > 0) {\n const validatorMethodNames: string[] = [];\n\n config.validators.forEach((validatorFn, index) => {\n const methodName = `__dynamicValidator${index}`;\n validatorMethodNames.push(methodName);\n\n // Attach validator as instance method that uses 'this'\n (entityClass.prototype as any)[methodName] = function ():\n | Problem[]\n | Promise<Problem[]> {\n return validatorFn(this);\n };\n });\n\n Reflect.defineMetadata(\n ENTITY_VALIDATOR_METADATA_KEY,\n validatorMethodNames,\n entityClass.prototype,\n );\n }\n}\n\n/**\n * Factory for creating schema-based entities without pre-defining a class\n */\nexport class EntitySchema {\n /**\n * Define a new entity schema and return a wrapper with EntityUtils methods\n *\n * @param config - Schema configuration with name, properties, options, and validators\n * @returns Wrapper object with parse, serialize, validate, etc. methods\n *\n * @example\n * ```typescript\n * const UserSchema = EntitySchema.define({\n * name: 'User',\n * properties: {\n * name: { type: () => String },\n * age: { type: () => Number, optional: true },\n * tags: { type: () => String, array: true },\n * },\n * validators: [\n * (user) => user.name.length < 2 ? [{ path: 'name', message: 'Too short' }] : []\n * ]\n * });\n *\n * const user = await UserSchema.parse({ name: 'John', age: 30, tags: ['admin'] });\n * const serialized = UserSchema.serialize(user);\n * const isValid = (await UserSchema.validate(user)).length === 0;\n * ```\n */\n static define<T extends object = any>(\n config: EntitySchemaConfig<T>,\n ): EntitySchemaWrapper<T> {\n // Generate anonymous entity class\n const entityClass = class {\n constructor(data: any) {\n Object.assign(this, data);\n }\n } as unknown as AnyCtor<T>;\n\n // Define entity metadata\n defineEntity(entityClass, {\n name: config.name,\n options: config.options,\n properties: config.properties,\n validators: config.validators,\n });\n\n // Create wrapper with EntityUtils methods\n const wrapper: EntitySchemaWrapper<T> = {\n entityClass,\n\n async parse(data: unknown): Promise<T> {\n return EntityUtils.parse(entityClass as any, data) as Promise<T>;\n },\n\n async safeParse(data: unknown) {\n return EntityUtils.safeParse(entityClass as any, data) as Promise<\n | { success: true; data: T; problems: Problem[] }\n | { success: false; data: undefined; problems: Problem[] }\n >;\n },\n\n async parsePartial(data: unknown): Promise<Partial<T>> {\n return EntityUtils.partialParse(entityClass as any, data) as Promise<\n Partial<T>\n >;\n },\n\n async safeParsePartial(data: unknown) {\n return EntityUtils.safePartialParse(\n entityClass as any,\n data,\n ) as unknown as Promise<\n | { success: true; data: Partial<T>; problems: Problem[] }\n | { success: false; data: undefined; problems: Problem[] }\n >;\n },\n\n serialize(instance: T): unknown {\n return EntityUtils.toJSON(instance as any);\n },\n\n async update(instance: T, data: unknown): Promise<T> {\n return EntityUtils.update(instance as any, data as any) as Promise<T>;\n },\n\n async safeUpdate(instance: T, data: unknown) {\n return EntityUtils.safeUpdate(instance as any, data as any) as Promise<\n | { success: true; data: T; problems: Problem[] }\n | { success: false; data: T; problems: Problem[] }\n >;\n },\n\n async validate(instance: T): Promise<Problem[]> {\n return EntityUtils.validate(instance as any);\n },\n\n equals(a: T, b: T): boolean {\n return EntityUtils.equals(a as any, b as any);\n },\n\n diff(a: T, b: T): Record<string, { from: unknown; to: unknown }> {\n const diffs = EntityUtils.diff(a as any, b as any);\n const result: Record<string, { from: unknown; to: unknown }> = {};\n for (const diff of diffs) {\n result[diff.property] = { from: diff.oldValue, to: diff.newValue };\n }\n return result;\n },\n\n getChanges(a: T, b: T): Partial<T> {\n return EntityUtils.changes(a as any, b as any) as Partial<T>;\n },\n };\n\n return wrapper;\n }\n}\n\n/**\n * Property definition helpers for EntitySchema\n * These mirror the decorator-based property helpers but return PropertyOptions objects\n */\nexport const EntityProps = {\n /**\n * String property with optional validation\n * @example\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * name: EntityProps.String({ minLength: 2, maxLength: 50 }),\n * email: EntityProps.String({ pattern: /^.+@.+\\..+$/ }),\n * }\n * })\n */\n String: stringPropertyOptions,\n\n /**\n * Enum property (validates string against enum values)\n * @example\n * enum Status { Active = 'active', Inactive = 'inactive' }\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * status: EntityProps.Enum(Status),\n * }\n * })\n */\n Enum: enumPropertyOptions,\n\n /**\n * Number property with optional min/max validation\n * @example\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * age: EntityProps.Number({ min: 0, max: 150 }),\n * score: EntityProps.Number({ optional: true }),\n * }\n * })\n */\n Number: numberPropertyOptions,\n\n /**\n * Integer property (number that must be an integer)\n * @example\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * age: EntityProps.Int({ min: 0 }),\n * }\n * })\n */\n Int: intPropertyOptions,\n\n /**\n * Boolean property\n * @example\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * isActive: EntityProps.Boolean(),\n * emailVerified: EntityProps.Boolean({ optional: true }),\n * }\n * })\n */\n Boolean: booleanPropertyOptions,\n\n /**\n * Date property\n * @example\n * EntitySchema.define({\n * name: 'Event',\n * properties: {\n * createdAt: EntityProps.Date(),\n * scheduledFor: EntityProps.Date({ optional: true }),\n * }\n * })\n */\n Date: datePropertyOptions,\n\n /**\n * BigInt property\n * @example\n * EntitySchema.define({\n * name: 'Transaction',\n * properties: {\n * amount: EntityProps.BigInt(),\n * }\n * })\n */\n BigInt: bigIntPropertyOptions,\n\n /**\n * Entity property (nested entity)\n * @example\n * const AddressSchema = EntitySchema.define({ ... });\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * address: EntityProps.Entity(() => AddressSchema.entityClass),\n * }\n * })\n */\n Entity: entityPropertyOptions,\n\n /**\n * Array property\n * @example\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * tags: EntityProps.Array(() => String),\n * addresses: EntityProps.Array(() => AddressSchema.entityClass),\n * }\n * })\n */\n Array: arrayPropertyOptions,\n\n /**\n * Passthrough property (no deserialization/validation)\n * @example\n * EntitySchema.define({\n * name: 'Config',\n * properties: {\n * metadata: EntityProps.Passthrough(),\n * }\n * })\n */\n Passthrough: passthroughPropertyOptions,\n\n /**\n * Zod schema property (validates using Zod schema)\n * @example\n * import { z } from 'zod';\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * data: EntityProps.Zod(z.object({\n * name: z.string().min(3),\n * age: z.number().int().min(0)\n * })),\n * }\n * })\n */\n Zod: zodPropertyOptions,\n\n /**\n * Discriminated entity property (for polymorphic entities with discriminator)\n * @example\n * EntitySchema.define({\n * name: 'Shape',\n * properties: {\n * shape: EntityProps.DiscriminatedEntity({ discriminatorProperty: 'type' }),\n * }\n * })\n */\n DiscriminatedEntity: discriminatedEntityPropertyOptions,\n};\n"],"names":["ENTITY_METADATA_KEY","ENTITY_OPTIONS_METADATA_KEY","ENTITY_VALIDATOR_METADATA_KEY","PROPERTY_METADATA_KEY","PROPERTY_OPTIONS_METADATA_KEY","EntityRegistry","EntityUtils","stringPropertyOptions","enumPropertyOptions","numberPropertyOptions","intPropertyOptions","booleanPropertyOptions","datePropertyOptions","bigIntPropertyOptions","entityPropertyOptions","arrayPropertyOptions","passthroughPropertyOptions","discriminatedEntityPropertyOptions","zodPropertyOptions","defineEntity","entityClass","config","entityName","name","Reflect","defineMetadata","entityOptions","options","register","propertyKeys","Object","keys","properties","prototype","validators","length","validatorMethodNames","forEach","validatorFn","index","methodName","push","EntitySchema","define","data","assign","wrapper","parse","safeParse","parsePartial","partialParse","safeParsePartial","safePartialParse","serialize","instance","toJSON","update","safeUpdate","validate","equals","a","b","diff","diffs","result","property","from","oldValue","to","newValue","getChanges","changes","EntityProps","String","Enum","Number","Int","Boolean","Date","BigInt","Entity","Array","Passthrough","Zod","DiscriminatedEntity"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/lib/entity-definition.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n ENTITY_METADATA_KEY,\n ENTITY_OPTIONS_METADATA_KEY,\n ENTITY_VALIDATOR_METADATA_KEY,\n PROPERTY_METADATA_KEY,\n PROPERTY_OPTIONS_METADATA_KEY,\n PropertyOptions,\n type AnyCtor,\n} from './types.js';\nimport { EntityOptions } from './entity.js';\nimport { EntityRegistry } from './entity-registry.js';\nimport { EntityUtils } from './entity-utils.js';\nimport { Problem } from './problem.js';\nimport {\n stringPropertyOptions,\n enumPropertyOptions,\n numberPropertyOptions,\n intPropertyOptions,\n booleanPropertyOptions,\n datePropertyOptions,\n bigIntPropertyOptions,\n entityPropertyOptions,\n arrayPropertyOptions,\n passthroughPropertyOptions,\n discriminatedEntityPropertyOptions,\n} from './property.js';\nimport { zodPropertyOptions } from './zod-property.js';\n\n/**\n * Configuration for defining an entity schema\n */\nexport interface EntitySchemaConfig<T = any> {\n /**\n * Name for the entity (required for schema-based entities)\n */\n name: string;\n /**\n * Entity options (collection, stringifiable, etc.)\n */\n options?: Omit<EntityOptions, 'name'>;\n /**\n * Property definitions - key is property name, value is property options\n */\n properties: Record<string, PropertyOptions>;\n /**\n * Validator functions that will be attached to the entity class\n * Each function receives the entity instance and returns problems\n */\n validators?: Array<(instance: T) => Problem[] | Promise<Problem[]>>;\n /**\n * Whether to register this entity in the EntityRegistry.\n * For dynamic schemas, defaults to false to prevent memory leaks.\n * Set to true if you need discriminated entity deserialization.\n */\n register?: boolean;\n}\n\n/**\n * Wrapper object returned by EntitySchema.define() with EntityUtils methods\n */\nexport interface EntitySchemaWrapper<T> {\n /**\n * The generated entity class\n */\n readonly entityClass: AnyCtor<T>;\n\n /**\n * Parse data into an entity instance\n */\n parse(data: unknown): Promise<T>;\n\n /**\n * Parse data into an entity instance without throwing\n */\n safeParse(\n data: unknown,\n ): Promise<\n | { success: true; data: T; problems: Problem[] }\n | { success: false; data: undefined; problems: Problem[] }\n >;\n\n /**\n * Parse only present properties without throwing\n */\n parsePartial(data: unknown): Promise<Partial<T>>;\n\n /**\n * Parse only present properties without throwing\n */\n safeParsePartial(\n data: unknown,\n ): Promise<\n | { success: true; data: Partial<T>; problems: Problem[] }\n | { success: false; data: undefined; problems: Problem[] }\n >;\n\n /**\n * Serialize entity instance to plain object\n */\n serialize(instance: T): unknown;\n\n /**\n * Update entity instance with new data\n */\n update(instance: T, data: unknown): Promise<T>;\n\n /**\n * Update entity instance with new data without throwing\n */\n safeUpdate(\n instance: T,\n data: unknown,\n ): Promise<\n | { success: true; data: T; problems: Problem[] }\n | { success: false; data: T; problems: Problem[] }\n >;\n\n /**\n * Validate entity instance\n */\n validate(instance: T): Promise<Problem[]>;\n\n /**\n * Check if two instances are equal\n */\n equals(a: T, b: T): boolean;\n\n /**\n * Get differences between two instances\n */\n diff(a: T, b: T): Record<string, { from: unknown; to: unknown }>;\n\n /**\n * Get changed values as partial object\n */\n getChanges(a: T, b: T): Partial<T>;\n}\n\n/**\n * Define entity metadata on an existing class programmatically\n * This sets the same metadata that @Entity() and @Property() decorators would set\n *\n * @param entityClass - The class to define as an entity\n * @param config - Configuration with properties, options, and validators\n * @internal\n */\nfunction defineEntity<T>(\n entityClass: AnyCtor<T>,\n config: EntitySchemaConfig<T>,\n): void {\n const entityName = config.name;\n\n // 1. Mark as entity\n Reflect.defineMetadata(ENTITY_METADATA_KEY, true, entityClass);\n\n // 2. Set entity options with resolved name\n const entityOptions: EntityOptions = {\n ...config.options,\n name: entityName,\n };\n Reflect.defineMetadata(\n ENTITY_OPTIONS_METADATA_KEY,\n entityOptions,\n entityClass,\n );\n\n // 3. Register in EntityRegistry if enabled\n if (config.register) {\n EntityRegistry.register(entityName, entityClass);\n }\n\n // 4. Set property keys\n const propertyKeys = Object.keys(config.properties);\n Reflect.defineMetadata(\n PROPERTY_METADATA_KEY,\n propertyKeys,\n entityClass.prototype,\n );\n\n // 5. Set property options\n Reflect.defineMetadata(\n PROPERTY_OPTIONS_METADATA_KEY,\n config.properties,\n entityClass.prototype,\n );\n\n // 6. Set up validators if provided\n if (config.validators && config.validators.length > 0) {\n const validatorMethodNames: string[] = [];\n\n config.validators.forEach((validatorFn, index) => {\n const methodName = `__dynamicValidator${index}`;\n validatorMethodNames.push(methodName);\n\n // Attach validator as instance method that uses 'this'\n (entityClass.prototype as any)[methodName] = function ():\n | Problem[]\n | Promise<Problem[]> {\n return validatorFn(this);\n };\n });\n\n Reflect.defineMetadata(\n ENTITY_VALIDATOR_METADATA_KEY,\n validatorMethodNames,\n entityClass.prototype,\n );\n }\n}\n\n/**\n * Factory for creating schema-based entities without pre-defining a class\n */\nexport class EntitySchema {\n /**\n * Define a new entity schema and return a wrapper with EntityUtils methods\n *\n * @param config - Schema configuration with name, properties, options, and validators\n * @returns Wrapper object with parse, serialize, validate, etc. methods\n *\n * @example\n * ```typescript\n * const UserSchema = EntitySchema.define({\n * name: 'User',\n * properties: {\n * name: { type: () => String },\n * age: { type: () => Number, optional: true },\n * tags: { type: () => String, array: true },\n * },\n * validators: [\n * (user) => user.name.length < 2 ? [{ path: 'name', message: 'Too short' }] : []\n * ]\n * });\n *\n * const user = await UserSchema.parse({ name: 'John', age: 30, tags: ['admin'] });\n * const serialized = UserSchema.serialize(user);\n * const isValid = (await UserSchema.validate(user)).length === 0;\n * ```\n */\n static define<T extends object = any>(\n config: EntitySchemaConfig<T>,\n ): EntitySchemaWrapper<T> {\n // Generate anonymous entity class\n const entityClass = class {\n constructor(data: any) {\n Object.assign(this, data);\n }\n } as unknown as AnyCtor<T>;\n\n // Define entity metadata\n defineEntity(entityClass, {\n name: config.name,\n options: config.options,\n properties: config.properties,\n validators: config.validators,\n register: config.register ?? false, // Default to false for dynamic schemas to prevent memory leaks\n });\n\n // Create wrapper with EntityUtils methods\n const wrapper: EntitySchemaWrapper<T> = {\n entityClass,\n\n async parse(data: unknown): Promise<T> {\n return EntityUtils.parse(entityClass as any, data) as Promise<T>;\n },\n\n async safeParse(data: unknown) {\n return EntityUtils.safeParse(entityClass as any, data) as Promise<\n | { success: true; data: T; problems: Problem[] }\n | { success: false; data: undefined; problems: Problem[] }\n >;\n },\n\n async parsePartial(data: unknown): Promise<Partial<T>> {\n return EntityUtils.partialParse(entityClass as any, data) as Promise<\n Partial<T>\n >;\n },\n\n async safeParsePartial(data: unknown) {\n return EntityUtils.safePartialParse(\n entityClass as any,\n data,\n ) as unknown as Promise<\n | { success: true; data: Partial<T>; problems: Problem[] }\n | { success: false; data: undefined; problems: Problem[] }\n >;\n },\n\n serialize(instance: T): unknown {\n return EntityUtils.toJSON(instance as any);\n },\n\n async update(instance: T, data: unknown): Promise<T> {\n return EntityUtils.update(instance as any, data as any) as Promise<T>;\n },\n\n async safeUpdate(instance: T, data: unknown) {\n return EntityUtils.safeUpdate(instance as any, data as any) as Promise<\n | { success: true; data: T; problems: Problem[] }\n | { success: false; data: T; problems: Problem[] }\n >;\n },\n\n async validate(instance: T): Promise<Problem[]> {\n return EntityUtils.validate(instance as any);\n },\n\n equals(a: T, b: T): boolean {\n return EntityUtils.equals(a as any, b as any);\n },\n\n diff(a: T, b: T): Record<string, { from: unknown; to: unknown }> {\n const diffs = EntityUtils.diff(a as any, b as any);\n const result: Record<string, { from: unknown; to: unknown }> = {};\n for (const diff of diffs) {\n result[diff.property] = { from: diff.oldValue, to: diff.newValue };\n }\n return result;\n },\n\n getChanges(a: T, b: T): Partial<T> {\n return EntityUtils.changes(a as any, b as any) as Partial<T>;\n },\n };\n\n return wrapper;\n }\n}\n\n/**\n * Property definition helpers for EntitySchema\n * These mirror the decorator-based property helpers but return PropertyOptions objects\n */\nexport const EntityProps = {\n /**\n * String property with optional validation\n * @example\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * name: EntityProps.String({ minLength: 2, maxLength: 50 }),\n * email: EntityProps.String({ pattern: /^.+@.+\\..+$/ }),\n * }\n * })\n */\n String: stringPropertyOptions,\n\n /**\n * Enum property (validates string against enum values)\n * @example\n * enum Status { Active = 'active', Inactive = 'inactive' }\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * status: EntityProps.Enum(Status),\n * }\n * })\n */\n Enum: enumPropertyOptions,\n\n /**\n * Number property with optional min/max validation\n * @example\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * age: EntityProps.Number({ min: 0, max: 150 }),\n * score: EntityProps.Number({ optional: true }),\n * }\n * })\n */\n Number: numberPropertyOptions,\n\n /**\n * Integer property (number that must be an integer)\n * @example\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * age: EntityProps.Int({ min: 0 }),\n * }\n * })\n */\n Int: intPropertyOptions,\n\n /**\n * Boolean property\n * @example\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * isActive: EntityProps.Boolean(),\n * emailVerified: EntityProps.Boolean({ optional: true }),\n * }\n * })\n */\n Boolean: booleanPropertyOptions,\n\n /**\n * Date property\n * @example\n * EntitySchema.define({\n * name: 'Event',\n * properties: {\n * createdAt: EntityProps.Date(),\n * scheduledFor: EntityProps.Date({ optional: true }),\n * }\n * })\n */\n Date: datePropertyOptions,\n\n /**\n * BigInt property\n * @example\n * EntitySchema.define({\n * name: 'Transaction',\n * properties: {\n * amount: EntityProps.BigInt(),\n * }\n * })\n */\n BigInt: bigIntPropertyOptions,\n\n /**\n * Entity property (nested entity)\n * @example\n * const AddressSchema = EntitySchema.define({ ... });\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * address: EntityProps.Entity(() => AddressSchema.entityClass),\n * }\n * })\n */\n Entity: entityPropertyOptions,\n\n /**\n * Array property\n * @example\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * tags: EntityProps.Array(() => String),\n * addresses: EntityProps.Array(() => AddressSchema.entityClass),\n * }\n * })\n */\n Array: arrayPropertyOptions,\n\n /**\n * Passthrough property (no deserialization/validation)\n * @example\n * EntitySchema.define({\n * name: 'Config',\n * properties: {\n * metadata: EntityProps.Passthrough(),\n * }\n * })\n */\n Passthrough: passthroughPropertyOptions,\n\n /**\n * Zod schema property (validates using Zod schema)\n * @example\n * import { z } from 'zod';\n * EntitySchema.define({\n * name: 'User',\n * properties: {\n * data: EntityProps.Zod(z.object({\n * name: z.string().min(3),\n * age: z.number().int().min(0)\n * })),\n * }\n * })\n */\n Zod: zodPropertyOptions,\n\n /**\n * Discriminated entity property (for polymorphic entities with discriminator)\n * @example\n * EntitySchema.define({\n * name: 'Shape',\n * properties: {\n * shape: EntityProps.DiscriminatedEntity({ discriminatorProperty: 'type' }),\n * }\n * })\n */\n DiscriminatedEntity: discriminatedEntityPropertyOptions,\n};\n"],"names":["ENTITY_METADATA_KEY","ENTITY_OPTIONS_METADATA_KEY","ENTITY_VALIDATOR_METADATA_KEY","PROPERTY_METADATA_KEY","PROPERTY_OPTIONS_METADATA_KEY","EntityRegistry","EntityUtils","stringPropertyOptions","enumPropertyOptions","numberPropertyOptions","intPropertyOptions","booleanPropertyOptions","datePropertyOptions","bigIntPropertyOptions","entityPropertyOptions","arrayPropertyOptions","passthroughPropertyOptions","discriminatedEntityPropertyOptions","zodPropertyOptions","defineEntity","entityClass","config","entityName","name","Reflect","defineMetadata","entityOptions","options","register","propertyKeys","Object","keys","properties","prototype","validators","length","validatorMethodNames","forEach","validatorFn","index","methodName","push","EntitySchema","define","data","assign","wrapper","parse","safeParse","parsePartial","partialParse","safeParsePartial","safePartialParse","serialize","instance","toJSON","update","safeUpdate","validate","equals","a","b","diff","diffs","result","property","from","oldValue","to","newValue","getChanges","changes","EntityProps","String","Enum","Number","Int","Boolean","Date","BigInt","Entity","Array","Passthrough","Zod","DiscriminatedEntity"],"mappings":"AAAA,qDAAqD,GACrD,SACEA,mBAAmB,EACnBC,2BAA2B,EAC3BC,6BAA6B,EAC7BC,qBAAqB,EACrBC,6BAA6B,QAGxB,aAAa;AAEpB,SAASC,cAAc,QAAQ,uBAAuB;AACtD,SAASC,WAAW,QAAQ,oBAAoB;AAEhD,SACEC,qBAAqB,EACrBC,mBAAmB,EACnBC,qBAAqB,EACrBC,kBAAkB,EAClBC,sBAAsB,EACtBC,mBAAmB,EACnBC,qBAAqB,EACrBC,qBAAqB,EACrBC,oBAAoB,EACpBC,0BAA0B,EAC1BC,kCAAkC,QAC7B,gBAAgB;AACvB,SAASC,kBAAkB,QAAQ,oBAAoB;AAgHvD;;;;;;;CAOC,GACD,SAASC,aACPC,WAAuB,EACvBC,MAA6B;IAE7B,MAAMC,aAAaD,OAAOE,IAAI;IAE9B,oBAAoB;IACpBC,QAAQC,cAAc,CAACzB,qBAAqB,MAAMoB;IAElD,2CAA2C;IAC3C,MAAMM,gBAA+B;QACnC,GAAGL,OAAOM,OAAO;QACjBJ,MAAMD;IACR;IACAE,QAAQC,cAAc,CACpBxB,6BACAyB,eACAN;IAGF,2CAA2C;IAC3C,IAAIC,OAAOO,QAAQ,EAAE;QACnBvB,eAAeuB,QAAQ,CAACN,YAAYF;IACtC;IAEA,uBAAuB;IACvB,MAAMS,eAAeC,OAAOC,IAAI,CAACV,OAAOW,UAAU;IAClDR,QAAQC,cAAc,CACpBtB,uBACA0B,cACAT,YAAYa,SAAS;IAGvB,0BAA0B;IAC1BT,QAAQC,cAAc,CACpBrB,+BACAiB,OAAOW,UAAU,EACjBZ,YAAYa,SAAS;IAGvB,mCAAmC;IACnC,IAAIZ,OAAOa,UAAU,IAAIb,OAAOa,UAAU,CAACC,MAAM,GAAG,GAAG;QACrD,MAAMC,uBAAiC,EAAE;QAEzCf,OAAOa,UAAU,CAACG,OAAO,CAAC,CAACC,aAAaC;YACtC,MAAMC,aAAa,CAAC,kBAAkB,EAAED,OAAO;YAC/CH,qBAAqBK,IAAI,CAACD;YAE1B,uDAAuD;YACtDpB,YAAYa,SAAS,AAAQ,CAACO,WAAW,GAAG;gBAG3C,OAAOF,YAAY,IAAI;YACzB;QACF;QAEAd,QAAQC,cAAc,CACpBvB,+BACAkC,sBACAhB,YAAYa,SAAS;IAEzB;AACF;AAEA;;CAEC,GACD,OAAO,MAAMS;IACX;;;;;;;;;;;;;;;;;;;;;;;;GAwBC,GACD,OAAOC,OACLtB,MAA6B,EACL;QACxB,kCAAkC;QAClC,MAAMD,cAAc;YAClB,YAAYwB,IAAS,CAAE;gBACrBd,OAAOe,MAAM,CAAC,IAAI,EAAED;YACtB;QACF;QAEA,yBAAyB;QACzBzB,aAAaC,aAAa;YACxBG,MAAMF,OAAOE,IAAI;YACjBI,SAASN,OAAOM,OAAO;YACvBK,YAAYX,OAAOW,UAAU;YAC7BE,YAAYb,OAAOa,UAAU;YAC7BN,UAAUP,OAAOO,QAAQ,IAAI;QAC/B;QAEA,0CAA0C;QAC1C,MAAMkB,UAAkC;YACtC1B;YAEA,MAAM2B,OAAMH,IAAa;gBACvB,OAAOtC,YAAYyC,KAAK,CAAC3B,aAAoBwB;YAC/C;YAEA,MAAMI,WAAUJ,IAAa;gBAC3B,OAAOtC,YAAY0C,SAAS,CAAC5B,aAAoBwB;YAInD;YAEA,MAAMK,cAAaL,IAAa;gBAC9B,OAAOtC,YAAY4C,YAAY,CAAC9B,aAAoBwB;YAGtD;YAEA,MAAMO,kBAAiBP,IAAa;gBAClC,OAAOtC,YAAY8C,gBAAgB,CACjChC,aACAwB;YAKJ;YAEAS,WAAUC,QAAW;gBACnB,OAAOhD,YAAYiD,MAAM,CAACD;YAC5B;YAEA,MAAME,QAAOF,QAAW,EAAEV,IAAa;gBACrC,OAAOtC,YAAYkD,MAAM,CAACF,UAAiBV;YAC7C;YAEA,MAAMa,YAAWH,QAAW,EAAEV,IAAa;gBACzC,OAAOtC,YAAYmD,UAAU,CAACH,UAAiBV;YAIjD;YAEA,MAAMc,UAASJ,QAAW;gBACxB,OAAOhD,YAAYoD,QAAQ,CAACJ;YAC9B;YAEAK,QAAOC,CAAI,EAAEC,CAAI;gBACf,OAAOvD,YAAYqD,MAAM,CAACC,GAAUC;YACtC;YAEAC,MAAKF,CAAI,EAAEC,CAAI;gBACb,MAAME,QAAQzD,YAAYwD,IAAI,CAACF,GAAUC;gBACzC,MAAMG,SAAyD,CAAC;gBAChE,KAAK,MAAMF,QAAQC,MAAO;oBACxBC,MAAM,CAACF,KAAKG,QAAQ,CAAC,GAAG;wBAAEC,MAAMJ,KAAKK,QAAQ;wBAAEC,IAAIN,KAAKO,QAAQ;oBAAC;gBACnE;gBACA,OAAOL;YACT;YAEAM,YAAWV,CAAI,EAAEC,CAAI;gBACnB,OAAOvD,YAAYiE,OAAO,CAACX,GAAUC;YACvC;QACF;QAEA,OAAOf;IACT;AACF;AAEA;;;CAGC,GACD,OAAO,MAAM0B,cAAc;IACzB;;;;;;;;;;GAUC,GACDC,QAAQlE;IAER;;;;;;;;;;GAUC,GACDmE,MAAMlE;IAEN;;;;;;;;;;GAUC,GACDmE,QAAQlE;IAER;;;;;;;;;GASC,GACDmE,KAAKlE;IAEL;;;;;;;;;;GAUC,GACDmE,SAASlE;IAET;;;;;;;;;;GAUC,GACDmE,MAAMlE;IAEN;;;;;;;;;GASC,GACDmE,QAAQlE;IAER;;;;;;;;;;GAUC,GACDmE,QAAQlE;IAER;;;;;;;;;;GAUC,GACDmE,OAAOlE;IAEP;;;;;;;;;GASC,GACDmE,aAAalE;IAEb;;;;;;;;;;;;;GAaC,GACDmE,KAAKjE;IAEL;;;;;;;;;GASC,GACDkE,qBAAqBnE;AACvB,EAAE"}
@@ -28,6 +28,12 @@ export interface EntityOptions {
28
28
  * Used by @CollectionEntity ('collection') and @Stringifiable ('value').
29
29
  */
30
30
  wrapperProperty?: string;
31
+ /**
32
+ * Whether to register this entity in the EntityRegistry.
33
+ * Set to false for dynamic entities to prevent memory leaks.
34
+ * Defaults to true.
35
+ */
36
+ register?: boolean;
31
37
  }
32
38
  /**
33
39
  * Decorator that marks a class as an Entity.
@@ -1 +1 @@
1
- {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../src/lib/entity.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,cAAc,CAqBlE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAM,GACxC,cAAc,CAMhB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,aAAa,CAC3B,OAAO,GAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAM,GACxC,cAAc,CAEhB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAmBjD"}
1
+ {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../src/lib/entity.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,cAAc,CAwBlE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAM,GACxC,cAAc,CAMhB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,aAAa,CAC3B,OAAO,GAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAM,GACxC,cAAc,CAEhB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAmBjD"}
@@ -31,8 +31,11 @@ import { EntityRegistry } from './entity-registry.js';
31
31
  Reflect.defineMetadata(ENTITY_METADATA_KEY, true, target);
32
32
  // Determine the entity name - use provided name or fall back to class name
33
33
  const entityName = options.name ?? target.name;
34
- // Register the entity in the global registry
35
- EntityRegistry.register(entityName, target);
34
+ // Register the entity in the global registry (unless explicitly disabled)
35
+ const shouldRegister = options.register !== false;
36
+ if (shouldRegister) {
37
+ EntityRegistry.register(entityName, target);
38
+ }
36
39
  // Store the name in options for later retrieval
37
40
  const optionsWithName = {
38
41
  ...options,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/entity.ts"],"sourcesContent":["import {\n ENTITY_METADATA_KEY,\n ENTITY_OPTIONS_METADATA_KEY,\n ENTITY_VALIDATOR_METADATA_KEY,\n} from './types.js';\nimport { EntityRegistry } from './entity-registry.js';\n\n/**\n * Options for Entity decorator\n */\nexport interface EntityOptions {\n /**\n * Optional name for the entity. Used for discriminated entity deserialization.\n * If not specified, the class name will be used.\n * Must be unique across all entities - conflicts will throw an error.\n */\n name?: string;\n /**\n * Whether this entity represents a collection.\n * Collection entities must have a 'collection' property that is an array.\n * When serialized, collection entities are unwrapped to just their array.\n * When deserialized from an array, the array is wrapped in { collection: [...] }.\n */\n collection?: boolean;\n /**\n * Whether this entity represents a stringifiable value.\n * Stringifiable classes must have a 'value' property that is a string.\n * When serialized, stringifiable instances are unwrapped to just their string.\n * When deserialized from a string, the string is wrapped in { value: \"...\" }.\n */\n stringifiable?: boolean;\n /**\n * The name of the single \"wrapper\" property for entities that act as transparent wrappers.\n * When set, this property name is excluded from error paths during validation.\n * Used by @CollectionEntity ('collection') and @Stringifiable ('value').\n */\n wrapperProperty?: string;\n}\n\n/**\n * Decorator that marks a class as an Entity.\n * This allows us to identify entity instances later.\n *\n * @param options - Optional configuration for the entity\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * name: string;\n * }\n *\n * @Entity({ name: 'CustomUser' })\n * class User {\n * name: string;\n * }\n *\n * @Entity({ collection: true })\n * class Tags {\n * @ArrayProperty(() => String)\n * collection: string[];\n * }\n * ```\n */\nexport function Entity(options: EntityOptions = {}): ClassDecorator {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n return function (target: Function) {\n // Store metadata on the class constructor\n Reflect.defineMetadata(ENTITY_METADATA_KEY, true, target);\n\n // Determine the entity name - use provided name or fall back to class name\n const entityName = options.name ?? target.name;\n\n // Register the entity in the global registry\n EntityRegistry.register(entityName, target);\n\n // Store the name in options for later retrieval\n const optionsWithName = { ...options, name: entityName };\n\n Reflect.defineMetadata(\n ENTITY_OPTIONS_METADATA_KEY,\n optionsWithName,\n target,\n );\n };\n}\n\n/**\n * Decorator that marks a class as a Collection Entity.\n * This is syntax sugar for @Entity({ collection: true }).\n *\n * Collection entities must have a 'collection' property that is an array.\n * When serialized with EntityUtils.toJSON(), they are unwrapped to just the array.\n * When deserialized from an array, the array is wrapped in { collection: [...] }.\n *\n * @example\n * ```typescript\n * @CollectionEntity()\n * class Tags {\n * @ArrayProperty(() => String)\n * readonly collection: string[];\n *\n * constructor(data: { collection: string[] }) {\n * this.collection = data.collection;\n * }\n * }\n *\n * @Entity()\n * class Article {\n * @EntityProperty(() => Tags)\n * tags!: Tags;\n * }\n *\n * const article = new Article(...);\n * const json = EntityUtils.toJSON(article);\n * // { tags: [\"tag1\", \"tag2\"] } - unwrapped to array\n *\n * // Also works when serializing the collection directly:\n * const tagsJson = EntityUtils.toJSON(tags);\n * // [\"tag1\", \"tag2\"] - unwrapped to array\n * ```\n */\nexport function CollectionEntity(\n options: Pick<EntityOptions, 'name'> = {},\n): ClassDecorator {\n return Entity({\n collection: true,\n wrapperProperty: 'collection',\n ...options,\n });\n}\n\n/**\n * Decorator that marks a class as Stringifiable.\n * This is syntax sugar for @Entity({ stringifiable: true }).\n *\n * Stringifiable classes must have a 'value' property that is a string.\n * When serialized with EntityUtils.toJSON(), they are unwrapped to just the string.\n * When deserialized from a string, the string is wrapped in { value: \"...\" }.\n *\n * @example\n * ```typescript\n * @Stringifiable()\n * class UserId {\n * @StringProperty()\n * readonly value: string;\n *\n * constructor(data: { value: string }) {\n * this.value = data.value;\n * }\n * }\n *\n * @Entity()\n * class User {\n * @EntityProperty(() => UserId)\n * id!: UserId;\n * }\n *\n * const user = new User(...);\n * const json = EntityUtils.toJSON(user);\n * // { id: \"user-123\" } - unwrapped to string\n *\n * // Also works when serializing the stringifiable directly:\n * const userId = new UserId({ value: \"user-123\" });\n * const idJson = EntityUtils.toJSON(userId);\n * // \"user-123\" - unwrapped to string\n *\n * // Parse from string:\n * const parsed = await EntityUtils.parse(UserId, \"user-456\");\n * // UserId { value: \"user-456\" }\n * ```\n */\nexport function Stringifiable(\n options: Pick<EntityOptions, 'name'> = {},\n): ClassDecorator {\n return Entity({ stringifiable: true, wrapperProperty: 'value', ...options });\n}\n\n/**\n * Decorator that marks a method as an entity validator.\n * The method should return an array of Problems.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) firstName!: string;\n * @Property({ type: () => String }) lastName!: string;\n *\n * @EntityValidator()\n * validateNames(): Problem[] {\n * const problems: Problem[] = [];\n * if (this.firstName === this.lastName) {\n * problems.push(new Problem({\n * property: 'firstName',\n * message: 'First and last name cannot be the same'\n * }));\n * }\n * return problems;\n * }\n * }\n * ```\n */\nexport function EntityValidator(): MethodDecorator {\n return (target: object, propertyKey: string | symbol): void => {\n if (typeof propertyKey !== 'string') {\n return;\n }\n\n const existingValidators: string[] =\n Reflect.getOwnMetadata(ENTITY_VALIDATOR_METADATA_KEY, target) || [];\n\n if (!existingValidators.includes(propertyKey)) {\n existingValidators.push(propertyKey);\n }\n\n Reflect.defineMetadata(\n ENTITY_VALIDATOR_METADATA_KEY,\n existingValidators,\n target,\n );\n };\n}\n"],"names":["ENTITY_METADATA_KEY","ENTITY_OPTIONS_METADATA_KEY","ENTITY_VALIDATOR_METADATA_KEY","EntityRegistry","Entity","options","target","Reflect","defineMetadata","entityName","name","register","optionsWithName","CollectionEntity","collection","wrapperProperty","Stringifiable","stringifiable","EntityValidator","propertyKey","existingValidators","getOwnMetadata","includes","push"],"mappings":"AAAA,SACEA,mBAAmB,EACnBC,2BAA2B,EAC3BC,6BAA6B,QACxB,aAAa;AACpB,SAASC,cAAc,QAAQ,uBAAuB;AAkCtD;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASC,OAAOC,UAAyB,CAAC,CAAC;IAChD,sEAAsE;IACtE,OAAO,SAAUC,MAAgB;QAC/B,0CAA0C;QAC1CC,QAAQC,cAAc,CAACR,qBAAqB,MAAMM;QAElD,2EAA2E;QAC3E,MAAMG,aAAaJ,QAAQK,IAAI,IAAIJ,OAAOI,IAAI;QAE9C,6CAA6C;QAC7CP,eAAeQ,QAAQ,CAACF,YAAYH;QAEpC,gDAAgD;QAChD,MAAMM,kBAAkB;YAAE,GAAGP,OAAO;YAAEK,MAAMD;QAAW;QAEvDF,QAAQC,cAAc,CACpBP,6BACAW,iBACAN;IAEJ;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCC,GACD,OAAO,SAASO,iBACdR,UAAuC,CAAC,CAAC;IAEzC,OAAOD,OAAO;QACZU,YAAY;QACZC,iBAAiB;QACjB,GAAGV,OAAO;IACZ;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCC,GACD,OAAO,SAASW,cACdX,UAAuC,CAAC,CAAC;IAEzC,OAAOD,OAAO;QAAEa,eAAe;QAAMF,iBAAiB;QAAS,GAAGV,OAAO;IAAC;AAC5E;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASa;IACd,OAAO,CAACZ,QAAgBa;QACtB,IAAI,OAAOA,gBAAgB,UAAU;YACnC;QACF;QAEA,MAAMC,qBACJb,QAAQc,cAAc,CAACnB,+BAA+BI,WAAW,EAAE;QAErE,IAAI,CAACc,mBAAmBE,QAAQ,CAACH,cAAc;YAC7CC,mBAAmBG,IAAI,CAACJ;QAC1B;QAEAZ,QAAQC,cAAc,CACpBN,+BACAkB,oBACAd;IAEJ;AACF"}
1
+ {"version":3,"sources":["../../src/lib/entity.ts"],"sourcesContent":["import {\n ENTITY_METADATA_KEY,\n ENTITY_OPTIONS_METADATA_KEY,\n ENTITY_VALIDATOR_METADATA_KEY,\n} from './types.js';\nimport { EntityRegistry } from './entity-registry.js';\n\n/**\n * Options for Entity decorator\n */\nexport interface EntityOptions {\n /**\n * Optional name for the entity. Used for discriminated entity deserialization.\n * If not specified, the class name will be used.\n * Must be unique across all entities - conflicts will throw an error.\n */\n name?: string;\n /**\n * Whether this entity represents a collection.\n * Collection entities must have a 'collection' property that is an array.\n * When serialized, collection entities are unwrapped to just their array.\n * When deserialized from an array, the array is wrapped in { collection: [...] }.\n */\n collection?: boolean;\n /**\n * Whether this entity represents a stringifiable value.\n * Stringifiable classes must have a 'value' property that is a string.\n * When serialized, stringifiable instances are unwrapped to just their string.\n * When deserialized from a string, the string is wrapped in { value: \"...\" }.\n */\n stringifiable?: boolean;\n /**\n * The name of the single \"wrapper\" property for entities that act as transparent wrappers.\n * When set, this property name is excluded from error paths during validation.\n * Used by @CollectionEntity ('collection') and @Stringifiable ('value').\n */\n wrapperProperty?: string;\n /**\n * Whether to register this entity in the EntityRegistry.\n * Set to false for dynamic entities to prevent memory leaks.\n * Defaults to true.\n */\n register?: boolean;\n}\n\n/**\n * Decorator that marks a class as an Entity.\n * This allows us to identify entity instances later.\n *\n * @param options - Optional configuration for the entity\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * name: string;\n * }\n *\n * @Entity({ name: 'CustomUser' })\n * class User {\n * name: string;\n * }\n *\n * @Entity({ collection: true })\n * class Tags {\n * @ArrayProperty(() => String)\n * collection: string[];\n * }\n * ```\n */\nexport function Entity(options: EntityOptions = {}): ClassDecorator {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n return function (target: Function) {\n // Store metadata on the class constructor\n Reflect.defineMetadata(ENTITY_METADATA_KEY, true, target);\n\n // Determine the entity name - use provided name or fall back to class name\n const entityName = options.name ?? target.name;\n\n // Register the entity in the global registry (unless explicitly disabled)\n const shouldRegister = options.register !== false;\n if (shouldRegister) {\n EntityRegistry.register(entityName, target);\n }\n\n // Store the name in options for later retrieval\n const optionsWithName = { ...options, name: entityName };\n\n Reflect.defineMetadata(\n ENTITY_OPTIONS_METADATA_KEY,\n optionsWithName,\n target,\n );\n };\n}\n\n/**\n * Decorator that marks a class as a Collection Entity.\n * This is syntax sugar for @Entity({ collection: true }).\n *\n * Collection entities must have a 'collection' property that is an array.\n * When serialized with EntityUtils.toJSON(), they are unwrapped to just the array.\n * When deserialized from an array, the array is wrapped in { collection: [...] }.\n *\n * @example\n * ```typescript\n * @CollectionEntity()\n * class Tags {\n * @ArrayProperty(() => String)\n * readonly collection: string[];\n *\n * constructor(data: { collection: string[] }) {\n * this.collection = data.collection;\n * }\n * }\n *\n * @Entity()\n * class Article {\n * @EntityProperty(() => Tags)\n * tags!: Tags;\n * }\n *\n * const article = new Article(...);\n * const json = EntityUtils.toJSON(article);\n * // { tags: [\"tag1\", \"tag2\"] } - unwrapped to array\n *\n * // Also works when serializing the collection directly:\n * const tagsJson = EntityUtils.toJSON(tags);\n * // [\"tag1\", \"tag2\"] - unwrapped to array\n * ```\n */\nexport function CollectionEntity(\n options: Pick<EntityOptions, 'name'> = {},\n): ClassDecorator {\n return Entity({\n collection: true,\n wrapperProperty: 'collection',\n ...options,\n });\n}\n\n/**\n * Decorator that marks a class as Stringifiable.\n * This is syntax sugar for @Entity({ stringifiable: true }).\n *\n * Stringifiable classes must have a 'value' property that is a string.\n * When serialized with EntityUtils.toJSON(), they are unwrapped to just the string.\n * When deserialized from a string, the string is wrapped in { value: \"...\" }.\n *\n * @example\n * ```typescript\n * @Stringifiable()\n * class UserId {\n * @StringProperty()\n * readonly value: string;\n *\n * constructor(data: { value: string }) {\n * this.value = data.value;\n * }\n * }\n *\n * @Entity()\n * class User {\n * @EntityProperty(() => UserId)\n * id!: UserId;\n * }\n *\n * const user = new User(...);\n * const json = EntityUtils.toJSON(user);\n * // { id: \"user-123\" } - unwrapped to string\n *\n * // Also works when serializing the stringifiable directly:\n * const userId = new UserId({ value: \"user-123\" });\n * const idJson = EntityUtils.toJSON(userId);\n * // \"user-123\" - unwrapped to string\n *\n * // Parse from string:\n * const parsed = await EntityUtils.parse(UserId, \"user-456\");\n * // UserId { value: \"user-456\" }\n * ```\n */\nexport function Stringifiable(\n options: Pick<EntityOptions, 'name'> = {},\n): ClassDecorator {\n return Entity({ stringifiable: true, wrapperProperty: 'value', ...options });\n}\n\n/**\n * Decorator that marks a method as an entity validator.\n * The method should return an array of Problems.\n *\n * @example\n * ```typescript\n * @Entity()\n * class User {\n * @Property({ type: () => String }) firstName!: string;\n * @Property({ type: () => String }) lastName!: string;\n *\n * @EntityValidator()\n * validateNames(): Problem[] {\n * const problems: Problem[] = [];\n * if (this.firstName === this.lastName) {\n * problems.push(new Problem({\n * property: 'firstName',\n * message: 'First and last name cannot be the same'\n * }));\n * }\n * return problems;\n * }\n * }\n * ```\n */\nexport function EntityValidator(): MethodDecorator {\n return (target: object, propertyKey: string | symbol): void => {\n if (typeof propertyKey !== 'string') {\n return;\n }\n\n const existingValidators: string[] =\n Reflect.getOwnMetadata(ENTITY_VALIDATOR_METADATA_KEY, target) || [];\n\n if (!existingValidators.includes(propertyKey)) {\n existingValidators.push(propertyKey);\n }\n\n Reflect.defineMetadata(\n ENTITY_VALIDATOR_METADATA_KEY,\n existingValidators,\n target,\n );\n };\n}\n"],"names":["ENTITY_METADATA_KEY","ENTITY_OPTIONS_METADATA_KEY","ENTITY_VALIDATOR_METADATA_KEY","EntityRegistry","Entity","options","target","Reflect","defineMetadata","entityName","name","shouldRegister","register","optionsWithName","CollectionEntity","collection","wrapperProperty","Stringifiable","stringifiable","EntityValidator","propertyKey","existingValidators","getOwnMetadata","includes","push"],"mappings":"AAAA,SACEA,mBAAmB,EACnBC,2BAA2B,EAC3BC,6BAA6B,QACxB,aAAa;AACpB,SAASC,cAAc,QAAQ,uBAAuB;AAwCtD;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASC,OAAOC,UAAyB,CAAC,CAAC;IAChD,sEAAsE;IACtE,OAAO,SAAUC,MAAgB;QAC/B,0CAA0C;QAC1CC,QAAQC,cAAc,CAACR,qBAAqB,MAAMM;QAElD,2EAA2E;QAC3E,MAAMG,aAAaJ,QAAQK,IAAI,IAAIJ,OAAOI,IAAI;QAE9C,0EAA0E;QAC1E,MAAMC,iBAAiBN,QAAQO,QAAQ,KAAK;QAC5C,IAAID,gBAAgB;YAClBR,eAAeS,QAAQ,CAACH,YAAYH;QACtC;QAEA,gDAAgD;QAChD,MAAMO,kBAAkB;YAAE,GAAGR,OAAO;YAAEK,MAAMD;QAAW;QAEvDF,QAAQC,cAAc,CACpBP,6BACAY,iBACAP;IAEJ;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCC,GACD,OAAO,SAASQ,iBACdT,UAAuC,CAAC,CAAC;IAEzC,OAAOD,OAAO;QACZW,YAAY;QACZC,iBAAiB;QACjB,GAAGX,OAAO;IACZ;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCC,GACD,OAAO,SAASY,cACdZ,UAAuC,CAAC,CAAC;IAEzC,OAAOD,OAAO;QAAEc,eAAe;QAAMF,iBAAiB;QAAS,GAAGX,OAAO;IAAC;AAC5E;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASc;IACd,OAAO,CAACb,QAAgBc;QACtB,IAAI,OAAOA,gBAAgB,UAAU;YACnC;QACF;QAEA,MAAMC,qBACJd,QAAQe,cAAc,CAACpB,+BAA+BI,WAAW,EAAE;QAErE,IAAI,CAACe,mBAAmBE,QAAQ,CAACH,cAAc;YAC7CC,mBAAmBG,IAAI,CAACJ;QAC1B;QAEAb,QAAQC,cAAc,CACpBN,+BACAmB,oBACAf;IAEJ;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rtpaulino/entity",
3
- "version": "0.24.0",
3
+ "version": "0.24.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",