Initial commit - Event Planner application
This commit is contained in:
66
node_modules/@mikro-orm/core/serialization/EntitySerializer.d.ts
generated
vendored
Normal file
66
node_modules/@mikro-orm/core/serialization/EntitySerializer.d.ts
generated
vendored
Normal 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>>;
|
||||
255
node_modules/@mikro-orm/core/serialization/EntitySerializer.js
generated
vendored
Normal file
255
node_modules/@mikro-orm/core/serialization/EntitySerializer.js
generated
vendored
Normal 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);
|
||||
}
|
||||
14
node_modules/@mikro-orm/core/serialization/EntityTransformer.d.ts
generated
vendored
Normal file
14
node_modules/@mikro-orm/core/serialization/EntityTransformer.d.ts
generated
vendored
Normal 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;
|
||||
}
|
||||
234
node_modules/@mikro-orm/core/serialization/EntityTransformer.js
generated
vendored
Normal file
234
node_modules/@mikro-orm/core/serialization/EntityTransformer.js
generated
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
35
node_modules/@mikro-orm/core/serialization/SerializationContext.d.ts
generated
vendored
Normal file
35
node_modules/@mikro-orm/core/serialization/SerializationContext.d.ts
generated
vendored
Normal 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;
|
||||
}
|
||||
125
node_modules/@mikro-orm/core/serialization/SerializationContext.js
generated
vendored
Normal file
125
node_modules/@mikro-orm/core/serialization/SerializationContext.js
generated
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
3
node_modules/@mikro-orm/core/serialization/index.d.ts
generated
vendored
Normal file
3
node_modules/@mikro-orm/core/serialization/index.d.ts
generated
vendored
Normal 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
3
node_modules/@mikro-orm/core/serialization/index.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './EntityTransformer.js';
|
||||
export * from './EntitySerializer.js';
|
||||
export * from './SerializationContext.js';
|
||||
Reference in New Issue
Block a user