1.2.2 • Published 1 year ago

@avanzu/decorators v1.2.2

Weekly downloads
-
License
ISC
Repository
github
Last release
1 year ago

@avanzu/decorators

Decorators allowing to extend and alter behavior of methods

Attach logging to class methods

import { Log, LogAsync } from '@avanzu/decorators'

class Example {

    @Log()
    public demo(myArg: number) : string {
        return `demo`
    }

    @LogAsync()
    public async demoAsync(myArg: number) : Promise<string> {
        return 'demo-async'
    }
}

During execution of the decorated methods, you will now have log entries

Example.demo() {
    time: 1714361138410,
    className: 'LogTest',
    methodName: 'demo',
    ms: 0
}

LogTest.demoAsync() {
    time: 1714361138410,
    className: 'LogTest',
    methodName: 'demo',
    ms: 0
}

Decorator options

In order to customize the logging behavior, both decorators accept configuration options.

  • enter (boolean) - wether to generate a log entry before the actual method call. Default: false
  • exit (boolean) - wether to generate a log entry after the method call. Default: true
  • error (boolean) - wether to generate a log entry when an error occured during method execution. Default: true
  • message (string) - the log message that should be used for the log entry. Default: ${ClassName}.${MethodName}()
  • args (boolean) - wether to include the method arguments in the log context. Default: false
  • result (boolean) - wether to include the return value of the method call in the log context. Default: false
  • enterPrefix (string) - string to prepend to the enter log message. Default: ''
  • errorPrefix (string) - string to prepend to the exit log message. Default: ''
  • exitPrefix (string) - string to prepent to the error log message. Default: ''
  • level ('debug'|'info'|'warn'|'error') - the log level of enter and exit logs. Default: 'info'

Global configuration

In order to to configure the logging behavior globally, you can use the same decorator options on the LogBox

import { LogBox } from '@avanzu/decorators'

LogBox.configure({
    // ...
 })

Change the logger

You can change the logger from console to anything else that implements the Logger interface.

import { LogBox } from '@avanzu/decorators'
import { myLogger } from './logger'

LogBox.use(myLogger)

Attach plugin capabilities to classes

Make your class PluginAware, decorate the methods as Pluggable that should allow plugins.

// ./src/pluginDemo.class.ts

import { PluginAware, Pluggable } from '@avanzu/decorators'

@PlguinAware()
class PluginDemo {

    @Pluggable()
    async doFoo(myArg: string) : Promise<string>{
        return `did foo with ${myArg}`
    }

    @Pluggable()
    async doBar(myArg: number): Promise<string>{
        return `didBar`
    }

}

Create your plugin(s)

// ./src/plugins/demo.plugin.ts

import { Plugin, ExecutionContext } from '@avanzu/decorators'
import { PluginDemo } from './pluginDemo.class'

type FooContext = ExecutionContext<[string], string>

@Plugin({ target: PluginDemo })
class DemoPlugin {

    async beforeDoFoo({ params }: FooContext) : Promise<void> {
        params.at(0) = params.at(0).concat('[extended before]')
    }

    async afterDoFoo(context: FooContext) : Promise<FooContext> {

        return {
            ...context,
            result: '[extended after]'.concat(context.result)
         }
    }

}

Register your plugin instance

// ./src/main.ts

import { Plugins } from '@avanzu/decorators'
import { PluginDemo } from './pluginDemo.class'
import { DemoPlugin } from './plugins/demo.plugin'

Plugins.register(PluginDemo, new DemoPlugin())

Execute your plugin aware method

// ./src/main.ts

const pluginDemo = new PluginDemo()

const result = await pluginDemo.doFoo('foo')

// -> [extended after] did foo with foo[extended before]

Using symbols or strings

Instead of using class constructors to correlate a plugin with its corresponding class, you can also use symbols or strings.

Declare your key(s)

// ./src/types.ts

export const Types = {
    Demo: Symbol('pluginAware.demo'),
    // ...
}

Assing the key to your plugin aware class

// ./src/pluginDemo.class.ts

import { PluginAware, Pluggable } from '@avanzu/decorators'
import { Types } from './types'

@PlguinAware({ key: Types.Demo })
class PluginDemo {
    // ...
}

Assing the target to your plugin

// ./src/plugins/demo.plugin.ts

import { Plugin, ExecutionContext } from '@avanzu/decorators'
import { Types } from '../types'

// ...

@Plugin({ target: Types.Demo })
class DemoPlugin {
    //...
}

Register the plugin instance with the corresponding key

// ./src/main.ts

import { Plugins } from '@avanzu/decorators'
import { DemoPlugin } from './plugins/demo.plugin'
import { Types } from './types'

Plugins.register(Types.Demo, new DemoPlugin())

Limitations

  • Due to the implementation, Pluggable methods should be async (or return a Promise). Decorating a synchronous method will make it async.
  • The order of execution is determined by the order of registration. If your plugins have dependencies on each other, you have to register them in the correct order yourself.
1.2.2

1 year ago

1.2.0

1 year ago

1.1.5

1 year ago

1.1.4

1 year ago

1.1.3

1 year ago

1.1.2

1 year ago

1.1.1

1 year ago

1.1.0

1 year ago