From e987227a3798b85b64710a899634de707c0572cf Mon Sep 17 00:00:00 2001 From: crusader Date: Wed, 20 Dec 2017 21:02:50 +0900 Subject: [PATCH] ing --- src/ts/@overflow/commons/core/type/index.ts | 3 + .../@overflow/commons/core/type/metadata.ts | 520 ++++++++++++++++++ src/ts/@overflow/commons/core/type/type.ts | 9 + src/ts/@overflow/commons/core/type/util.ts | 388 +++++++++++++ .../decorator/annotation/annotation.ts | 16 + .../commons/decorator/annotation/index.ts | 1 + .../@overflow/commons/decorator/constants.ts | 11 + .../@overflow/commons/decorator/decorator.ts | 76 +++ src/ts/@overflow/commons/decorator/index.ts | 4 + src/ts/@overflow/commons/redux/action.ts | 14 + .../commons/redux/api_async_action.ts | 23 + .../commons/redux/api_rest_action.ts | 23 + .../commons/redux/decorators/reducer.ts | 34 ++ .../commons/redux/decorators/rest.ts | 6 + .../@overflow/commons/redux/decorators/rpc.ts | 6 + src/ts/@overflow/commons/redux/lpc_reducer.ts | 35 ++ .../modules/member/reducer/member_reducer.ts | 10 + src/ts/@overflow/webapp/config/index.ts | 0 .../@overflow/webapp/redux/webapp_reducer.ts | 11 + 19 files changed, 1190 insertions(+) create mode 100644 src/ts/@overflow/commons/core/type/index.ts create mode 100644 src/ts/@overflow/commons/core/type/metadata.ts create mode 100644 src/ts/@overflow/commons/core/type/type.ts create mode 100644 src/ts/@overflow/commons/core/type/util.ts create mode 100644 src/ts/@overflow/commons/decorator/annotation/annotation.ts create mode 100644 src/ts/@overflow/commons/decorator/annotation/index.ts create mode 100644 src/ts/@overflow/commons/decorator/constants.ts create mode 100644 src/ts/@overflow/commons/decorator/decorator.ts create mode 100644 src/ts/@overflow/commons/decorator/index.ts create mode 100644 src/ts/@overflow/commons/redux/action.ts create mode 100644 src/ts/@overflow/commons/redux/api_async_action.ts create mode 100644 src/ts/@overflow/commons/redux/api_rest_action.ts create mode 100644 src/ts/@overflow/commons/redux/decorators/reducer.ts create mode 100644 src/ts/@overflow/commons/redux/decorators/rest.ts create mode 100644 src/ts/@overflow/commons/redux/decorators/rpc.ts create mode 100644 src/ts/@overflow/commons/redux/lpc_reducer.ts create mode 100644 src/ts/@overflow/modules/member/reducer/member_reducer.ts create mode 100644 src/ts/@overflow/webapp/config/index.ts create mode 100644 src/ts/@overflow/webapp/redux/webapp_reducer.ts diff --git a/src/ts/@overflow/commons/core/type/index.ts b/src/ts/@overflow/commons/core/type/index.ts new file mode 100644 index 0000000..09189c3 --- /dev/null +++ b/src/ts/@overflow/commons/core/type/index.ts @@ -0,0 +1,3 @@ +export * from './metadata'; +export * from './type'; +export * from './util'; diff --git a/src/ts/@overflow/commons/core/type/metadata.ts b/src/ts/@overflow/commons/core/type/metadata.ts new file mode 100644 index 0000000..5192301 --- /dev/null +++ b/src/ts/@overflow/commons/core/type/metadata.ts @@ -0,0 +1,520 @@ +import { + PropertyKeyType, +} from './type'; + +import * as Util from './util'; + + +export class Metadata { + /** + * Gets the metadata value for the provided metadata key on the target object or its prototype chain. + * @param key A key used to store and retrieve metadata. + * @param target The target object on which the metadata is defined. + * @param propertyKey The property key for the target. + * @returns The metadata value for the metadata key if found; otherwise, `undefined`. + * @example + * + * ```typescript + * class Example { + * // property declarations are not part of ES6, though they are valid in TypeScript: + * // static staticProperty; + * // property; + * + * static staticMethod(p) { } + * method(p) { } + * } + * + * // constructor + * result = Metadata.get("custom:annotation", Example); + * + * // property (on constructor) + * result = Metadata.get("custom:annotation", Example, "staticProperty"); + * + * // property (on prototype) + * result = Metadata.get("custom:annotation", Example.prototype, "property"); + * + * // method (on constructor) + * result = Metadata.get("custom:annotation", Example, "staticMethod"); + * + * // method (on prototype) + * result = Metadata.get("custom:annotation", Example.prototype, "method"); + * ``` + * + */ + public static get(key: string, target: any, propertyKey?: PropertyKeyType): any { + return Reflect.getMetadata(key, Util.getClass(target), propertyKey!); + } + + /** + * Gets the metadata value for the provided metadata key on the target object or its prototype chain. + * @param key A key used to store and retrieve metadata. + * @param target The target object on which the metadata is defined. + * @param propertyKey The property key for the target. + * @returns The metadata value for the metadata key if found; otherwise, `undefined`. + * @example + * + * ```typescript + * class Example { + * // property declarations are not part of ES6, though they are valid in TypeScript: + * // static staticProperty; + * // property; + * + * static staticMethod(p) { } + * method(p) { } + * } + * + * // constructor + * result = Metadata.getOwn("custom:annotation", Example); + * + * // property (on constructor) + * result = Metadata.getOwn("custom:annotation", Example, "staticProperty"); + * + * // property (on prototype) + * result = Metadata.getOwn("custom:annotation", Example.prototype, "property"); + * + * // method (on constructor) + * result = Metadata.getOwn("custom:annotation", Example, "staticMethod"); + * + * // method (on prototype) + * result = Metadata.getOwn("custom:annotation", Example.prototype, "method"); + * ``` + * + */ + public static getOwn(key: string, target: any, propertyKey?: string | symbol): any { + return Reflect.getOwnMetadata(key, Util.getClass(target), propertyKey!); + } + + /** + * Gets the metadata value for the provided metadata DESIGN_TYPE on the target object or its prototype chain. + * @param target The target object on which the metadata is defined. + * @param propertyKey The property key for the target. + * @returns The metadata value for the metadata key if found; otherwise, `undefined`. + * @example + * + * ```typescript + * class Example { + * // property declarations are not part of ES6, though they are valid in TypeScript: + * // static staticProperty; + * // property; + * + * static staticMethod(p) { } + * method(p) { } + * } + * + * // on contructor + * result = Metadata.getType(Example); + * + * // property (on constructor) + * result = Metadata.getType(Example, "staticProperty"); + * + * // method (on constructor) + * result = Metadata.getType(Example, "staticMethod"); + * ``` + * + */ + public static getType(target: any, propertyKey?: string | symbol): any { + return Reflect.getMetadata(DESIGN_TYPE, target, propertyKey!); + } + + /** + * Gets the metadata value for the provided metadata DESIGN_TYPE on the target object or its prototype chain. + * @param target The target object on which the metadata is defined. + * @param propertyKey The property key for the target. + * @returns The metadata value for the metadata key if found; otherwise, `undefined`. + * @example + * + * ```typescript + * class Example { + * // property declarations are not part of ES6, though they are valid in TypeScript: + * // static staticProperty; + * // property; + * + * static staticMethod(p) { } + * method(p) { } + * } + * + * // on contructor + * result = Metadata.getOwnType(Example); + * + * // property (on constructor) + * result = Metadata.getOwnType(Example, "staticProperty"); + * + * // method (on constructor) + * result = Metadata.getOwnType(Example, "staticMethod"); + * ``` + * + */ + public static getOwnType(target: any, propertyKey?: string | symbol): any { + return Reflect.getMetadata(DESIGN_TYPE, target, propertyKey!); + } + + /** + * Gets the metadata value for the provided metadata DESIGN_RETURN_TYPE on the target object or its prototype chain. + * @param target The target object on which the metadata is defined. + * @param propertyKey The property key for the target. + * @returns The metadata value for the metadata key if found; otherwise, `undefined`. + * @example + * + * ```typescript + * class Example { + * // property declarations are not part of ES6, though they are valid in TypeScript: + * // static staticProperty; + * // property; + * + * static staticMethod(p) { } + * method(p) { } + * } + * + * // on contructor + * result = Metadata.getReturnType(Example); + * + * // property (on constructor) + * result = Metadata.getReturnType(Example, "staticProperty"); + * + * // method (on constructor) + * result = Metadata.getReturnType(Example, "staticMethod"); + * ``` + * + */ + public static getReturnType(target: any, propertyKey?: string | symbol): any { + return Reflect.getMetadata(DESIGN_RETURN_TYPE, target, propertyKey!); + } + + /** + * Gets the metadata value for the provided metadata DESIGN_RETURN_TYPE on the target object or its prototype chain. + * @param target The target object on which the metadata is defined. + * @param propertyKey The property key for the target. + * @returns The metadata value for the metadata key if found; otherwise, `undefined`. + * @example + * + * ```typescript + * class Example { + * // property declarations are not part of ES6, though they are valid in TypeScript: + * // static staticProperty; + * // property; + * + * static staticMethod(p) { } + * method(p) { } + * } + * + * // on contructor + * result = Metadata.getOwnReturnType(Example); + * + * // property (on constructor) + * result = Metadata.getOwnReturnType(Example, "staticProperty"); + * + * // method (on constructor) + * result = Metadata.getOwnReturnType(Example, "staticMethod"); + * ``` + * + */ + public static getOwnReturnType(target: any, propertyKey?: string | symbol): any { + return Reflect.getOwnMetadata(DESIGN_RETURN_TYPE, target, propertyKey!); + } + + /** + * Gets a value indicating whether the target object or its prototype chain has the provided metadata key defined. + * @param key A key used to store and retrieve metadata. + * @param target The target object on which the metadata is defined. + * @param propertyKey The property key for the target. + * @returns `true` if the metadata key was defined on the target object or its prototype chain; otherwise, `false`. + * @example + * + * ```typescript + * class Example { + * // property declarations are not part of ES6, though they are valid in TypeScript: + * // static staticProperty; + * // property; + * + * static staticMethod(p) { } + * method(p) { } + * } + * + * // constructor + * result = Metadata.has("custom:annotation", Example); + * + * // property (on constructor) + * result = Metadata.has("custom:annotation", Example, "staticProperty"); + * + * // property (on prototype) + * result = Metadata.has("custom:annotation", Example.prototype, "property"); + * + * // method (on constructor) + * result = Metadata.has("custom:annotation", Example, "staticMethod"); + * + * // method (on prototype) + * result = Metadata.has("custom:annotation", Example.prototype, "method"); + * ``` + * + */ + public static has(key: string, target: any, propertyKey?: string | symbol): boolean { + return Reflect.hasMetadata(key, Util.getClass(target), propertyKey!); + } + + /** + * Gets a value indicating whether the target object or its prototype chain has the provided metadata key defined. + * @param key A key used to store and retrieve metadata. + * @param target The target object on which the metadata is defined. + * @param propertyKey The property key for the target. + * @returns `true` if the metadata key was defined on the target object or its prototype chain; otherwise, `false`. + * @example + * + * ```typescript + * class Example { + * // property declarations are not part of ES6, though they are valid in TypeScript: + * // static staticProperty; + * // property; + * + * static staticMethod(p) { } + * method(p) { } + * } + * + * // constructor + * result = Metadata.has("custom:annotation", Example); + * + * // property (on constructor) + * result = Metadata.hasOwn("custom:annotation", Example, "staticProperty"); + * + * // property (on prototype) + * result = Metadata.hasOwn("custom:annotation", Example.prototype, "property"); + * + * // method (on constructor) + * result = Metadata.hasOwn("custom:annotation", Example, "staticMethod"); + * + * // method (on prototype) + * result = Metadata.hasOwn("custom:annotation", Example.prototype, "method"); + * ``` + * + */ + public static hasOwn(key: string, target: any, propertyKey?: string | symbol): boolean { + return Reflect.hasOwnMetadata(key, Util.getClass(target), propertyKey!); + } + + /** + * Deletes the metadata entry from the target object with the provided key. + * @param key A key used to store and retrieve metadata. + * @param target The target object on which the metadata is defined. + * @param propertyKey The property key for the target. + * @returns `true` if the metadata entry was found and deleted; otherwise, false. + * @example + * + * ```typescript + * class Example { + * // property declarations are not part of ES6, though they are valid in TypeScript: + * // static staticProperty; + * // property; + * + * static staticMethod(p) { } + * method(p) { } + * } + * + * // constructor + * result = Metadata.delete("custom:annotation", Example); + * + * // property (on constructor) + * result = Metadata.delete("custom:annotation", Example, "staticProperty"); + * + * // property (on prototype) + * result = Metadata.delete("custom:annotation", Example.prototype, "property"); + * + * // method (on constructor) + * result = Metadata.delete("custom:annotation", Example, "staticMethod"); + * + * // method (on prototype) + * result = Metadata.delete("custom:annotation", Example.prototype, "method"); + * ``` + * + */ + public static delete(key: string, target: any, propertyKey?: string | symbol): boolean { + return Reflect.deleteMetadata(key, Util.getClass(target), propertyKey!); + } + + /** + * Set the metadata value for the provided metadata DESIGN_PARAM_TYPES on the target object or its prototype chain. + * @param target The target object on which the metadata is defined. + * @param propertyKey The property key for the target. + * @param value A value that contains attached metadata. + * @returns The metadata value for the metadata key if found; otherwise, `undefined`. + * @example + * + * ```typescript + * class Example { + * // property declarations are not part of ES6, though they are valid in TypeScript: + * // static staticProperty; + * // property; + * + * static staticMethod(p) { } + * method(p) { } + * } + * + * // on contructor + * result = Metadata.setParamTypes(Example, undefined, [Object]); + * + * // property (on constructor) + * result = Metadata.setParamTypes(Example, "staticProperty", [Object]); + * + * // property (on prototype) + * result = Metadata.setParamTypes(Example.prototype, "property", [Object]); + * + * // method (on constructor) + * result = Metadata.setParamTypes(Example, "staticMethod", [Object]); + * + * // method (on prototype) + * result = Metadata.setParamTypes(Example.prototype, "method", [Object]); + * ``` + * + */ + public static setParamTypes(target: any, propertyKey: string | symbol, value: any): void { + return this.set(DESIGN_PARAM_TYPES, value, target.prototype, propertyKey); + } + + /** + * Get all metadata for a metadataKey. + * @param metadataKey + */ + public static getTargetsFromPropertyKey = (metadataKey: string | symbol): any[] => + PROPERTIES.has(metadataKey) ? PROPERTIES.get(metadataKey) || [] : [] + + /** + * Define a unique metadata entry on the target. + * @param key A key used to store and retrieve metadata. + * @param value A value that contains attached metadata. + * @param target The target object on which to define metadata. + * @param propertyKey The property key for the target. + * @example + * + * ```typescript + * class Example { + * // property declarations are not part of ES6, though they are valid in TypeScript: + * // static staticProperty; + * // property; + * + * static staticMethod(p) { } + * method(p) { } + * } + * + * // constructor + * Reflect.defineMetadata("custom:annotation", options, Example); + * + * // property (on constructor) + * Reflect.defineMetadata("custom:annotation", Number, Example, "staticProperty"); + * + * // property (on prototype) + * Reflect.defineMetadata("custom:annotation", Number, Example.prototype, "property"); + * + * // method (on constructor) + * Reflect.defineMetadata("custom:annotation", Number, Example, "staticMethod"); + * + * // method (on prototype) + * Reflect.defineMetadata("custom:annotation", Number, Example.prototype, "method"); + * + * // decorator factory as metadata-producing annotation. + * function MyAnnotation(options): PropertyDecorator { + * return (target, key) => Reflect.defineMetadata("custom:annotation", options, target, key); + * } + * ``` + * + */ + public static set(key: string, value: any, target: any, propertyKey?: string | symbol): void { + + const targets: any[] = PROPERTIES.has(key) ? PROPERTIES.get(key) || [] : []; + const classConstructor = Util.getClass(target); + + if (targets.indexOf(classConstructor) === -1) { + targets.push(classConstructor); + PROPERTIES.set(key, targets); + } + + Reflect.defineMetadata(key, value, Util.getClass(target), propertyKey!); + } + + /** + * Gets the metadata value for the provided metadata DESIGN_PARAM_TYPES on the target object or its prototype chain. + * @param target The target object on which the metadata is defined. + * @param propertyKey The property key for the target. + * @returns The metadata value for the metadata key if found; otherwise, `undefined`. + * @example + * + * ```typescript + * class Example { + * // property declarations are not part of ES6, though they are valid in TypeScript: + * // static staticProperty; + * // property; + * + * static staticMethod(p) { } + * method(p) { } + * } + * + * // on contructor + * result = Metadata.getParamTypes(Example); + * + * // property (on constructor) + * result = Metadata.getParamTypes(Example, "staticProperty"); + * + * // method (on constructor) + * result = Metadata.getParamTypes(Example, "staticMethod"); + * ``` + * + */ + public static getParamTypes(target: any, propertyKey?: string | symbol): any[] { + return Reflect.getMetadata(DESIGN_PARAM_TYPES, target, propertyKey!) || []; + } + + /** + * Gets the metadata value for the provided metadata DESIGN_PARAM_TYPES on the target object or its prototype chain. + * @param target The target object on which the metadata is defined. + * @param propertyKey The property key for the target. + * @returns The metadata value for the metadata key if found; otherwise, `undefined`. + * @example + * + * ```typescript + * class Example { + * // property declarations are not part of ES6, though they are valid in TypeScript: + * // static staticProperty; + * // property; + * + * static staticMethod(p) { } + * method(p) { } + * } + * + * // on contructor + * result = Metadata.getParamTypes(Example); + * + * // property (on constructor) + * result = Metadata.getParamTypes(Example, "staticProperty"); + * + * // method (on constructor) + * result = Metadata.getParamTypes(Example, "staticMethod"); + * ``` + * + */ + public static getOwnParamTypes(target: any, propertyKey?: string | symbol): any[] { + return Reflect.getOwnMetadata(DESIGN_PARAM_TYPES, target, propertyKey!) || []; + } +} + + +/** + * Metadata key + * @private + * @type {string} + */ +const DESIGN_PARAM_TYPES = 'design:paramtypes'; +/** + * Metadata key + * @private + * @type {string} + */ +const DESIGN_TYPE = 'design:type'; +/** + * Metadata key + * @private + * @type {string} + */ +const DESIGN_RETURN_TYPE = 'design:returntype'; +/** + * Properties collections + * @private + * @type {string} + */ +const PROPERTIES: Map = new Map(); diff --git a/src/ts/@overflow/commons/core/type/type.ts b/src/ts/@overflow/commons/core/type/type.ts new file mode 100644 index 0000000..889f490 --- /dev/null +++ b/src/ts/@overflow/commons/core/type/type.ts @@ -0,0 +1,9 @@ +export type IdentityType = T | symbol; +export type PropertyKeyType = IdentityType; + +export const Type = Function; +export interface Type extends Function { + new (...args: any[]): T; +} + +export type DecoratorParametersType = [any, string | symbol, number | PropertyDescriptor]; diff --git a/src/ts/@overflow/commons/core/type/util.ts b/src/ts/@overflow/commons/core/type/util.ts new file mode 100644 index 0000000..e05d287 --- /dev/null +++ b/src/ts/@overflow/commons/core/type/util.ts @@ -0,0 +1,388 @@ +import { + DecoratorParametersType, + PropertyKeyType, +} from './type'; + +/** + * Get the provide constructor. + * @param targetClass + */ +export const getContructor = (targetClass: any): Function => + typeof targetClass === 'function' + ? targetClass + : targetClass.constructor; + +/** + * Get the provide constructor if target is an instance. + * @param target + * @returns {*} + */ +export function getClass(target: any): any { + return target.prototype ? target : target.constructor; +} + +/** + * + * @param target + * @returns {symbol} + */ +export function getClassOrSymbol(target: any): any { + return typeof target === 'symbol' ? target : getClass(target); +} + +/** + * Return true if the given obj is a primitive. + * @param target + * @returns {boolean} + */ +export function isPrimitiveOrPrimitiveClass(target: any): boolean { + return isString(target) + || isNumber(target) + || isBoolean(target); +} + +/** + * + * @param target + * @returns {'string' | 'number' | 'boolean' | 'any'} + */ +export function primitiveOf(target: any): 'string' | 'number' | 'boolean' | 'any' { + if (isString(target)) { + return 'string'; + } + if (isNumber(target)) { + return 'number'; + } + if (isBoolean(target)) { + return 'boolean'; + } + return 'any'; +} + +/** + * + * @param target + * @returns {boolean} + */ +export function isString(target: any): boolean { + return typeof target === 'string' || target instanceof String || target === String; +} + +/** + * + * @param target + * @returns {boolean} + */ +export function isNumber(target: any): boolean { + return typeof target === 'number' || target instanceof Number || target === Number; +} + +/** + * + * @param target + * @returns {boolean} + */ +export function isBoolean(target: any): boolean { + return typeof target === 'boolean' || target instanceof Boolean || target === Boolean; +} + +/** + * + * @param target + * @returns {Boolean} + */ +export function isArray(target: any): boolean { + return Array.isArray(target); +} + +/** + * Return true if the clazz is an array. + * @param target + * @returns {boolean} + */ +export function isArrayOrArrayClass(target: any): boolean { + if (target === Array) { + return true; + } + return isArray(target); +} + +/** + * Return true if the target. + * @param target + * @returns {boolean} + */ +export function isCollection(target: any): boolean { + return isArrayOrArrayClass(target) + || target === Map + || target instanceof Map + || target === Set + || target instanceof Set + || target === WeakMap + || target instanceof WeakMap + || target === WeakSet + || target instanceof WeakSet; +} + +/** + * + * @param target + * @returns {boolean} + */ +export function isDate(target: any): boolean { + return target === Date || target instanceof Date; +} + +/** + * + * @param target + * @returns {boolean} + */ +export function isObject(target: any): boolean { + return target === Object; +} + +/** + * + * @param target + * @returns {boolean} + */ +export function isClass(target: any): boolean { + return !isPrimitiveOrPrimitiveClass(target) + && !isObject(target) + && !isDate(target) + && target !== undefined + && !isPromise(target); +} + +/** + * Return true if the value is an empty string, null or undefined. + * @param value + * @returns {boolean} + */ +export function isEmpty(value: any): boolean { + return value === '' || value === null || value === undefined; +} + +/** + * Get object name + */ +export const nameOf = (obj: any): string => { + switch (typeof obj) { + default: + return '' + obj; + case 'symbol': + return nameOfSymbol(obj); + case 'function': + return nameOfClass(obj); + } +}; + +/** + * Get the provide name. + * @param targetClass + */ +export const nameOfClass = (targetClass: any) => { + return typeof targetClass === 'function' + ? targetClass.name + : targetClass.constructor.name; +}; +/** + * Get symbol name. + * @param sym + */ +export const nameOfSymbol = (sym: symbol): string => + sym.toString().replace('Symbol(', '').replace(')', ''); + +/** + * + * @param out + * @param obj + * @param {{[p: string]: (collection: any[], value: any) => any}} reducers + * @returns {any} + */ +export function deepExtends(out: any, obj: any, reducers: { [key: string]: (collection: any[], value: any) => any } = {}): any { + + if (obj === undefined || obj === null) { + return obj; + } + + if (isPrimitiveOrPrimitiveClass(obj) || typeof obj === 'symbol' || typeof obj === 'function') { + return obj; + } + + if (isArrayOrArrayClass(obj)) { + out = out || []; + } else { + out = out || {}; + } + + const defaultReducer = reducers.default ? reducers.default : (collection: any[], value: any) => { + collection.push(value); + return collection; + }; + const set = (key: string | number, value: any) => { + if (isArrayOrArrayClass(obj)) { + out.push(value); + } else { + out[key] = value; + } + }; + + Object.keys(obj).forEach(key => { + let value = obj[key]; + + if (value === undefined || value === null) { + return; + } + + if (value === '' && out[key] !== '') { + return; + } + + if (isPrimitiveOrPrimitiveClass(value) || typeof value === 'function') { + set(key, value); + return; + } + + if (isArrayOrArrayClass(value)) { + + value = value.map((v: any) => deepExtends(undefined, v)); + + set(key, [] + .concat(out[key] || [], value) + .reduce((collection: any[], v: any) => + reducers[key] ? reducers[key](collection, v) : defaultReducer(collection, v), + [])); + return; + } + + // Object + if (isArrayOrArrayClass(obj)) { + set(key, deepExtends(undefined, value, reducers)); + } else { + set(key, deepExtends(out[key], value, reducers)); + } + }); + + if (isArrayOrArrayClass(out)) { + out.reduce((collection: any[], value: any) => defaultReducer(collection, value), []); + } + + return out; +} + +/** + * + * @param target + * @returns {boolean} + */ +export function isPromise(target: any): boolean { + return target === Promise || target instanceof Promise; +} + +/** + * + * @param target + * @returns {any} + */ +export function getInheritedClass(target: any): any { + return Object.getPrototypeOf(target); +} + +/** + * + * @param {any[]} args + * @returns {'parameter' | 'property' | 'method' | 'class'} + */ +export function getDecoratorType(args: any[]): 'parameter' | 'property' | 'method' | 'class' { + const [, propertyKey, descriptor] = args; + + if (typeof descriptor === 'number') { + return 'parameter'; + } + + if (propertyKey && descriptor === undefined || descriptor && (descriptor.get || descriptor.set)) { + return 'property'; + } + return (descriptor && descriptor.value) ? 'method' : 'class'; +} + +/** + * + * @param target + * @param {PropertyKeyType} propertyKey + * @returns {PropertyDescriptor} + */ +export function descriptorOf(target: any, propertyKey: PropertyKeyType): PropertyDescriptor { + return Object.getOwnPropertyDescriptor(target && target.prototype || target, propertyKey)!; +} + +/** + * + * @param target + * @param {string} propertyKey + * @returns {DecoratorParametersType} + */ +export function decoratorArgs(target: any, propertyKey: string): DecoratorParametersType { + return [ + target, + propertyKey, + descriptorOf(target, propertyKey)!, + ]; +} + +/** + * + * @param target + * @returns {Array} + */ +export function ancestorsOf(target: any): any[] { + const classes = []; + + let currentTarget = getClass(target); + + while (nameOf(currentTarget) !== '') { + classes.unshift(currentTarget); + currentTarget = getInheritedClass(currentTarget); + } + + return classes; +} + +/** + * + * @param target + * @param {string} name + * @param {Function} callback + */ +export function applyBefore(target: any, name: string, callback: Function): void { + const original = target[name]; + target[name] = function (...args: any[]): any { + callback(...args); + return original.apply(this, args); + }; +} + +/** + * + * @param {Promise} promise + * @param {number} time + * @returns {Promise} + */ +export function promiseTimeout(promise: Promise, time: number = 1000): Promise<{ ok: boolean, response: any }> { + const timeout = (p: Promise, t: number) => new Promise((resolve) => { + p.then((response) => { + resolve(); + return response; + }); + setTimeout(() => resolve({ok: false}), t); + }); + + promise = promise.then((response) => ({ok: true, response})); + + return Promise.race([ + promise, + timeout(promise, time), + ]); +} diff --git a/src/ts/@overflow/commons/decorator/annotation/annotation.ts b/src/ts/@overflow/commons/decorator/annotation/annotation.ts new file mode 100644 index 0000000..8dbbd60 --- /dev/null +++ b/src/ts/@overflow/commons/decorator/annotation/annotation.ts @@ -0,0 +1,16 @@ +import { + PropertyKeyType, +} from '@overflow/commons/core/type'; + +export interface Annotation { + classDecorator?: (target: TFunction) => TFunction | void; + propertyDecorator?: (target: Object, propertyKey: PropertyKeyType) => void; + methodDecorator?: (target: Object, propertyKey: PropertyKeyType, + descriptor: TypedPropertyDescriptor) => TypedPropertyDescriptor | void; + parameterDecorator?: (target: Object, propertyKey: PropertyKeyType, parameterIndex: number) => void; +} + +export abstract class Annotation { +} + +export default Annotation; diff --git a/src/ts/@overflow/commons/decorator/annotation/index.ts b/src/ts/@overflow/commons/decorator/annotation/index.ts new file mode 100644 index 0000000..acd091e --- /dev/null +++ b/src/ts/@overflow/commons/decorator/annotation/index.ts @@ -0,0 +1 @@ +export * from './annotation'; diff --git a/src/ts/@overflow/commons/decorator/constants.ts b/src/ts/@overflow/commons/decorator/constants.ts new file mode 100644 index 0000000..b05925a --- /dev/null +++ b/src/ts/@overflow/commons/decorator/constants.ts @@ -0,0 +1,11 @@ +export const ANNOTATION_HANDLER_CLASS: string = 'classDecorator'; +export const ANNOTATION_HANDLER_PROPERTY: string = 'propertyDecorator'; +export const ANNOTATION_HANDLER_METHOD: string = 'methodDecorator'; +export const ANNOTATION_HANDLER_PARAMETER: string = 'parameterDecorator'; + +export enum DecoratorType { + CLASS = 'Class', + PROPERTY = 'Property', + METHOD = 'Method', + PARAMETER = 'Parameter', +} diff --git a/src/ts/@overflow/commons/decorator/decorator.ts b/src/ts/@overflow/commons/decorator/decorator.ts new file mode 100644 index 0000000..4956334 --- /dev/null +++ b/src/ts/@overflow/commons/decorator/decorator.ts @@ -0,0 +1,76 @@ +import { + Type, +} from '@overflow/commons/core/type'; + +import * as TypeUtil from '@overflow/commons/core/type/util'; + +import * as Constants from './constants'; +import { + Annotation, +} from './annotation'; + +export class Decorator { + public static create = (AnnotationClass: Type) => { + + return (...handlerArgs: any[]) => { + let annotation: Annotation = new AnnotationClass(...handlerArgs); + + return (...decoratorArgs: any[]) => { + let decoratorType: Constants.DecoratorType = Decorator._detectDecoratorType(name, annotation, decoratorArgs); + // let type = typeof decoratorArgs[0] === 'function' ? decoratorArgs[0].prototype : decoratorArgs[0]; + let clazz = TypeUtil.getClass(decoratorArgs[0]); + + switch(decoratorType) { + case Constants.DecoratorType.CLASS: + return annotation.classDecorator.call(annotation, clazz, decoratorArgs[0]); + case Constants.DecoratorType.PROPERTY: + return annotation.propertyDecorator.call(annotation, clazz, decoratorArgs[0], decoratorArgs[1]); + case Constants.DecoratorType.METHOD: + return annotation.methodDecorator.call(annotation, clazz, decoratorArgs[0], decoratorArgs[1], decoratorArgs[2]); + case Constants.DecoratorType.PARAMETER: + return annotation.parameterDecorator.call(annotation, clazz, decoratorArgs[0], decoratorArgs[1], decoratorArgs[2]); + default: + } + }; + }; + } + + private static _detectDecoratorType(name: string, annotation: any, args: any[]): Constants.DecoratorType { + let params = []; + for (let i = 0; i < args.length; i++) { + if (args[i]) { + params.push(args[i]); + } + } + const paramCount = params.length; + let decoratorType: Constants.DecoratorType; + + if (1 === paramCount) { + if (Constants.ANNOTATION_HANDLER_CLASS in annotation) { + throw new Error(`Cannot apply @${name} decorator on Class.`); + } + decoratorType = Constants.DecoratorType.CLASS; + } else if (2 === paramCount) { + if (Constants.ANNOTATION_HANDLER_PROPERTY in annotation) { + throw new Error(`Cannot apply @${name} decorator on Property.`); + } + decoratorType = Constants.DecoratorType.PROPERTY; + } else if (3 === paramCount) { + if(typeof args[2] === 'number') { + if (Constants.ANNOTATION_HANDLER_PARAMETER in annotation) { + throw new Error(`Cannot apply @${name} decorator on Parameter.`); + } + decoratorType = Constants.DecoratorType.PARAMETER; + } else { + if (Constants.ANNOTATION_HANDLER_METHOD in annotation) { + throw new Error(`Cannot apply @${name} decorator on Method.`); + } + decoratorType = Constants.DecoratorType.METHOD; + } + } else { + throw new Error(`@${name} decorator is not valid here!`); + } + + return decoratorType; + } +} diff --git a/src/ts/@overflow/commons/decorator/index.ts b/src/ts/@overflow/commons/decorator/index.ts new file mode 100644 index 0000000..23baea3 --- /dev/null +++ b/src/ts/@overflow/commons/decorator/index.ts @@ -0,0 +1,4 @@ +export * from './constants'; +export * from './decorator'; + +export * from './annotation'; diff --git a/src/ts/@overflow/commons/redux/action.ts b/src/ts/@overflow/commons/redux/action.ts new file mode 100644 index 0000000..f3f3db9 --- /dev/null +++ b/src/ts/@overflow/commons/redux/action.ts @@ -0,0 +1,14 @@ +import * as Redux from 'redux'; + +export interface Action extends Redux.Action { + payload?: Payload; + error?: Error; +} + +export type actions = (method: string, ...params: any[]) => Action; +export const actions: actions = (method: string, ...params: any[]): Action => { + return { + type: method, + payload: params, + }; +}; diff --git a/src/ts/@overflow/commons/redux/api_async_action.ts b/src/ts/@overflow/commons/redux/api_async_action.ts new file mode 100644 index 0000000..1c0fb48 --- /dev/null +++ b/src/ts/@overflow/commons/redux/api_async_action.ts @@ -0,0 +1,23 @@ +import { + Action, +} from './action'; + +// API Action Type +export type ASYNC_REQUEST = '@overflow/commons/async/REQUEST'; +export const ASYNC_REQUEST: ASYNC_REQUEST = '@overflow/commons/async/REQUEST'; + +export interface AsyncRequestPayload { + method: string; + params?: any[]; +} + +export type asyncRequestActions = (method: string, ...params: any[]) => Action; +export const asyncRequestActions: asyncRequestActions = (method: string, ...params: any[]): Action => { + return { + type: ASYNC_REQUEST, + payload: { + method: method, + params: params, + }, + }; +}; diff --git a/src/ts/@overflow/commons/redux/api_rest_action.ts b/src/ts/@overflow/commons/redux/api_rest_action.ts new file mode 100644 index 0000000..3152217 --- /dev/null +++ b/src/ts/@overflow/commons/redux/api_rest_action.ts @@ -0,0 +1,23 @@ +import { + Action, +} from './action'; + +// REST Action Type +export type REST_REQUEST = '@overflow/commons/rest/REQUEST'; +export const REST_REQUEST: REST_REQUEST = '@overflow/commons/rest/REQUEST'; + +export interface RESTRequestPayload { + entry: string; + params?: any[]; +} + +export type restRequestActions = (entry: string, ...params: any[]) => Action; +export const restRequestActions: restRequestActions = (entry: string, ...params: any[]): Action => { + return { + type: REST_REQUEST, + payload: { + entry: entry, + params: params, + }, + }; +}; diff --git a/src/ts/@overflow/commons/redux/decorators/reducer.ts b/src/ts/@overflow/commons/redux/decorators/reducer.ts new file mode 100644 index 0000000..b7c685c --- /dev/null +++ b/src/ts/@overflow/commons/redux/decorators/reducer.ts @@ -0,0 +1,34 @@ +import * as TypeUtil from '@overflow/commons/core/type/util'; +import { + Annotation, + Decorator, +} from '@overflow/commons/decorator'; + + + +export interface ReducerAnnotationAttributes { + name?: string; +} + +export class ReducerAnnotation extends Annotation { + public readonly attributes: ReducerAnnotationAttributes; + + public constructor(name: string | ReducerAnnotationAttributes) { + super(); + if (TypeUtil.isString(name)) { + this.attributes = { + name: name, + }; + } else { + this.attributes = name; + } + } + + public classDecorator = (target: TFunction): TFunction | void => { + console.log('Reducer'); + } +} + +export const Reducer = Decorator.create(ReducerAnnotation); + +export default Reducer; diff --git a/src/ts/@overflow/commons/redux/decorators/rest.ts b/src/ts/@overflow/commons/redux/decorators/rest.ts new file mode 100644 index 0000000..3decf31 --- /dev/null +++ b/src/ts/@overflow/commons/redux/decorators/rest.ts @@ -0,0 +1,6 @@ + + +export function Rest(path: string): Function { + return Use(...['get', path].concat(args)); +} + diff --git a/src/ts/@overflow/commons/redux/decorators/rpc.ts b/src/ts/@overflow/commons/redux/decorators/rpc.ts new file mode 100644 index 0000000..c247acc --- /dev/null +++ b/src/ts/@overflow/commons/redux/decorators/rpc.ts @@ -0,0 +1,6 @@ + + +export function RPC(method: string): Function { + return Use(...['get', path].concat(args)); +} + diff --git a/src/ts/@overflow/commons/redux/lpc_reducer.ts b/src/ts/@overflow/commons/redux/lpc_reducer.ts new file mode 100644 index 0000000..997a2af --- /dev/null +++ b/src/ts/@overflow/commons/redux/lpc_reducer.ts @@ -0,0 +1,35 @@ +import { + Action, +} from './action'; + +class LPCReducer { + + private reducerMap: Map; + + private constructor() { + this.reducerMap = new Map(); + } + + public reducer(state: State, action: Action): State { + let rm: string[] = action.type.split('.'); + if (null == rm || 2 !== rm.length) { + console.log(`Method[${action.type}] is not valid.`); + return null; + } + + const reducerName: string = rm[0]; + const methodName: string = rm[1]; + + if (this.reducerMap.has(reducerName)) { + let reducers = this.reducerMap.get(reducerName); + for (let reducer of reducers) { + + state = reducer(state, action); + } + } + + return null; + } + + +} diff --git a/src/ts/@overflow/modules/member/reducer/member_reducer.ts b/src/ts/@overflow/modules/member/reducer/member_reducer.ts new file mode 100644 index 0000000..1c979a3 --- /dev/null +++ b/src/ts/@overflow/modules/member/reducer/member_reducer.ts @@ -0,0 +1,10 @@ + +export default class MemberReducer { + /** + * signin + */ + public signin(state: any, result: any, error: any): any { + + return state; + } +} diff --git a/src/ts/@overflow/webapp/config/index.ts b/src/ts/@overflow/webapp/config/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/ts/@overflow/webapp/redux/webapp_reducer.ts b/src/ts/@overflow/webapp/redux/webapp_reducer.ts new file mode 100644 index 0000000..fd44f48 --- /dev/null +++ b/src/ts/@overflow/webapp/redux/webapp_reducer.ts @@ -0,0 +1,11 @@ +import { + LPCReducer, +} from '@overflow/commons/redux/lpc_reducer'; + +class WebAppReducer extends LPCReducer { + private static readonly instance: WebAppReducer = new WebAppReducer(); + public static getInstance(): WebAppReducer { + return WebAppReducer.instance; + } + +}