npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@domestika/decorators

v1.8.0

Published

> Set of ES6 decorators to improve your apps

Downloads

8

Readme

decorators

Set of ES6 decorators to improve your apps

Definition

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. The primary benefit of the Decorator pattern is that you can take a rather vanilla object and wrap it in more advanced behaviors. Learn more

Installation

npm install @domestika/decorators

Reference

Error

Wrapper any function and handle the errors for you:

If the function return a promise:

  • When is resolved return [null, resp]
  • When is rejected return [err, null]
  • When throw an exception return [err, null]

If the function is a sync function:

  • When is execute return [null, resp]
  • When throw an exception return [err, null]
import {inlineError} from '@domestika/decorators';

class Buzz {
  @inlineError
  method() {
    return Promise.reject(new Error('KO'))
  }

}

const buzz = new Buzz()
const [err, resp] = buzz.method()

console.log(typeof err) // ==> Error

@streamify

Creates a stream of calls to any method of a class. Dependency of RxJS

import {streamify} from '@domestika/decorators';

@streamify('greeting', 'greetingAsync')
class Person {
    greeting(name){
        return `Hi ${name}`;
    }

    greetingAsync(name){
        return new Promise( resolve => setTimeout(resolve, 100, `Hi ${name}`) );
    }
}

const person = new Person();

person.$.greeting.subscribe(({params, result}) => {
    console.log(`method was called with ${params} and response was "${result}"`); // => method was called with ['Carlos'] and response was "Hi Carlos"
});

person.$.greetingAsync.subscribe(({params, result}) => {
    console.log(`method was called with ${params} and response was "${result}"`); // => method was called with ['Carlos'] and response was "Hi Carlos"
});

person.greeting('Carlos');
person.greetingAsync('Carlos');

@cache

There are two types of cache handlers (Memory LRU and Redis LRU):

Memory LRU cache

Creates a cache of calls to any method of a class, only when the response is not an error.

import {cache} from '@domestika/decorators';

class Dummy {
  @cache()
  syncRndNumber (num) { return Math.random() }
}
const dummy = new Dummy()

const firstCall = dummy.syncRndNumber()
const secondCall = dummy.syncRndNumber()

// => firstCall === secondCall

Dump cache to console if setting to truthy 'dumpCache' key in localStorage:

localStorage.__dumpCache__ = true

By default the TTL for the keys in the cache is 500ms, but it can be changed with the ttl option.

import {cache} from '@domestika/decorators';

class Dummy {
  @cache({ttl: 2000})
  syncRndNumber (num) { return Math.random() }
}

For this method the cache is of 2 seconds.

It is possible to set TTL using a string with the format ttl: 'XXX [second|seconds|minute|minutes|hour|hours]', thus, avoiding writing very large integers.

Redis LRU cache:

It creates a cache of the decorated method response of a class, only when the response is not an error. You must decorate methods that return a promise and its resolved value is a plain javascript object, a JSON, or a simple type (number, string...).

Note: Redis cache only works in server side.

import {UseCase} from '@domestika/domain'
import {inlineError, cache} from '@domestika/decorators'

export class GetSeoTagsSearchUseCase extends UseCase {
  @cache({
          server: true,
          ttl: '1 minute',
          redis: {host: 'localhost', port: 6379}
        })
  @inlineError
  async execute({adSearchParamsAggregate}) {
    const [seoTagsError, seoTagsResponse] = await this._service.execute({
      adSearchParamsAggregate
    })

    if (seoTagsError) {
      return Promise.reject(seoTagsError)
    }

    return seoTagsResponse?.toJSON()
  }
}

Configuration

To have Redis cache fully working, previously a Redis server should be up and running, you must set server flag to true and provide desired redis server connection config: @cache({server: true, redis: {host: YOUR_REDIS_HOST, port: YOUR_REDIS_PORT_NUMBER}}), if one of these params is not provided redis cache won't be activated.

To do real requests against your Redis server you must set USE_REDIS_IN_DOMESTIKA_DECORATORS_CACHE variable to true (process.env.USE_REDIS_IN_DOMESTIKA_DECORATORS_CACHE in SSR).

You can add it in your web-app config-[dev|pre|pro] file as USE_REDIS_IN_DOMESTIKA_DECORATORS_CACHE: true, or as you wish. In case you want to pass tests against a real redis server you should set this variable, otherwise tests are running against a mocked redis.

This decorator will look for a USE_VERSION_NAMESPACE_FOR_REDIS_DOMESTIKA_DECORATORS_CACHE variable in the host, global.USE_VERSION_NAMESPACE_FOR_REDIS_DOMESTIKA_DECORATORS_CACHE in SSR. This variable will add a version tag namespace in the cache key stored in Redis, it would be helpful to avoid not cleaned cache entries for different web-app deployed versions. If it's not decalred cache entry will be stored without version namespace in the key.

Options:

Common for both LRU and Redis:

  • ttl: Time to life for each cache register (default: 500ms)

  • server: If the cache will be used in a NodeJS env. Be careful that could break your server. You should set it to true if you are adding redis config and want to activate redis cache. (default: false)

  • algorithm: Which algorithm will be used to discard register in the cache when will be full. For now, only lru available. (default: lru)

  • size: Maximum number of registers in the cache, when they exceed this number they will be erased (default: 100)

  • cacheKeyString: String param containing cache key(it must be unique). It is useful to define a fixed cache key(constructor name + function name, e.g. cacheKeyString: GetAdListSearchUseCase#execute) and avoid problems with code minification. By default the following cache key will be created for ${target.constructor.name}::${fnName} (default: undefined)

Only for Redis:

  • redis: desired redis server connection config @cache({server: true, redis: {host: YOUR_REDIS_HOST, port: YOUR_REDIS_PORT_NUMBER}}). (default: undefined, if redis={} -> {host: '127.0.0.1', port: 6379}) Remember server flag must be true and process.env.USE_REDIS_IN_DOMESTIKA_DECORATORS_CACHE must be setted to true to connect to the provided redis server.

How to disable the cache

In some cases we might want to disable the cache for certain environment or testing purposes. In that case, we should expose a variable into the global scope as:

// For client side
window.__DOMESTIKA_CACHE_DISABLED__ = true

// Server side
global.__DOMESTIKA_CACHE_DISABLED__ = true

@tracer

Sends a performance timing metric to the configured reporter.

import {tracer} from '@domestika/decorators'

class SomeUseCase {
    @tracer({metric: 'METRIC_1'})
    execute({input}) {
        return ...
    }
}

Configuration

This decorator will look for a __DOMESTIKA_DECORATOR_TRACER_REPORTER__ variable in the host (window.__DOMESTIKA_DECORATOR_TRACER_REPORTER__ in browser/`global.DOMESTIKA_DECORATOR_TRACER_REPORTER in SSR).

If no reporter defined is found it will use the default ConsoleReporter which will output the messages in console.

Also, the tracer provides a `DataDogReporter which implements the Reporter Interface. This reporter needs a client to be passed to the reporter constructor. In this case, we are using hot-shots, which is a StatsD compatible client.

Note: be sure to define this in a server-only executed file.

import {DataDogReporter} from '@domestika/decorators/lib/decorators/tracer'
import StatsD from 'hot-shots'

global.__DOMESTIKA_DECORATOR_TRACER_REPORTER__ = new DataDogReporter({
     client: new StatsD({
       errorHandler: error => {
         console.log('Socket errors caught here: ', error)
       },
       globalTags: {
         env: process.env.NODE_ENV,
         node_ssr: 'milanuncios',
         origin: 'server'
       }
     }),
     siteName: 'ma'
   })

The provided DataDogReporter accepts a siteName parameter that will be appended to the metric name: frontend.${siteName}.tracer.datadog.reporter, so we could look for our metric in datadog as frontend.ma.tracer.datadog.reporter.

Usage

After having the reporter configured, you need to add the @tracer in the useCases / methods you want to be measured. The tracer uses the Performance API.

import {UseCase} from '@domestika/domain'
import {inlineError, tracer} from '@domestika/decorators'

export class GetAdSearchParamsFromURLSearchUseCase extends UseCase {
  ... 

  @tracer()
  @inlineError
  async execute({path}) {

The decorator accepts an optional metric parameter that will be sent to the reporter.

import {UseCase} from '@domestika/domain'
import {inlineError, tracer} from '@domestika/decorators'

export class GetAdSearchParamsFromURLSearchUseCase extends UseCase {
  ... 

  @tracer({metric: 'get_search_params'})
  @inlineError
  async execute({path}) {

Compatibility

The @tracer decorator works fine with the @inlineError decorator, but it should be placed first:

  (...)
  
  @tracer({metric: 'metric_1'})
  @inlineError
  async execute({path}) {
  ...