diff --git a/di/src/Container.ts b/di/src/Container.ts index ce5126e..71b153c 100755 --- a/di/src/Container.ts +++ b/di/src/Container.ts @@ -133,11 +133,14 @@ class Container { return factory.produce(construction_args, params.reverse().all()) } - make(target: Instantiable|DependencyKey, ...parameters: any[]): T { - if ( isInstantiable(target) ) + make(target: DependencyKey, ...parameters: any[]) { + if ( this.has_key(target) ) { + return this.resolve_and_create(target, ...parameters) + } + else if ( typeof target !== 'string' ) return this.produce_factory(new Factory(target), parameters) else - return this.resolve_and_create(target, ...parameters) + throw new TypeError(`Invalid or unknown make target: ${target}`) } } diff --git a/di/src/decorator/Service.ts b/di/src/decorator/Service.ts index 9403563..f6316fa 100755 --- a/di/src/decorator/Service.ts +++ b/di/src/decorator/Service.ts @@ -1,11 +1,16 @@ import { container } from '../global.ts' import { isInstantiable } from '../type/Instantiable.ts' +import { Injectable } from './Injection.ts' + +const injectable = Injectable() const Service = (name?: string): ClassDecorator => { return (target) => { if ( isInstantiable(target) ) { if ( name ) container.register_named(name, target) else container.register(target) + + injectable(target) } } } diff --git a/lib/src/const/status.ts b/lib/src/const/status.ts new file mode 100644 index 0000000..3ba7446 --- /dev/null +++ b/lib/src/const/status.ts @@ -0,0 +1,15 @@ +const STATUS_STOPPED = Symbol('status stopped') +const STATUS_STARTING = Symbol('status starting') +const STATUS_RUNNING = Symbol('status running') +const STATUS_STOPPING = Symbol('status stopping') +const STATUS_ERROR = Symbol('status error') + +const isStatus = (something: any) => [ + STATUS_STOPPED, + STATUS_STARTING, + STATUS_RUNNING, + STATUS_STOPPING, + STATUS_ERROR, +].includes(something) + +export { STATUS_STOPPED, STATUS_STARTING, STATUS_RUNNING, STATUS_STOPPING, STATUS_ERROR, isStatus } diff --git a/lib/src/external/std.ts b/lib/src/external/std.ts new file mode 100644 index 0000000..ed3f852 --- /dev/null +++ b/lib/src/external/std.ts @@ -0,0 +1,3 @@ +export { serve } from 'https://deno.land/std/http/server.ts' +export * from 'https://deno.land/std/fmt/colors.ts' +export { config as dotenv } from 'https://deno.land/x/dotenv/mod.ts' diff --git a/lib/src/lifecycle/Unit.ts b/lib/src/lifecycle/Unit.ts new file mode 100644 index 0000000..850b66d --- /dev/null +++ b/lib/src/lifecycle/Unit.ts @@ -0,0 +1,19 @@ +import { STATUS_STOPPED, isStatus } from '../const/status.ts' + +export default abstract class LifecycleUnit { + private _status = STATUS_STOPPED + + public get status() { + return this._status + } + + public set status(status) { + if ( !isStatus(status) ) + throw new TypeError('Invalid unit status: '+status.description) + + this._status = status + } + + public async up(): Promise {}; + public async down(): Promise {}; +} diff --git a/lib/src/lifecycle/decorators.ts b/lib/src/lifecycle/decorators.ts new file mode 100644 index 0000000..82ff42f --- /dev/null +++ b/lib/src/lifecycle/decorators.ts @@ -0,0 +1,11 @@ +import { Service } from '../../../di/src/decorator/Service.ts' + +const service = Service() + +const Unit = (): ClassDecorator => { + return (target) => { + return service(target) + } +} + +export { Unit } diff --git a/lib/src/service/logging/Logger.ts b/lib/src/service/logging/Logger.ts new file mode 100644 index 0000000..acf70f2 --- /dev/null +++ b/lib/src/service/logging/Logger.ts @@ -0,0 +1,45 @@ +import { LogMessage, LoggingLevel } from './types.ts' +import {make} from "../../../../di/src/global.ts"; +import {Logging} from "./Logging.ts"; +import {blue, cyan, gray, green, red, yellow} from "../../external/std.ts"; + +const isLoggerClass = (something: any): something is (typeof Logger) => { + return something.prototype instanceof Logger +} + +export { isLoggerClass } + +export default abstract class Logger { + public abstract async write(message: LogMessage): Promise; + + public static register() { + make(Logging).register_logger(this) + } + + public static remove() { + make(Logging).remove_logger(this) + } + + protected format_date(date: Date): string { + return `${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}` + } + + protected level_display(level: LoggingLevel): string { + switch(level) { + case LoggingLevel.Success: + return green('success') + case LoggingLevel.Error: + return red('error') + case LoggingLevel.Warning: + return yellow('warning') + case LoggingLevel.Info: + return blue('info') + case LoggingLevel.Debug: + return cyan('debug') + case LoggingLevel.Verbose: + return gray('verbose') + case LoggingLevel.Silent: + return gray('silent') + } + } +} diff --git a/lib/src/service/logging/Logging.ts b/lib/src/service/logging/Logging.ts new file mode 100644 index 0000000..f83a64b --- /dev/null +++ b/lib/src/service/logging/Logging.ts @@ -0,0 +1,74 @@ +import {LoggingLevel, LogMessage} from './types.ts' +import Logger from './Logger.ts' +import {Service} from '../../../../di/src/decorator/Service.ts' +import {make} from '../../../../di/src/global.ts' +import {isInstantiable} from '../../../../di/src/type/Instantiable.ts' + +@Service() +class Logging { + private _level = LoggingLevel.Warning + private _loggers: Logger[] = [] + + public get level() { + return this._level + } + + public set level(level) { + this._level = level + } + + public success(output: any, force = false) { + this.write_log(LoggingLevel.Success, output, force) + } + + public error(output: any, force = false) { + this.write_log(LoggingLevel.Error, output, force) + } + + public warn(output: any, force = false) { + this.write_log(LoggingLevel.Warning, output, force) + } + + public info(output: any, force = false) { + this.write_log(LoggingLevel.Info, output, force) + } + + public debug(output: any, force = false) { + this.write_log(LoggingLevel.Debug, output, force) + } + + public verbose(output: any, force = false) { + this.write_log(LoggingLevel.Verbose, output, force) + } + + protected write_log(level: LoggingLevel, output: any, force = false) { + const message = this.build_message(level, output) + if ( this._level >= level || force ) { + for ( const logger of this._loggers ) { + logger.write(message) + } + } + } + + protected build_message(level: LoggingLevel, output: any): LogMessage { + return { + level, + output, + date: new Date, + } + } + + public register_logger(logger_class: typeof Logger) { + if ( isInstantiable(logger_class) ) { + const logger = make(logger_class) + if ( !this._loggers.includes(logger) ) + this._loggers.push(logger) + } + } + + public remove_logger(logger_class: typeof Logger) { + this._loggers = this._loggers.filter(x => !(x instanceof logger_class)) + } +} + +export { Logging } diff --git a/lib/src/service/logging/StandardLogger.ts b/lib/src/service/logging/StandardLogger.ts new file mode 100644 index 0000000..95f4b4a --- /dev/null +++ b/lib/src/service/logging/StandardLogger.ts @@ -0,0 +1,11 @@ +import AbstractLogger from './Logger.ts' +import { LogMessage } from './types.ts' +import { gray } from '../../external/std.ts' + +export default class StandardLogger extends AbstractLogger { + public async write(message: LogMessage): Promise { + const prefix = this.level_display(message.level) + const text = `${prefix} ${gray(this.format_date(message.date))}` + console.log(text, message.output) + } +} diff --git a/lib/src/service/logging/types.ts b/lib/src/service/logging/types.ts new file mode 100644 index 0000000..c6d1f9f --- /dev/null +++ b/lib/src/service/logging/types.ts @@ -0,0 +1,33 @@ +enum LoggingLevel { + Silent = 0, + Success = 1, + Error = 1, + Warning = 2, + Info = 3, + Debug = 4, + Verbose = 5, +} + +const isLoggingLevel = (something: any): something is LoggingLevel => { + return [ + LoggingLevel.Silent, + LoggingLevel.Success, + LoggingLevel.Error, + LoggingLevel.Warning, + LoggingLevel.Info, + LoggingLevel.Debug, + LoggingLevel.Verbose + ].includes(something) +} + +interface LogMessage { + level: LoggingLevel, + date: Date, + output: any, +} + +const isLogMessage = (something: any): something is LogMessage => { + return isLoggingLevel(something?.level) && something?.date instanceof Date; +} + +export { LoggingLevel, LogMessage, isLoggingLevel, isLogMessage } diff --git a/lib/src/unit/Scaffolding.ts b/lib/src/unit/Scaffolding.ts new file mode 100644 index 0000000..5186949 --- /dev/null +++ b/lib/src/unit/Scaffolding.ts @@ -0,0 +1,30 @@ +import LifecycleUnit from '../lifecycle/Unit.ts' +import { Unit } from '../lifecycle/decorators.ts' +import { dotenv } from '../external/std.ts' +import { Logging } from '../service/logging/Logging.ts' +import StandardLogger from '../service/logging/StandardLogger.ts' + +@Unit() +export default class Scaffolding extends LifecycleUnit { + private config = {} + + constructor( + protected logger: Logging + ) { + super() + } + + public refresh_env() { + this.config = dotenv() + } + + public setup_logging() { + StandardLogger.register() + this.logger.info('Logging initialized.', true) + } + + public async up() { + this.setup_logging() + this.refresh_env() + } +} diff --git a/test.ts b/test.ts new file mode 100644 index 0000000..cf651b1 --- /dev/null +++ b/test.ts @@ -0,0 +1,5 @@ +import { make, container } from "./di/src/global.ts"; +import Scaffolding from "./lib/src/unit/Scaffolding.ts"; + +const scaf = make(Scaffolding) +scaf.up() diff --git a/tsconfig.json b/tsconfig.json index 4052b24..04073ec 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,7 @@ { "compilerOptions": { "experimentalDecorators": true, - "emitDecoratorMetadata": true, - "target": "es2017" + "emitDecoratorMetadata": true } }