npm package: @rlanz/bus
Description: A simple and lean driver-based service bus implementation
Author: Romain Lanz
Latest version: 0.4.0

<div align="center">
  <img src="https://github.com/RomainLanz/bus/assets/2793951/fb93dc3d-d05d-40e4-a66b-35bb2a161750" alt="@rlanz/bus">
</div>

<div align="center">

[![typescript-image]][typescript-url]
[![gh-workflow-image]][gh-workflow-url]
[![npm-image]][npm-url]
[![npm-download-image]][npm-download-url]
[![license-image]][license-url]

</div>

<hr />

`@rlanz/bus` is a service bus implementation for Node.js. It is designed to be simple and easy to use.

Currently, it supports the following transports:

<p>
👉 <strong>Memory:</strong> A simple in-memory transport for testing purposes.<br />
👉 <strong>Redis:</strong> A Redis transport for production usage.
</p>

## Table of Contents

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Installation](#installation)
- [Usage](#usage)
- [Retry Queue](#retry-queue)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Installation

```bash
npm install @rlanz/bus
```

## Usage

The module exposes a manager that can be used to register buses.

```typescript
import { BusManager } from '@rlanz/bus'
import { redis } from '@rlanz/bus/transports/redis'
import { memory } from '@rlanz/bus/transports/memory'

const manager = new BusManager({
  default: 'main',
  transports: {
    main: {
      transport: memory(),
    },
    redis: {
      transport: redis({
        host: 'localhost',
        port: 6379,
      }),
    }
  }
})
```

Once the manager is created, you can subscribe to channels and publish messages.

```typescript
manager.subscribe('channel', (message) => {
  console.log('Received message', message)
})

manager.publish('channel', 'Hello world')
```

By default, the bus will use the `default` transport. You can specify different transport by using the `use` method.

```typescript
manager.use('redis').publish('channel', 'Hello world')
```

### Without the manager

If you don't need multiple buses, you can create a single bus directly by importing the transports and the Bus class.

```typescript
import { Bus } from '@rlanz/bus'
import { RedisTransport } from '@rlanz/bus/transports/redis'

const transport = new RedisTransport({
  host: 'localhost',
  port: 6379,
})

const bus = new Bus(transport, {
  retryQueue: {
    retryInterval: '100ms'
  }
})
```

## Retry Queue

The bus also supports a retry queue. When a message fails to be published, it will be moved to the retry queue.

For example, your Redis server is down.

```typescript
const manager = new BusManager({
  default: 'main',
  transports: {
    main: {
      transport: redis({
        host: 'localhost',
        port: 6379,
      }),
      retryQueue: {
        retryInterval: '100ms'
      }
    },
  }
})

manager.use('redis').publish('channel', 'Hello World')
```

The message will be moved to the retry queue and will be retried every 100ms.

You have multiple options to configure the retry queue.

```typescript
export interface RetryQueueOptions {
  // Enable the retry queue (default: true)
  enabled?: boolean
  
  // Defines if we allow duplicates messages in the retry queue (default: true)
  removeDuplicates?: boolean
  
  // The maximum size of the retry queue (default: null)
  maxSize?: number | null
  
  // The interval between each retry (default: false)
  retryInterval?: Duration | false
}
```

## Test helpers

The module also provides some test helpers to make it easier to test the code that relies on the bus. First, you can use the `MemoryTransport` to create a bus that uses an in-memory transport.

You can also use the `ChaosTransport` to simulate a transport that fails randomly, in order to test the resilience of your code.

```ts
import { Bus } from '@rlanz/bus'
import { ChaosTransport } from '@rlanz/bus/test_helpers'

const buggyTransport = new ChaosTransport(new MemoryTransport())
const bus = new Bus(buggyTransport)

/**
 * Now, every time you will try to publish a message, the transport 
 * will throw an error.
 */
buggyTransport.alwaysThrow()
```

[gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/romainlanz/bus/test.yml?branch=main&style=for-the-badge
[gh-workflow-url]: https://github.com/romainlanz/bus/actions/workflows/test.yml
[npm-image]: https://img.shields.io/npm/v/@rlanz/bus.svg?style=for-the-badge&logo=npm
[npm-url]: https://www.npmjs.com/package/@rlanz/bus
[npm-download-image]: https://img.shields.io/npm/dm/@rlanz/bus?style=for-the-badge
[npm-download-url]: https://www.npmjs.com/package/@rlanz/bus
[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
[typescript-url]: https://www.typescriptlang.org
[license-image]: https://img.shields.io/npm/l/@rlanz/bus?color=blueviolet&style=for-the-badge
[license-url]: LICENSE.md