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

@universal-packages/dynamic-api

v1.12.4

Published

Dynamic decoupling-adapting system

Downloads

5,952

Readme

Dynamic API

npm version Testing codecov

Dynamic decoupling-adapting system, works mostly for when a system can be done in several opinionated ways and/or needs to be extended in a dynamic way, basically a dynamic API for internal systems, instead of calling concrete methods with concrete results, you called dynamic-api methods with dynamic results depending on the context.

Install

npm install @universal-packages/dynamic-api

DynamicApi

The DynamicApi class is the entry interface to load and perform all our Dynamics.

import { DynamicApi } from '@universal-packages/dynamic-api'

const dynamicApi = new DynamicApi({ dynamicsLocation: './src' })

await dynamicApi.loadDynamics()

const result = await dynamicApi.performDynamic('calculate', { fast: true })

console.log(result)

// > "I did it fast"

Options

  • debug Boolean If true the instance of this dynamic api will keep track of what is being performed into a log.

    console.log(dynamicApi.debugLog)
    
    // > [{ name: 'calculate', payload: { fast: true }, results: ['I did it fast'], hooks: { after: [AfterCalculateDynamic], before: [BeforeCalculateDynamic] } }]
  • dynamicsLocation Required String default: './src' Where to look up for dynamics to load.

  • namespace String When given the prefix of the file extension will be a mix of the provided namespace and the key word dynamic, ex: when name space is auth the files with the pattern file.auth-dynamic.js|ts will be loaded. If no namespace is provided the just files with the prefix dynamic will be loaded.

  • accumulate Boolean By default only the first dynamic with a given name will be performed and the retuning value will be returned to the user. When accumulate is true all dynamics with the same name will be performed and all the results will be accumulated in an array and returned to the user.

    const result = await dynamicApi.performDynamic('calculate', { fast: true })
    
    console.log(result)
    
    // > ["I did it fast", "I also did it fast"]

    -- modules Map When decorating dynamics you can mark them as part of a module, you need to enable modules explicitly in the dynamic api.

    import { Dynamic } from '@universal-packages/dynamic-api'
    
    @Dynamic('sub-calculations', 'extra')
    export default class CalculateDynamic {
      async perform(payload) {
        return 'I am an extra calculation'
      }
    }
    const dynamicApi = new DynamicApi({ modules: { 'sub-calculations': { enabled: true } } })
    const result = await dynamicApi.performDynamic('extra')
    
    console.log(result)
    
    // > 'I am an extra calculation'

Instance methods

performDynamic(name: string, payload: Object)

Performs a dynamic in an asynchronous way.

performDynamicSync(name: string, payload: Object)

To not waste overhead in async calls perform dynamics synchronically, they of course should implement a sync perform method.

Decorators

@Dynamic

@Dynamic(name: string, [default: boolean]) @Dynamic(module: string, name: string, [default: boolean])

Dynamics are classes as a default export, decorated with @Dynamic decorator and implementing the method perform.

import { Dynamic } from '@universal-packages/dynamic-api'

@Dynamic('calculate')
export default class CalculateDynamic {
  async perform(payload) {
    if (payload.fast) {
      return 'I did it fast'
    } else {
      return 'I was slow'
    }
  }
}

You can perform other dynamics inside your dynamics by accessing the second argument for the perform method where the dynamic api caller is shared.

import { Dynamic } from '@universal-packages/dynamic-api'

@Dynamic('calculate')
export default class CalculateDynamic {
  async perform(payload, dynamicApi) {
    if (payload.fast) {
      const speed = await dynamicApi.performDynamic('calculate-speed', { fast: true })

      return 'I did it fast like ' + speed + ' fast'
    } else {
      return 'I was slow'
    }
  }
}

The whole point of the dynamic API is to be extensible in all posable ways, to be dynamic if we will. When creating a dynamic API you may want to let he user override provided default dynamics, in order to let that happen we mark dynamics as default, if the user creates another dynamic with same name, then that dynamic will be performed instead of the default one.

import { Dynamic } from '@universal-packages/dynamic-api'

@Dynamic('calculate', true) // <-- True to be default
export default class CalculateDynamic {
  async perform(payload) {
    if (payload.fast) {
      return 'I did it fast'
    } else {
      return 'I was slow'
    }
  }
}

You can make a dynamic part of a module by providing a module name as the first argument.

import { Dynamic } from '@universal-packages/dynamic-api'

@Dynamic('sub-calculations', 'extra')
export default class CalculateDynamic {
  async perform(payload) {
    return 'I am an extra calculation'
  }
}

@DynamicHook(lifeCycle: before | after, name: string)

Hooks allows the user to perform some other tasks before and after a main dynamic is performed, for example you need to calculate something in a dynamic but need to also log that the calculation was done, instead of overriding the dynamic for your specific case you create a hook to run after the dynamic.

import { DynamicHook } from '@universal-packages/dynamic-api'

@DynamicHook('after', 'calculate')
export default class AfterCalculateDynamic {
  async perform(payload) {
    console.log('A calculation was made with:', payload)
  }
}

after hooks have the particularity of having access to the result given by the main dynamic.

import { DynamicHook } from '@universal-packages/dynamic-api'

@DynamicHook('after', 'calculate')
export default class AfterCalculateDynamic {
  async perform(payload, result) {
    console.log('A calculation was made with:', payload, 'and with result:', result)
  }
}

The same way as with dynamics you can perform other dynamics inside your dynamics hooks by accessing the second argument in before hooks and the third in after hooks for the perform method where the dynamic api caller is shared.

import { Dynamic } from '@universal-packages/dynamic-api'

@DynamicHook('before', 'calculate')
export default class BeforeCalculateDynamic {
  async perform(payload, dynamicApi) {
    console.log('about to calculate with:', payload)
    await dynamicApi.performDynamic('prepare-data')
  }
}

Events

DynamicApi is an emitter, it does not emit anything by itself but you can use it to communicate to other parts of your app what is going on in your dynamics.

import { Dynamic } from '@universal-packages/dynamic-api'

@Dynamic('calculate')
export default class CalculateDynamic {
  async perform(payload, dynamicApi) {
    dynamicApi.emit('event', 'A calculation was done')
    if (payload.fast) {
      return 'I did it fast'
    } else {
      return 'I was slow'
    }
  }
}

Typescript

This library is developed in TypeScript and shipped fully typed.

Typing dynamics

Since all here is dynamic performDynamic takes generic payloads and return generic results, if you want to type your dynamics you can always create an interface typing the payload and the result of your dynamics.

interface DynamicNames {
  calculate: {
    payload: { fast: boolean }
    result: string
  }
}

const dynamicApi = new DynamicApi<DynamicNames>({ dynamicsLocation: './src' })

// Now result is string type and performDynamic will require a payload of the specific shape
const result = dynamicApi.performDynamic('calculate', { fast: true })

Use your template names in the hooks as well

import { Dynamic } from '@universal-packages/dynamic-api'

import { DynamicNames } from './types'

@Dynamic<DynamicNames>('calculate')
export default class CalculateDynamic {}

Contributing

The development of this library happens in the open on GitHub, and we are grateful to the community for contributing bugfixes and improvements. Read below to learn how you can take part in improving this library.

License

MIT licensed.