@thisisagile/easy
Version:
Straightforward library for building domain-driven microservice architectures
65 lines (49 loc) • 2.23 kB
text/typescript
import 'reflect-metadata';
import { List, toList } from './List';
import { isDefined } from './Is';
import { on, use } from './Constructor';
import { Optional } from './Types';
import { Entry, entries, values } from './Object';
type MetaParseOptions = { initial?: any; skipUndefined?: boolean };
class ClassMeta {
constructor(
readonly subject: any,
private readonly data: any = (subject.prototype ?? subject).constructor
) {}
get = <T>(key: string): T => Reflect.getMetadata(key, this.data) as T;
set = <T>(key: string, value: T): T => {
Reflect.defineMetadata(key, value, this.data);
return value;
};
entries = <T = unknown>(): List<Entry<T>> => entries<T>(this.subject);
parse = <Out, Value>(f: (v: Value) => Out, options: MetaParseOptions = {}): Record<string, Out> => {
const { initial = {}, skipUndefined = false } = options;
return this.entries<Value>().reduce((a, [k, val]) => use(f(val), v => (isDefined(v) || !skipUndefined ? on(a, a => (a[k] = v)) : a)), initial);
};
properties = (key?: string): List<PropertyMeta> =>
toList([...Object.getOwnPropertyNames(this.subject), ...Object.getOwnPropertyNames(Object.getPrototypeOf(this.subject))])
.map(p => this.property(p))
.filter(p => (key ? p.get(key) : p));
keys = <T = any>(key: string): List<T> =>
this.properties()
.map(p => p.get<T>(key))
.reduce((list, u) => (u ? list.add(u) : list), toList<T>());
values = <T = unknown>(): List<T> => values<T>(this.subject);
property = (property: string | symbol): PropertyMeta => new PropertyMeta(this.subject, property);
}
class PropertyMeta {
constructor(
readonly subject: any,
readonly property: string | symbol,
private readonly data = Reflect.getMetadata(property, subject)
) {}
get value(): any {
return this.subject[this.property];
}
get = <T>(key: string): Optional<T> => (isDefined(this.data) && isDefined(this.data[key]) ? (this.data[key] as T) : undefined);
set = <T>(key: string, value: T): T => {
Reflect.defineMetadata(this.property, { ...this.data, [key]: value }, this.subject);
return value;
};
}
export const meta = (subject: unknown): ClassMeta => new ClassMeta(subject ?? {});