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

genix

v1.0.1

Published

Library for build event driven applications

Downloads

6

Readme

Genix

Build

genix is a library for build event driven applications in a easy way. Through genix we should be able to create applications with very low levels of coupling which are easy to tests, as main goals it has:

  • Decoupling
  • Maintainability
  • Reliability
  • Testing

Events

First of all we should say that genix supports events and commands as main building blocks, the differences between them are mainly semantic. Events should indicate actions that already happened, is common use them to notify that something changed in the application, for example data-loaded , order-ready, payment-done etc. One event have one or more handlers associate to it, for example:

import { onEvent, emit } from 'genix';

function f1() {
  onEvent('data-loaded', (data) => {
    console.log(data);
  });
}

function f2() {
  const data = { user: 'user' };
  emit('data-loaded', data);
}

f1();
f2(); // {user: 'user'}

In the above functions, the first one register a handler for data-loaded event and in the second this event is emitted. genix allow chain any numbers of events in a way that events can be emitted from handlers, so the following can be done:

import { onEvent, emit } from 'genix';

function f1() {
  onEvent('data-loaded', function* () {
    console.log(data);
    emit('data-processed', true);
  });
}

In the previous example first data-loaded is emitted and then data-processed.

Commands

In other hand commands are meant to indicate something which should happen. commands They should be named with a verb in imperative mood. When we trigger a command genix execute the handler associate with this command and return its value. The main differences between events and commands are that genix only allow one handler per command and we can get a return value after execute a command which can't happen in the case of events. Examples of commands can be load-data, prepare-order, execute-payment.

import { onCommand, exec } from 'genix';

function store() {
  const user = {
    Bob: {
      job: 'Software developer',
      userId: 1,
    },
  };

  onCommand('get-user', (userName) => {
    // Commands handlers can receive arguments
    return data[userName];
  });
}

function app() {
  const userInfo = exec('get-user', 'Bob');
  console.log(userInfo); // {userId: 1, job: 'Software Developer'}
}

store();
app();

Commands handlers can be any kind of function sync or async it doesn't matter genix will handle them correctly, and also from commands handlers we can emit events.

Testing

Before was mentioned that low coupling is one of the main goals of genix making in this way applications easier to test. In order to achieve that genix allow us wrap our function to emit events and commands against them, let's see an example:

Suppose that we have a counter function which is in charge to increment and decrement a specific value, this function wil react to increment command, also it expose a command get-value that return the actual value of our counter. Also this function has a handler for state-restored event which will cause that our counter goes to zero.

import { onCommand, onEvent } from 'genix';

function counter() {
  let value = 0;

  onCommand('increment', () => {
    value++;
  });

  onCommand('get-value', () => value);

  onEvent('state-restored', () => {
    value = 0;
  });
}

export default counter;

Now is time to test this function so we can ensure that it work correctly, let's see how genix can help us with that:

import genix from 'genix';
import counter from './counter';

describe('Counter', () => {
  it('should increment value correctly', async () => {
    const wrapper = genix.wrap(counter);

    wrapper.exec('increment').exec('increment').exec('get-value');

    const { data } = await wrapper.run();

    expect(data).toBe(2);
  });
});

Before continue with more tests a few things should be notice. First as we mention genix allow us wrap our functions so we can exec commands against it and also emit events. This operations are lazy in the sense that they are executed when the run function is called not before that. The run is asynchronous so this has as consequence that every test that use a genix wrapper should be an async test. After executed run we got and object that have a data property with the return value of the last command executed in our chain of operations for example if we execute this operations increment, state-restored, get-value then data will have the return value of get-value because it was the last command executed. Last but not least **functions using genix capabilities always should be wrapped in order to be tested, this is highly recommended in order to avoid race conditions and incorrect behaviors during testing. Being said this let's write a few more tests:

it('should decrement value correctly', () => {
    const wrapper = genix.wrap(counter);

    wrapper
      .exec('increment')
      .exec('increment')
      .exec('decrement')
      .exec('get-value');

    const { data } = await wrapper.run();
    expect(data).toBe(1);
});

it('should restore counter value correctly', () => {
    const wrapper = genix.wrap(counter);

    wrapper
      .exec('increment')
      .exec('decrement')
      .emit('state-restored')
      .exec('get-value');

    const { data } = await wrapper.run();

    expect(data).toBe(0);
});

Mocked commands

Sometimes we use a command inside of some handler, for example let's say that in the previous example our handler for increment event execute a get-amount command that return how much should be increased

onEvent('increment', () => {
  const amount = exec('get-amount');
  value += amount;
});

In this example our handler depends on get-amount command, with genix is very simple mock this command in order to test our increment handler:

it('should increment value correctly', async () => {
  const wrapper = genix.wrap(counter);

  // mocking get-amount response
  wrapper.onCommand('get-amount', () => 10);

  wrapper.exec('increment').exec('increment').exec('get-value');

  const { data } = await wrapper.run();

  expect(data).toBe(20);
});

Using the onCommand function we are able to set the response to any command used when our wrapper run, this is very util when we have handlers using commands from external dependencies like databases.

This are basic examples but show some of the capabilities of genix during testing, there missing parts here as inject fake commands during tests, capture events triggered during function execution. All of this will be covered in a section dedicated to fully cover the testing API in depth.

Examples

Future Plans

  • Improve documentation.
  • Add more examples.
  • Improve type coverage in the source code.
  • Integrate with React.js.
  • Integrate genix to use web workers under the hood.