MENU Navbar

Overview

Npm Version Build Status Test Coverage Maintainability Known Vulnerabilities dependencies Status devDependencies Status

Abstract

Install

npm install squiss-ts

High-volume Amazon SQS Poller and single-queue client for Node.js 6 and up with full typescript support
The library is production ready and is being stress used in a full blown production environment

Main features

Quick example

import {Squiss, Message} from 'squiss-ts';

const awsConfig = {
  accessKeyId: '<accessKeyId>',
  secretAccessKey: '<secretAccessKey>',
  region: '<region>',
};

const squiss = new Squiss({
  awsConfig,
  queueName: 'my-sqs-queue',
  bodyFormat: 'json',
  maxInFlight: 15
});

squiss.on('message', (message: Message) => {
  console.log(`${message.body.name} says: ${JSON.stringify(message.body.message)} and has attripute p1 with value ${message.attributes.p1}`);
  message.del();
});

squiss.start();

const messageToSend = {
    name: 'messageName',
    message: {
        a: 1,
        b: 2,
    },;
}

const propsToSend = {
    p1: 1,
    p2: 2,
};

squiss.sendMessage(messageToSend, 0, propsToSend);

How it works

Squiss processes as many messages simultaneously as possible.
Set the maxInFlight option to the number of messages your app can handle at one time without choking, and Squiss will keep that many messages flowing through your app, grabbing more as you mark each message as handled or ready for deletion.
If the queue is empty, Squiss will maintain an open connection to SQS, waiting for any messages that appear in real time.
Squiss can also handle renewing the visibility timeout for your messages until you handle the message, or message handling time (set up by you) has passed (see autoExtendTimeout).
Bonus: Squiss will also automatically handle the message attributes formatting and parsing when receiving and sending messages.

Versioning

This project adheres to Semantic Versioning

Current version

Npm Version

Squiss Class

import {Squiss} from 'squiss-ts';

const awsConfig = {
  accessKeyId: '<accessKeyId>',
  secretAccessKey: '<secretAccessKey>',
  region: '<region>',
};

const squiss = new Squiss({
  awsConfig,
  queueName: 'my-sqs-queue',
  bodyFormat: 'json',
  maxInFlight: 15
});

Queue message poller (EventEmitter)

Properties

Property Type Description
inFlight number The number of messages currently in flight
running boolean Whether Squiss is currently polling or not
sqs SQS The SQS model used

Constructor Options

Aws Options

awsConfig

const awsConfig = {
  accessKeyId: '<accessKeyId>',
  secretAccessKey: '<secretAccessKey>',
  region: '<region>',
};

An object mapping to pass to the SQS constructor, configuring the aws-sdk library.
This is commonly used to set the AWS region, endpoint, or the user credentials.
See the docs on configuring the aws-sdk for details.

Type SQS.Types.ClientConfiguration
Mandatory False

SQS

An instance of the official SQS Client, or an SQS constructor function to use rather than the default one provided by AWS.SQS

Type AWS.SQS | Object typeof AWS.SQS
Mandatory False
Default AWS.SQS

S3

An instance of the official S3 Client, or an S3 constructor function to use rather than the default one provided by AWS.S3

Type AWS.S3 | Object typeof AWS.S3
Mandatory False
Default AWS.S3

Queue Options

queueName

The name of the queue to be polled.

Type string
Mandatory True if queueUrl is not specified

queueUrl

The URL of the queue to be polled.

Type string
Mandatory True if queueName is not specified

accountNumber

If queueName is specified, the accountNumber of the queue owner can optionally be specified to access a queue in a different AWS account.

Type string | number
Mandatory False

correctQueueUrl

Changes the protocol, host, and port of the queue URL to match the configured SQS endpoint (see awsConfig), applicable only if queueName is specified.
This can be useful for testing against a local SQS service, such as ElasticMQ.

Type boolean
Mandatory False
Default false

visibilityTimeoutSecs

The amount of time, in seconds, that received messages will be unavailable to other squiss instances without being deleted.

Type number
Mandatory False
Default queue setting on read, or 30 seconds for createQueue()

Auto Extend Options

Squiss can automatically extend each message's VisibilityTimeout in the SQS queue until it's handled (by keeping, deleting, or releasing it).
It will place the API call to extend the timeout advancedCallMs milliseconds in advance of the expiration, and will extend it by the number of seconds specified in visibilityTimeoutSecs.
If visibilityTimeoutSecs is not specified, the VisibilityTimeout setting on the queue itself will be used.

autoExtendTimeout

Enable the message auto extend feature

Type boolean
Mandatory False
Default false

noExtensionsAfterSecs

If autoExtendTimeout is used, Squiss will stop auto-renewing a message's VisibilityTimeout when it reaches this age. Default is 12 hours - SQS's VisbilityTimeout maximum.

Type number
Mandatory False
Default 43200

advancedCallMs

If autoExtendTimeout is used, this is the number of milliseconds that Squiss will make the call to extend the VisibilityTimeout of the message, before the message is set to expire.

Type number
Mandatory False
Default 5000

Gzip Options

You can gzip the messages, which will reduce the message size, thus enabling you to send large messages, and also reduce the cost of those message.
You can optionally gzip only if a min message size criteria is met (minGzipSize), to reduce compute overhead.

gzip

Auto gzip messages to reduce message size.

Type boolean
Mandatory False
Default false

minGzipSize

The min message size to gzip (in bytes) when gzip is set to true.

Type number
Mandatory False
Default 0

S3 Options

const s3Options = {
  s3Fallback: true,
  s3Bucket: 'squiss-bucket',
  s3Retain: false,
  s3Prefix: `${queueName}`,
  minS3Size: 64000,
};

you can upload the messages body to S3 and replace the message with just the reference to the created blob.
This will reduce the message size, thus enabling you to send large messages, and also reduce the cost of those messages.

s3Fallback

Upload messages bigger than minS3Size or queue default maxMessageBytes to S3, and retrieve it from there when message is received.

Type boolean
Mandatory False
Default false

s3Bucket

if s3Fallback is set to true, upload message to this S3 bucket.

Type string
Mandatory True if s3Fallback is set to true

s3Retain

if s3Fallback is true, do not delete blob on message delete.

Type boolean
Mandatory False
Default false

minS3Size

The min message size to send to S3 (in bytes) when s3Fallback is set to true.

Type number
Mandatory False
Default queue max message size

s3Prefix

if s3Fallback is set to true, set this prefix to uploaded S3 blobs.

Type string
Mandatory False
Default ""

Delete options

deleteBatchSize

The number of messages to delete at one time.
Squiss will trigger a batch delete when this limit is reached, or when deleteWaitMs milliseconds have passed since the first queued delete, whichever comes first.
Set to 1 to make all deletes immediate.

Type number (max 10)
Mandatory False
Default 10

deleteWaitMs

The number of milliseconds to wait after the first queued message deletion before deleting the message(s) from SQS.

Type number
Mandatory False
Default 2000

Polling Options

activePollIntervalMs

The number of milliseconds to wait between requesting batches of messages when the queue is not empty, and the maxInFlight cap has not been hit.
For most use cases, it's better to leave this at 0 and let Squiss manage the active polling frequency according to maxInFlight.

Type number
Mandatory False
Default 0

idlePollIntervalMs

The number of milliseconds to wait before requesting a batch of messages when the queue was empty in the prior request.

Type number
Mandatory False
Default 0

maxInFlight

The number of messages to keep "in-flight", or processing simultaneously.
When this cap is reached, no more messages will be polled until currently in-flight messages are marked as deleted or handled.
Setting this option to 0 will uncap your in flight messages, pulling and delivering messages as long as there are messages to pull.

Type number
Mandatory False
Default 100

pollRetryMs

The number of milliseconds to wait before retrying when Squiss's call to retrieve messages from SQS fails.

Type number
Mandatory False
Default 2000

receiveBatchSize

The number of messages to receive at one time.

Type number (max 10 or maxInFlight)
Mandatory False
Default 10

minReceiveBatchSize

The minimum number of available message slots that will initiate a call to get the next batch.

Type number (max 10 or maxInFlight, whichever is lower)
Mandatory False
Default 1

receiveWaitTimeSecs

The number of seconds for which to hold open the SQS call to receive messages, when no message is currently available.
It is recommended to set this high, as Squiss will re-open the receiveMessage HTTP request as soon as the last one ends. If this needs to be set low, consider setting activePollIntervalMs to space out calls to SQS.

Type number (max 20)
Mandatory False
Default 20

Formatting Options

bodyFormat

The format of the incoming message.
Set to "json" to automatically call JSON.parse() on each incoming message.

Type 'json' | 'plain'
Mandatory False
Default plain

unwrapSns

Set to true to denote that Squiss should treat each message as though it comes from a queue subscribed to an SNS endpoint, and automatically extract the message from the SNS metadata wrapper.

Type boolean
Mandatory False
Default false

Attributes Options

receiveAttributes

An an array of strings with attribute names (e.g. myAttribute) to request along with the SQS.receiveMessage() call.
The attributes will be accessible via message.attributes.<attributeName>.

Type string[]
Mandatory False
Default ['All']

receiveSqsAttributes

An an array of strings with attribute names (e.g. ApproximateReceiveCount) to request along with the SQS.receiveMessage() call.
The attributes will be accessible via message.sqsAttributes.<attributeName>.

Type string[]
Mandatory False
Default ['All']

Queue Create Options

Are you using Squiss to create your queue as well? Squiss will use receiveWaitTimeSecs and visibilityTimeoutSecs in the queue create options, but consider setting any of the following options to configure it further.

delaySecs

The number of milliseconds by which to delay the delivery of new messages into the queue by default.

Type number
Mandatory False
Default 0

maxMessageBytes

The maximum size of a single message, in bytes, that the queue can support.

Type number
Mandatory False
Default 262144 (256KB)

messageRetentionSecs

The amount of time for which to retain messages in the queue until they expire, in seconds.

Type number (max 1209600 (14 days))
Mandatory False
Default 345600 (4 days)

queuePolicy

f specified, will be set as the access policy of the queue when createQueue() is called.
See the AWS Policy documentation for more information.

Type string
Mandatory False

Methods

Lifecycle Methods

start(): Promise<void>

squiss.start()
  .then(() => {
    console.log(`polling started`);
  });

Starts polling SQS for new messages. Each new message is handed off in the message event.

stop(soft?: boolean, timeout?: number): Promise<boolean>

squiss.stop()
  .then(() => {
    console.log(`polling stopped`);
  });

Hold on to your hats, this one stops the polling, aborting any in-progress request for new messages.
If called with soft=true while there's an active request for new messages, the active request will not be aborted and the message event may still be fired up to receiveWaitTimeSecs afterward.
A Promise will be returned and resolved with true when the queue is completely drained (all messages were handled), or false if a timeout value was sent and it passed before the queue was drained.

Message Methods

sendMessage(message: Object | string, delay?: number, attributes?: IMessageAttributes): Promise<SQS.Types.SendMessageResult>

squiss.sendMessage({a:1, b:2}, undefined, {correlationId: 'my correlation id'})
  .then((result) => {
    console.log(`message sent with id ${result.MessageId}`);
  });
squiss.sendMessage('my message data', 5000)
  .then((result) => {
    console.log(`message sent with id ${result.MessageId}`);
  });

Sends an individual message to the configured queue, and returns a promise that resolves with AWS's official message metadata: an object containing MessageId, MD5OfMessageAttributes, and MD5OfMessageBody.

Arguments:

For more information, see the official AWS documentation.

sendMessages(messages: {Object | string}[] | Object | string, delay?: number, attributes?: IMessageAttributes | IMessageAttributes[]): Promise<SQS.Types.SendMessageBatchResult>

squiss.sendMessage([{a:1, b:2}], undefined, {correlationId: 'my correlation id'})
  .then((result) => {
    console.log(`message ${result.Successful[0].Id} sent with MessageId ${result.Successful[0].MessageId}`);
  });
squiss.sendMessage([{a:1, b:2}, {c:3, d:4}], undefined, [{correlationId: 'my correlation id'}, {correlationId: 'my correlation id2'}])
  .then((result) => {
    console.log(`message ${result.Successful[0].Id} sent with MessageId ${result.Successful[0].MessageId}`);
  });

Sends an array of any number of messages to the configured SQS queue, breaking them down into appropriate batch requests executed in parallel (or as much as the default HTTP agent allows).
It returns a promise that resolves with a response closely aligned to the official AWS SDK's sendMessageBatch, except the results from all batch requests are merged.
The "Id" property supplied in the response will be the index of the message in the original messages array, in string form.

Arguments:

For more information, see the official AWS documentation.

changeMessageVisibility(message: Message | string,timeoutInSeconds: number): Promise<void>

squiss.changeVisibility(message, 5000)
  .then(() => {
    console.log('message visibility changed');
  });

Changes the visibility timeout of a message, given either Message object or the receipt handle string.

deleteMessage(message: Message): Promise<void>

squiss.deleteMessage(message)
  .then(() => {
    console.log('message deleted');
  });

Deletes a Message.
It's much easier to call message.del(), but if you need to do it right from the Squiss instance, this is how.

handledMessage(message: Message): void

squiss.handledMessage(message);

Informs Squiss that you got a message that you're not planning on deleting, so that Squiss can decrement the number of "in-flight" messages.
It's good practice to delete every message you process, but this can be useful in case of error.
You can also call message.keep() on the message itself to invoke this.

releaseMessage(message: Message): Promise<void>

squiss.releaseMessage(message)
  .then(() => {
    console.log('message released');
  });

Releases the given Message object back to the queue by setting its VisibilityTimeout to 0 and marking the message as handled internally. You can also call `message.release() on the message itself to invoke this.

Queue Methods

createQueue(): Promise<string>

squiss.createQueue()
  .then((queueUrl: string) => {
    console.log(`created queue ${queueUrl}`);
  });

Creates the configured queue.
Returns a promise that resolves with the new queue's URL when it's complete.

deleteQueue(): Promise<void>

squiss.deleteQueue()
  .then(() => {
    console.log('queue deleted');
  });

Deletes the configured queue, returning a promise that resolves on complete.
Squiss lets you do this, even though it makes Squiss useless. Squiss is so selfless.

purgeQueue(): Promise<void>

squiss.purgeQueue()
  .then(() => {
    console.log('queue purged');
  });

Deletes all the messages in a queue and init the in flight state.

getQueueUrl(): Promise<string>

squiss.getQueueUrl()
  .then((queueUrl: string) => {
    console.log(`queue url is ${queueUrl}`);
  });

Returns a Promise that resolves with the URL of the configured queue, even if you only instantiated Squiss with a queueName.
The correctQueueUrl setting applies to this result, if it was set.

getQueueVisibilityTimeout(): Promise<number>

squiss.getQueueVisibilityTimeout()
  .then((visibilityTimeout: number) => {
    console.log(`queue visibility timeout in seconds is ${visibilityTimeout}`);
  });

Retrieves the VisibilityTimeout (in seconds) set on the target queue.
When a message is received, it has this many seconds to be deleted before it will become available to be received again.

getQueueMaximumMessageSize(): Promise<number>

squiss.getQueueMaximumMessageSize()
  .then((maxSize: number) => {
    console.log(`queue max message size is ${maxSize}`);
  });

Retrieves the max message size (in bytes) set on the target queue.

Events

Queue Events

drained

squiss.on('drained', () => {
  console.log('no more messages being handled');
});

Emitted when the last in-flight message has been handled, and there are no more messages currently in flight.

queueEmpty

squiss.on('queueEmpty', () => {
  console.log('no new messages');
});

Emitted when Squiss asks SQS for new messages, and doesn't get any.

maxInFlight

squiss.on('maxInFlight', () => {
  console.log('max in flight cap reached');
});

Emitted when Squiss has hit the maxInFlight cap.
At this point, Squiss won't retrieve any more messages until at least 1 messages have been handled (e.g. deleted/released/kept).

error <Error>

squiss.on('error', (error: Error) => {
  console.log(`squiss error ${error}`);
});

Emitted if any of the AWS API calls outright fail.

aborted <AWSError>

squiss.on('aborted', (error: AWSError) => {
  console.log(`failed to extend message ${error}`);
});

Emitted if a request for new messages was already in progress while performing a hard stop().

Message Events

message

squiss.on('message', (message: Message) => {
  console.log('message received');
});

Emitted every time Squiss pulls a new message from the queue.

delQueued

squiss.on('delQueued', (message: Message) => {
  console.log('message queued for deletion');
});

Emitted when a message is queued for deletion, even if delete queuing has been turned off.

handled

squiss.on('handled', (message: Message) => {
  console.log('message was handled');
});

Emitted when a message is handled by any means: deleting, releasing, or calling keep().

released

squiss.on('released', (message: Message) => {
  console.log('message released');
});

Emitted after release() or releaseMessage() has been called and the VisibilityTimeout of a message has successfully been changed to 0.
The handled event will also be fired for released messages, but that will come earlier, when the release function is initially called.

timeoutReached

squiss.on('timeoutReached', (message: Message) => {
  console.log('message timeout reached');
});

Emitted when a message reaches it's timeout limit, including any extensions made with the autoExtendTimeout feature.

extendingTimeout

squiss.on('extendingTimeout', (message: Message) => {
  console.log('extending message timeout');
});

Emitted when a message VisibilityTimeout is about to be extended with the autoExtendTimeout feature.

timeoutExtended

squiss.on('timeoutExtended', (message: Message) => {
  console.log('message timeout was extended');
});

Emitted when a message VisibilityTimeout was extended with the autoExtendTimeout feature.

keep

squiss.on('keep', (message: Message) => {
  console.log('message kept');
});

Emitted after message.keep() has been called.
This happens when the timeout extender logic has exhausted all of its tries to extend the message visibility.

gotMessages <number>

squiss.on('gotMessages', (numOfMessages: number) => {
  console.log(`got ${numOfMessages} messages`);
});

Emitted when Squiss asks SQS for a new batch of messages, and gets some (or one).
Supplies the number of retrieved messages.

deleted <{message: Message, successId: string}>

squiss.on('deleted', (deletedEvent: IMessageDeletedEventPayload) => {
  console.log('message deleted');
});

Emitted when a message is confirmed as being successfully deleted from the queue.
The handled and delQueued events will also be fired for deleted messages, but that will come earlier, when the delete function is initially called.

delError <{message: Message, error: BatchResultErrorEntry}>

squiss.on('delError', (error: IMessageDeleteErrorEventPayload) => {
  console.log(`failed to delete message ${error}`);
});

Emitted when the message failed to get deleted. The object handed to you in this event is the AWS failure object described in the SQS deleteMessageBatch documentation.

autoExtendFail <{message: Message, error: AWSError}>

squiss.on('autoExtendFail', (error: IMessageErrorEventPayload) => {
  console.log(`failed to extend message ${error}`);
});

Emitted if autoExtendTimeout feature is enabled, and Squiss attempts to extend the message VisibilityTimeout that has either been deleted or otherwise expired.

autoExtendError <{message: Message, error: AWSError}>

squiss.on('autoExtendError', (error: IMessageErrorEventPayload) => {
  console.log(`failed to extend message ${error}`);
});

Emitted if autoExtendTimeout feature is enabled, and Squiss failed to extend the message VisibilityTimeout.

S3 Events

s3Download <{message: Message, data: {bucket: string, key: string, uploadSize: number}}>

squiss.on('s3Download', (payload: IMessageS3EventPayload) => {
  console.log(`downloaded s3 message ${payload.data.key}`);
});

Emitted if s3Fallback feature is enabled, and a message that was received downloaded its message body from S3.

s3Delete <{message: Message, data: {bucket: string, key: string, uploadSize: number}}>

squiss.on('s3Delete', (payload: IMessageS3EventPayload) => {
  console.log(`deleted s3 message ${payload.data.key}`);
});

Emitted if s3Fallback feature is enabled, and a message that was received with message body from S3 was deleted.

s3Upload <{bucket: string, key: string, uploadSize: number}>

squiss.on('s3Upload', (payload: IS3Upload) => {
  console.log(`uploaded s3 message ${payload.key}`);
});

Emitted if s3Fallback feature is enabled, and a message that was sent uploaded its body to S3.

Message Class

squiss.on('message', (message: Message) => {
  console.log(`${message.body.name} says: ${JSON.stringify(message.body.message)} and has attripute p1 with value ${message.attributes.p1}`);
  message.del();
});

SQS message handler (EventEmitter)

Properties

Property Type Description
raw SQS.Message The raw SQS message
body string | Object The parsed body of the message
subject string The SNS subject
topicArn string The SNS topic arn
topicName string The SNS topic name
attributes Object The user message attributes
sqsAttributes Object The SQS message attributes

Methods

parse(): Promise<string | any>

message.parse()
  .then((body) => {
    console.log(body);
  });

Parses the message and store it in body.
If the user requested to parse the message as json, it will be stored (and returned) as such.
The main purposes of this method are:

isHandled(): boolean

console.log(`message handled? ${message.isHandled()}`);

Returns true if the message was already handled (e.g. deleted/released/kept)

del(): Promise<void>

message.del()
  .then(() => {
    console.log('message deleted');
  });

Marks the message as handled and queue the message for deletion.
Returns once the message was deleted from the queue.

keep(): void

message.keep();

Marks the message as handled and release the message slot in Squiss (to allow another message to be fetched instead) while not performing any queue operation on it.

release(): Promise<void>

message.release()
  .then(() => {
    console.log('message released');
  });

Marks the message as handled and release the message back to the queue (e.g. changes the visibility timeout of the message to 0)
Returns once the message was released back to the queue.

changeVisibility(timeoutInSeconds: number): Promise<void>

message.changeVisibility(5000)
  .then(() => {
    console.log('message visibility changed');
  });

Changes the visibility timeout of the message

Events

Lifecycle Events

delQueued

message.on('delQueued', () => {
  console.log('message queued for deletion');
});

Emitted when a message is queued for deletion, even if delete queuing has been turned off.

handled

message.on('handled', () => {
  console.log('message was handled');
});

Emitted when a message is handled by any means: deleting, releasing, or calling keep().

released

message.on('released', () => {
  console.log('message released');
});

Emitted after keep() or releaseMessage() has been called and the VisibilityTimeout of a message has successfully been changed to 0.
The handled event will also be fired for released messages, but that will come earlier, when the release function is initially called.

keep

message.on('keep', () => {
  console.log('message kept');
});

Emitted after keep() has been called.
This happens when the timeout extender logic has exhausted all of its tries to extend the message visibility.

delError <BatchResultErrorEntry>

message.on('delError', (error: BatchResultErrorEntry) => {
  console.log(`failed to delete message ${error}`);
});

Emitted when the message failed to get deleted. The object handed to you in this event is the AWS failure object described in the SQS deleteMessageBatch documentation.

deleted

message.on('deleted', () => {
  console.log('message deleted');
});

Emitted when a message is confirmed as being successfully deleted from the queue.
The handled and delQueued events will also be fired for deleted messages, but that will come earlier, when the delete function is initially called.

Timeout Events

timeoutReached

message.on('timeoutReached', () => {
  console.log('message timeout reached');
});

Emitted when a message reaches it's timeout limit, including any extensions made with the autoExtendTimeout feature.

extendingTimeout

message.on('extendingTimeout', () => {
  console.log('extending message timeout');
});

Emitted when a message VisibilityTimeout is about to be extended with the autoExtendTimeout feature.

timeoutExtended

message.on('timeoutExtended', () => {
  console.log('message timeout was extended');
});

Emitted when a message VisibilityTimeout was extended with the autoExtendTimeout feature.

autoExtendFail <AWSError>

message.on('autoExtendFail', (error: AWSError) => {
  console.log(`failed to extend message ${error}`);
});

Emitted if autoExtendTimeout feature is enabled, and Squiss attempts to extend the message VisibilityTimeout that has either been deleted or otherwise expired.

autoExtendError <AWSError>

message.on('autoExtendError', (error: AWSError) => {
  console.log(`failed to extend message ${error}`);
});

Emitted if autoExtendTimeout feature is enabled, and Squiss failed to extend the message VisibilityTimeout.

S3 Events

s3Download <{bucket: string, key: string, uploadSize: number}>

message.on('s3Download', (payload: IS3Upload) => {
  console.log(`downloaded s3 message ${payload.key}`);
});

Emitted if s3Fallback feature is enabled, and a message that was received downloaded its message body from S3.

s3Delete <{bucket: string, key: string, uploadSize: number}>

message.on('s3Delete', (payload: IS3Upload) => {
  console.log(`deleted s3 message ${payload.key}`);
});

Emitted if s3Fallback feature is enabled, and a message that was received with message body from S3 was deleted.

Support

All support questions and issues should be submitted here

Credits

This project is a typescript port (with better performance, bug fixes and new features) of the wonderful and unmaintnaed project TomFrost/Squiss
Squiss was originally created at TechnologyAdvice in Nashville, TN.

Contributors

dannyrscott dependabot[bot] greenkeeper[bot] ksafranski loris mend-bolt-for-github[bot]
dannyrscott dependabot[bot] greenkeeper[bot] ksafranski loris mend-bolt-for-github[bot]
Raiszo regevbr TomFrost Ugzuzg upugo-dev
Raiszo regevbr TomFrost Ugzuzg upugo-dev

Full contributors list

Changelog

This project adheres to Semantic Versioning.

Development

v4.4.0

diff

Fixed

v4.1.1

diff

Fixed

v4.1.0

diff

Fixed

v4.0.10

diff

Fixed

v4.0.9

diff

Fixed

v4.0.8

diff

Fixed

v4.0.7

diff

Fixed

v4.0.6

diff

Fixed

v4.0.5

diff

Fixed

v4.0.4

diff

Fixed

v4.0.3

diff

Fixed

v4.0.2

diff

Fixed

v4.0.1

diff

Fixed

Added

v4.0.0

diff

Fixed

Added

v3.0.1

diff

Fixed

Added

v3.0.0

diff

Added

Fixed

v2.0.0

diff

Marking the library as stable after stress usage in a full blown production environment

v1.5.2

diff

Fixed

v1.5.1

diff

Fixed

v1.5.0

diff

Added

Fixed

v1.4.0

diff

Added

Fixed

v1.3.0

diff

Added

Fixed

v1.2.4

diff

Fixed

v1.2.3

diff

Fixed

v1.2.2

diff

Fixed

v1.2.1

Fixed

v1.2.0

Added

v1.1.0

Fixed

Added

v1.0.0

Added