Initial commit - Event Planner application

This commit is contained in:
mberlin
2026-03-18 14:55:56 -03:00
commit 86d779eb4d
7548 changed files with 1006324 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
import type {
ArrayElement,
AutoPath,
CleanTypeConfig,
SerializeDTO,
FromEntityType,
TypeConfig,
UnboxArray,
} from '../typings.js';
import { type PopulatePath } from '../enums.js';
/** Converts entity instances to plain DTOs via `serialize()`, with fine-grained control over populate, exclude, and serialization groups. */
export declare class EntitySerializer {
/** Serializes an entity to a plain DTO, with fine-grained control over population, exclusion, groups, and custom types. */
static serialize<T extends object, P extends string = never, E extends string = never>(
entity: T,
options?: SerializeOptions<T, P, E>,
): SerializeDTO<T, P, E>;
private static propertyName;
private static processProperty;
private static processCustomType;
private static extractChildOptions;
private static processEntity;
private static processCollection;
}
export interface SerializeOptions<T, P extends string = never, E extends string = never> {
/** Specify which relation should be serialized as populated and which as a FK. */
populate?: readonly AutoPath<T, P, `${PopulatePath.ALL}`>[];
/** Specify which properties should be omitted. */
exclude?: readonly AutoPath<T, E>[];
/** Enforce unpopulated references to be returned as objects, e.g. `{ author: { id: 1 } }` instead of `{ author: 1 }`. */
forceObject?: boolean;
/** Ignore custom property serializers. */
ignoreSerializers?: boolean;
/** Include properties marked as `hidden`. */
includeHidden?: boolean;
/** Skip properties with `null` value. */
skipNull?: boolean;
/** Only include properties for a specific group. If a property does not specify any group, it will be included, otherwise only properties with a matching group are included. */
groups?: string[];
/** Convert custom types to their database representation. By default, the `Type.toJSON` method is invoked instead. */
convertCustomTypes?: boolean;
}
/**
* Converts entity instance to POJO, converting the `Collection`s to arrays and unwrapping the `Reference` wrapper, while respecting the serialization options.
* This method accepts either a single entity or an array of entities, and returns the corresponding POJO or an array of POJO.
* To serialize a single entity, you can also use `wrap(entity).serialize()` which handles a single entity only.
*
* ```ts
* const dtos = serialize([user1, user, ...], { exclude: ['id', 'email'], forceObject: true });
* const [dto2, dto3] = serialize([user2, user3], { exclude: ['id', 'email'], forceObject: true });
* const dto1 = serialize(user, { exclude: ['id', 'email'], forceObject: true });
* const dto2 = wrap(user).serialize({ exclude: ['id', 'email'], forceObject: true });
* ```
*/
export declare function serialize<
Entity extends object,
Naked extends FromEntityType<Entity> = FromEntityType<Entity>,
Populate extends string = never,
Exclude extends string = never,
Config extends TypeConfig = never,
>(
entity: Entity,
options?: Config & SerializeOptions<UnboxArray<Entity>, Populate, Exclude>,
): Naked extends object[]
? SerializeDTO<ArrayElement<Naked>, Populate, Exclude, CleanTypeConfig<Config>>[]
: SerializeDTO<Naked, Populate, Exclude, CleanTypeConfig<Config>>;

View File

@@ -0,0 +1,255 @@
import { helper } from '../entity/wrap.js';
import { Utils } from '../utils/Utils.js';
import { ReferenceKind } from '../enums.js';
import { Reference } from '../entity/Reference.js';
import { SerializationContext } from './SerializationContext.js';
import { isRaw } from '../utils/RawQueryFragment.js';
function isVisible(meta, propName, options) {
const prop = meta.properties[propName];
if (options.groups && prop?.groups) {
return prop.groups.some(g => options.groups.includes(g));
}
if (
Array.isArray(options.populate) &&
options.populate?.find(item => item === propName || item.startsWith(propName + '.') || item === '*')
) {
return true;
}
if (options.exclude?.find(item => item === propName)) {
return false;
}
const visible = prop && !(prop.hidden && !options.includeHidden);
const prefixed = prop && !prop.primary && !prop.accessor && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
return visible && !prefixed;
}
function isPopulated(propName, options) {
if (
typeof options.populate !== 'boolean' &&
options.populate?.find(item => item === propName || item.startsWith(propName + '.') || item === '*')
) {
return true;
}
if (typeof options.populate === 'boolean') {
return options.populate;
}
return false;
}
/** Converts entity instances to plain DTOs via `serialize()`, with fine-grained control over populate, exclude, and serialization groups. */
export class EntitySerializer {
/** Serializes an entity to a plain DTO, with fine-grained control over population, exclusion, groups, and custom types. */
static serialize(entity, options = {}) {
const wrapped = helper(entity);
const meta = wrapped.__meta;
let contextCreated = false;
if (!wrapped.__serializationContext.root) {
const root = new SerializationContext();
SerializationContext.propagate(
root,
entity,
(meta, prop) => meta.properties[prop]?.kind !== ReferenceKind.SCALAR,
);
options.populate = options.populate ? Utils.asArray(options.populate) : options.populate;
contextCreated = true;
}
const root = wrapped.__serializationContext.root;
const ret = {};
const props = new Set();
if (meta.serializedPrimaryKey && !meta.compositePK) {
props.add(meta.serializedPrimaryKey);
} else {
meta.primaryKeys.forEach(pk => props.add(pk));
}
if (wrapped.isInitialized() || !wrapped.hasPrimaryKey()) {
const entityKeys = new Set(Object.keys(entity));
for (const prop of meta.props) {
if (entityKeys.has(prop.name) || (prop.getter && prop.accessor === prop.name)) {
props.add(prop.name);
}
}
for (const key of entityKeys) {
if (!meta.properties[key]) {
props.add(key);
}
}
}
const visited = root.visited.has(entity);
if (!visited) {
root.visited.add(entity);
}
for (const prop of props) {
if (!isVisible(meta, prop, options)) {
continue;
}
const cycle = root.visit(meta.class, prop);
if (cycle && visited) {
continue;
}
const val = this.processProperty(prop, entity, options);
if (!cycle) {
root.leave(meta.class, prop);
}
if (options.skipNull && Utils.isPlainObject(val)) {
Utils.dropUndefinedProperties(val, null);
}
if (isRaw(val)) {
throw new Error(`Trying to serialize raw SQL fragment: '${val.sql}'`);
}
const visible = typeof val !== 'undefined' && !(val === null && options.skipNull);
if (visible) {
ret[this.propertyName(meta, prop)] = val;
}
}
if (contextCreated) {
root.close();
}
if (!wrapped.isInitialized()) {
return ret;
}
for (const prop of meta.getterProps) {
// decorated get methods
if (prop.getterName != null) {
const visible = entity[prop.getterName] instanceof Function && isVisible(meta, prop.name, options);
if (visible) {
ret[this.propertyName(meta, prop.name)] = this.processProperty(prop.getterName, entity, options);
}
} else {
// decorated getters
const visible = typeof entity[prop.name] !== 'undefined' && isVisible(meta, prop.name, options);
if (visible) {
ret[this.propertyName(meta, prop.name)] = this.processProperty(prop.name, entity, options);
}
}
}
return ret;
}
static propertyName(meta, prop) {
/* v8 ignore next */
if (meta.properties[prop]?.serializedName) {
return meta.properties[prop].serializedName;
}
if (meta.properties[prop]?.primary && meta.serializedPrimaryKey) {
return meta.serializedPrimaryKey;
}
return prop;
}
static processProperty(prop, entity, options) {
const parts = prop.split('.');
prop = parts[0];
const wrapped = helper(entity);
const property = wrapped.__meta.properties[prop] ?? { name: prop };
const serializer = property?.serializer;
const value = entity[prop];
// getter method
if (entity[prop] instanceof Function) {
const returnValue = entity[prop]();
if (!options.ignoreSerializers && serializer) {
return serializer(returnValue, this.extractChildOptions(options, prop));
}
return returnValue;
}
/* v8 ignore next */
if (!options.ignoreSerializers && serializer) {
return serializer(value);
}
if (Utils.isCollection(value)) {
return this.processCollection(property, entity, options);
}
if (Utils.isEntity(value, true)) {
return this.processEntity(property, entity, wrapped.__platform, options);
}
if (Utils.isScalarReference(value)) {
return value.unwrap();
}
/* v8 ignore next */
if (property?.kind === ReferenceKind.EMBEDDED) {
if (Array.isArray(value)) {
return value.map(item => helper(item).toJSON());
}
if (Utils.isObject(value)) {
return helper(value).toJSON();
}
}
if (property.customType) {
return this.processCustomType(value, property, wrapped.__platform, options.convertCustomTypes);
}
return wrapped.__platform.normalizePrimaryKey(value);
}
static processCustomType(value, prop, platform, convertCustomTypes) {
if (!prop.customType) {
return value;
}
if (convertCustomTypes) {
return prop.customType.convertToDatabaseValue(value, platform, { mode: 'serialization' });
}
return prop.customType.toJSON(value, platform);
}
static extractChildOptions(options, prop) {
return {
...options,
populate: Array.isArray(options.populate)
? Utils.extractChildElements(options.populate, prop, '*')
: options.populate,
exclude: Array.isArray(options.exclude) ? Utils.extractChildElements(options.exclude, prop) : options.exclude,
};
}
static processEntity(prop, entity, platform, options) {
const child = Reference.unwrapReference(entity[prop.name]);
const wrapped = helper(child);
const populated = isPopulated(prop.name, options) && wrapped.isInitialized();
const expand = populated || !wrapped.__managed;
const meta = wrapped.__meta;
const childOptions = this.extractChildOptions(options, prop.name);
const visible = meta.primaryKeys.filter(prop => isVisible(meta, prop, childOptions));
if (expand) {
return this.serialize(child, childOptions);
}
const pk = this.processCustomType(wrapped.getPrimaryKey(), prop, wrapped.__platform, options.convertCustomTypes);
if (options.forceObject || wrapped.__config.get('serialization').forceObject) {
return Utils.primaryKeyToObject(meta, pk, visible);
}
if (Utils.isPlainObject(pk)) {
const pruned = Utils.primaryKeyToObject(meta, pk, visible);
if (visible.length === 1) {
return platform.normalizePrimaryKey(pruned[visible[0]]);
}
return pruned;
}
return platform.normalizePrimaryKey(pk);
}
static processCollection(prop, entity, options) {
const col = entity[prop.name];
if (!col.isInitialized()) {
return undefined;
}
return col.getItems(false).map(item => {
const populated = isPopulated(prop.name, options);
const wrapped = helper(item);
if (populated || !wrapped.__managed) {
return this.serialize(item, this.extractChildOptions(options, prop.name));
}
const pk = this.processCustomType(wrapped.getPrimaryKey(), prop, wrapped.__platform, options.convertCustomTypes);
if (options.forceObject || wrapped.__config.get('serialization').forceObject) {
return Utils.primaryKeyToObject(wrapped.__meta, pk);
}
return pk;
});
}
}
/**
* Converts entity instance to POJO, converting the `Collection`s to arrays and unwrapping the `Reference` wrapper, while respecting the serialization options.
* This method accepts either a single entity or an array of entities, and returns the corresponding POJO or an array of POJO.
* To serialize a single entity, you can also use `wrap(entity).serialize()` which handles a single entity only.
*
* ```ts
* const dtos = serialize([user1, user, ...], { exclude: ['id', 'email'], forceObject: true });
* const [dto2, dto3] = serialize([user2, user3], { exclude: ['id', 'email'], forceObject: true });
* const dto1 = serialize(user, { exclude: ['id', 'email'], forceObject: true });
* const dto2 = wrap(user).serialize({ exclude: ['id', 'email'], forceObject: true });
* ```
*/
export function serialize(entities, options) {
if (Array.isArray(entities)) {
return entities.map(e => EntitySerializer.serialize(e, options));
}
return EntitySerializer.serialize(entities, options);
}

View File

@@ -0,0 +1,14 @@
import type { EntityDTO, EntityKey } from '../typings.js';
/** Converts entity instances to plain objects via `toObject()`, respecting populate hints, hidden fields, and serialization context. */
export declare class EntityTransformer {
/** Converts an entity to a plain object, respecting populate hints, hidden fields, and custom serializers. */
static toObject<Entity extends object, Ignored extends EntityKey<Entity> = never>(
entity: Entity,
ignoreFields?: Ignored[],
raw?: boolean,
): Omit<EntityDTO<Entity>, Ignored>;
private static propertyName;
private static processProperty;
private static processEntity;
private static processCollection;
}

View File

@@ -0,0 +1,234 @@
import { helper, wrap } from '../entity/wrap.js';
import { Utils } from '../utils/Utils.js';
import { ReferenceKind } from '../enums.js';
import { SerializationContext } from './SerializationContext.js';
import { isRaw } from '../utils/RawQueryFragment.js';
function isVisible(meta, propName, ignoreFields = []) {
const prop = meta.properties[propName];
const visible = prop && !prop.hidden;
const prefixed = prop && !prop.primary && !prop.accessor && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
return visible && !prefixed && !ignoreFields.includes(propName);
}
/** Converts entity instances to plain objects via `toObject()`, respecting populate hints, hidden fields, and serialization context. */
export class EntityTransformer {
/** Converts an entity to a plain object, respecting populate hints, hidden fields, and custom serializers. */
static toObject(entity, ignoreFields = [], raw = false) {
if (!Array.isArray(ignoreFields)) {
ignoreFields = [];
}
const wrapped = helper(entity);
let contextCreated = false;
if (!wrapped) {
return entity;
}
if (!wrapped.__serializationContext.root) {
const root = new SerializationContext(
wrapped.__serializationContext.populate,
wrapped.__serializationContext.fields,
wrapped.__serializationContext.exclude,
);
SerializationContext.propagate(root, entity, isVisible);
contextCreated = true;
}
const root = wrapped.__serializationContext.root;
const meta = wrapped.__meta;
const ret = {};
const props = new Set();
if (meta.serializedPrimaryKey && !meta.compositePK) {
props.add(meta.serializedPrimaryKey);
} else {
meta.primaryKeys.forEach(pk => props.add(pk));
}
if (wrapped.isInitialized() || !wrapped.hasPrimaryKey()) {
const entityKeys = new Set(Object.keys(entity));
for (const prop of meta.props) {
if (entityKeys.has(prop.name) || (prop.getter && prop.accessor === prop.name)) {
props.add(prop.name);
}
}
for (const key of entityKeys) {
if (!meta.properties[key]) {
props.add(key);
}
}
}
const visited = root.visited.has(entity);
const includePrimaryKeys = wrapped.__config.get('serialization').includePrimaryKeys;
if (!visited) {
root.visited.add(entity);
}
for (const prop of props) {
const visible = raw ? meta.properties[prop] : isVisible(meta, prop, ignoreFields);
if (!visible) {
continue;
}
const populated = root.isMarkedAsPopulated(meta.class, prop);
if (!raw) {
const partiallyLoaded = root.isPartiallyLoaded(meta.class, prop);
const isPrimary = includePrimaryKeys && meta.properties[prop].primary;
if (!partiallyLoaded && !populated && !isPrimary) {
continue;
}
if (root.isExcluded(meta.class, prop) && !populated) {
continue;
}
}
const cycle = root.visit(meta.class, prop);
if (cycle && visited) {
continue;
}
const val = EntityTransformer.processProperty(prop, entity, raw, populated);
if (!cycle) {
root.leave(meta.class, prop);
}
if (isRaw(val)) {
throw new Error(`Trying to serialize raw SQL fragment: '${val.sql}'`);
}
if (typeof val === 'undefined') {
continue;
}
ret[this.propertyName(meta, prop, raw)] = val;
}
if (!wrapped.isInitialized() && wrapped.hasPrimaryKey()) {
return ret;
}
for (const prop of meta.getterProps) {
// decorated get methods
if (prop.getterName != null) {
const visible = !prop.hidden && entity[prop.getterName] instanceof Function;
const populated = root.isMarkedAsPopulated(meta.class, prop.name);
if (visible) {
ret[this.propertyName(meta, prop.name, raw)] = this.processProperty(prop.getterName, entity, raw, populated);
}
} else {
// decorated getters
const visible = !prop.hidden && typeof entity[prop.name] !== 'undefined';
const populated = root.isMarkedAsPopulated(meta.class, prop.name);
if (visible) {
ret[this.propertyName(meta, prop.name, raw)] = this.processProperty(prop.name, entity, raw, populated);
}
}
}
if (contextCreated) {
root.close();
}
return ret;
}
static propertyName(meta, prop, raw) {
if (raw) {
return prop;
}
if (meta.properties[prop].serializedName) {
return meta.properties[prop].serializedName;
}
if (meta.properties[prop].primary && meta.serializedPrimaryKey) {
return meta.serializedPrimaryKey;
}
return prop;
}
static processProperty(prop, entity, raw, populated) {
const wrapped = helper(entity);
const property = wrapped.__meta.properties[prop] ?? { name: prop };
const serializer = property?.serializer;
const value = entity[prop];
// getter method
if (entity[prop] instanceof Function) {
const returnValue = entity[prop]();
if (serializer && !raw) {
return serializer(returnValue);
}
return returnValue;
}
if (serializer && !raw) {
return serializer(value);
}
if (Utils.isCollection(value)) {
return EntityTransformer.processCollection(property, entity, raw, populated);
}
if (Utils.isEntity(value, true)) {
return EntityTransformer.processEntity(property, entity, wrapped.__platform, raw, populated);
}
if (Utils.isScalarReference(value)) {
return value.unwrap();
}
if (property.kind === ReferenceKind.EMBEDDED) {
if (Array.isArray(value)) {
return value.map(item => {
const wrapped = item && helper(item);
return wrapped ? wrapped.toJSON() : item;
});
}
const wrapped = value && helper(value);
return wrapped ? wrapped.toJSON() : value;
}
const customType = property?.customType;
if (customType) {
return customType.toJSON(value, wrapped.__platform);
}
if (property?.primary) {
return wrapped.__platform.normalizePrimaryKey(value);
}
return value;
}
static processEntity(prop, entity, platform, raw, populated) {
const child = entity[prop.name];
const wrapped = helper(child);
const meta = wrapped.__meta;
const visible = meta.primaryKeys.filter(prop => isVisible(meta, prop));
if (raw && wrapped.isInitialized() && child !== entity) {
return wrapped.toPOJO();
}
function isPopulated() {
if (wrapped.__populated != null) {
return wrapped.__populated;
}
if (populated) {
return true;
}
return !wrapped.__managed;
}
if (wrapped.isInitialized() && isPopulated() && child !== entity) {
return wrap(child).toJSON();
}
let pk = wrapped.getPrimaryKey();
if (prop.customType) {
pk = prop.customType.toJSON(pk, wrapped.__platform);
}
if (wrapped.__config.get('serialization').forceObject) {
return Utils.primaryKeyToObject(meta, pk, visible);
}
if (Utils.isPlainObject(pk)) {
const pruned = Utils.primaryKeyToObject(meta, pk, visible);
if (visible.length === 1) {
return platform.normalizePrimaryKey(pruned[visible[0]]);
}
return pruned;
}
return platform.normalizePrimaryKey(pk);
}
static processCollection(prop, entity, raw, populated) {
const col = entity[prop.name];
if (raw && col.isInitialized(true)) {
return col.map(item => helper(item).toPOJO());
}
if (col.shouldPopulate(populated)) {
return col.toArray();
}
if (col.isInitialized()) {
const wrapped = helper(entity);
const forceObject = wrapped.__config.get('serialization').forceObject;
return col.map(item => {
const wrapped = helper(item);
const pk = wrapped.getPrimaryKey();
if (prop.customType) {
return prop.customType.toJSON(pk, wrapped.__platform);
}
if (forceObject) {
return Utils.primaryKeyToObject(wrapped.__meta, pk);
}
return pk;
});
}
return undefined;
}
}

View File

@@ -0,0 +1,35 @@
import type { AnyEntity, EntityMetadata, EntityName, PopulateOptions } from '../typings.js';
/**
* Helper that allows to keep track of where we are currently at when serializing complex entity graph with cycles.
* Before we process a property, we call `visit` that checks if it is not a cycle path (but allows to pass cycles that
* are defined in populate hint). If not, we proceed and call `leave` afterwards.
*/
export declare class SerializationContext<T extends object> {
#private;
readonly path: [EntityName, string][];
readonly visited: Set<AnyEntity>;
constructor(populate?: PopulateOptions<T>[], fields?: Set<string>, exclude?: readonly string[]);
/**
* Returns true when there is a cycle detected.
*/
visit(entityName: EntityName, prop: string): boolean;
/** Removes the last entry from the visit path after processing a property. */
leave(entityName: EntityName, prop: string): void;
/** Cleans up the serialization context by removing root references from all tracked entities. */
close(): void;
/**
* When initializing new context, we need to propagate it to the whole entity graph recursively.
*/
static propagate(
root: SerializationContext<any>,
entity: AnyEntity,
isVisible: (meta: EntityMetadata, prop: string) => boolean,
): void;
/** Checks whether a property is explicitly listed in the populate hints for the current path. */
isMarkedAsPopulated(entityName: EntityName, prop: string): boolean;
/** Checks whether a property is excluded from serialization via the exclude list. */
isExcluded(entityName: EntityName, prop: string): boolean;
/** Checks whether a property is included in the partial fields selection for the current path. */
isPartiallyLoaded(entityName: EntityName, prop: string): boolean;
private register;
}

View File

@@ -0,0 +1,125 @@
import { Utils } from '../utils/Utils.js';
import { helper } from '../entity/wrap.js';
/**
* Helper that allows to keep track of where we are currently at when serializing complex entity graph with cycles.
* Before we process a property, we call `visit` that checks if it is not a cycle path (but allows to pass cycles that
* are defined in populate hint). If not, we proceed and call `leave` afterwards.
*/
export class SerializationContext {
path = [];
visited = new Set();
#entities = new Set();
#populate;
#fields;
#exclude;
constructor(populate = [], fields, exclude) {
this.#populate = populate;
this.#fields = fields;
this.#exclude = exclude;
}
/**
* Returns true when there is a cycle detected.
*/
visit(entityName, prop) {
if (!this.path.find(([cls, item]) => entityName === cls && prop === item)) {
this.path.push([entityName, prop]);
return false;
}
// check if the path is explicitly populated
if (!this.isMarkedAsPopulated(entityName, prop)) {
return true;
}
this.path.push([entityName, prop]);
return false;
}
/** Removes the last entry from the visit path after processing a property. */
leave(entityName, prop) {
const last = this.path.pop();
/* v8 ignore next */
if (last?.[0] !== entityName || last[1] !== prop) {
throw new Error(`Trying to leave wrong property: ${entityName}.${prop} instead of ${last?.join('.')}`);
}
}
/** Cleans up the serialization context by removing root references from all tracked entities. */
close() {
for (const entity of this.#entities) {
delete helper(entity).__serializationContext.root;
}
}
/**
* When initializing new context, we need to propagate it to the whole entity graph recursively.
*/
static propagate(root, entity, isVisible) {
root.register(entity);
const meta = helper(entity).__meta;
for (const key of Object.keys(entity)) {
if (!isVisible(meta, key)) {
continue;
}
const target = entity[key];
if (Utils.isEntity(target, true)) {
if (!target.__helper.__serializationContext.root) {
this.propagate(root, target, isVisible);
}
continue;
}
if (Utils.isCollection(target)) {
for (const item of target.getItems(false)) {
if (!item.__helper.__serializationContext.root) {
this.propagate(root, item, isVisible);
}
}
}
}
}
/** Checks whether a property is explicitly listed in the populate hints for the current path. */
isMarkedAsPopulated(entityName, prop) {
let populate = this.#populate ?? [];
for (const segment of this.path) {
const hints = populate.filter(p => p.field === segment[1]);
if (hints.length > 0) {
const childHints = [];
for (const hint of hints) {
// we need to check for cycles here too, as we could fall into endless loops for bidirectional relations
if (hint.all) {
return !this.path.find(([cls, item]) => entityName === cls && prop === item);
}
if (hint.children) {
childHints.push(...hint.children);
}
}
populate = childHints;
}
}
return !!populate?.some(p => p.field === prop);
}
/** Checks whether a property is excluded from serialization via the exclude list. */
isExcluded(entityName, prop) {
if (!this.#exclude || this.#exclude.length === 0) {
return false;
}
const fullPath = this.path.map(segment => segment[1] + '.').join('') + prop;
return this.#exclude.includes(fullPath);
}
/** Checks whether a property is included in the partial fields selection for the current path. */
isPartiallyLoaded(entityName, prop) {
if (!this.#fields) {
return true;
}
let fields = [...this.#fields];
for (const segment of this.path) {
/* v8 ignore next */
if (fields.length === 0) {
return true;
}
fields = fields
.filter(field => field.startsWith(`${segment[1]}.`) || field === '*')
.map(field => (field === '*' ? field : field.substring(segment[1].length + 1)));
}
return fields.some(p => p === prop || p === '*');
}
register(entity) {
helper(entity).__serializationContext.root = this;
this.#entities.add(entity);
}
}

View File

@@ -0,0 +1,3 @@
export * from './EntityTransformer.js';
export * from './EntitySerializer.js';
export * from './SerializationContext.js';

3
node_modules/@mikro-orm/core/serialization/index.js generated vendored Normal file
View File

@@ -0,0 +1,3 @@
export * from './EntityTransformer.js';
export * from './EntitySerializer.js';
export * from './SerializationContext.js';