You're Invited: Meet the Socket team at BSidesSF and RSAC - April 27 - May 1.RSVP
Socket
Sign inDemoInstall
Socket

circuit-state

Package Overview
Dependencies
Maintainers
0
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

circuit-state - npm Package Compare versions

Comparing version

to
3.0.0

.github/workflows/npm-publish.yml

259

index.js

@@ -1,152 +0,237 @@

'use strict';
(function (global, factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
module.exports = factory();
}
else if (typeof define === 'function' && define.amd) {
define([], factory);
}
else {
global.CircuitBreakerState = factory().CircuitBreakerState;
global.CircuitBreakerOpenError = factory().CircuitBreakerOpenError;
}
}(typeof window !== 'undefined' ? window : this, function () {
const OPEN = Symbol('open'); //eslint-disable-line no-undef
const CLOSED = Symbol('closed'); //eslint-disable-line no-undef
const HALF_OPEN = Symbol('half_open'); //eslint-disable-line no-undef
const OPEN = Symbol('open');
const CLOSED = Symbol('closed');
const HALF_OPEN = Symbol('half_open');
class CircuitBreakerOpenError extends Error {
const SUCCEEDED_EVENT = 'succeeded';
const FAILED_EVENT = 'failed';
const CLOSED_EVENT = 'closed';
const OPEN_EVENT = 'opened';
const HALF_OPEN_EVENT = 'half_opened';
const EXECUTIONS = 'executions';
const SUCCESSES = 'successes';
const FAILURES = 'failures';
class CircuitBreakerOpenError extends Error {
constructor() {
super('Circuit breaker is open');
this.name = 'CircuitBreakerOpenError';
this.code = 'EPERM';
super('Circuit breaker is open');
this.name = 'CircuitBreakerOpenError';
this.code = 'EPERM';
}
}
}
class Stats {
class CircuitBreakerEventEmitter {
constructor() {
this._events = {};
}
on(event, listener) {
if (!this._events[event]) {
this._events[event] = [];
}
this._events[event].push(listener);
return this;
}
once(event, listener) {
const onceWrapper = (...args) => {
listener(...args);
this.removeListener(event, onceWrapper);
};
this.on(event, onceWrapper);
return this;
}
removeListener(event, listener) {
if (!this._events[event]) return this;
this._events[event] = this._events[event].filter((l) => l !== listener);
return this;
}
off(event, listener) {
return this.removeListener(event, listener);
}
emit(event, ...args) {
if (!this._events[event]) return false;
this._events[event].forEach((listener) => listener(...args));
return true;
}
removeAllListeners(event) {
if (event) {
delete this._events[event];
} else {
this._events = {};
}
return this;
}
}
class Stats {
constructor(cbState) {
this._cbState = cbState;
this._counts = {
executions: 0,
successes: 0,
failures: 0
};
this._cbState = cbState;
this._counts = {
executions: 0,
successes: 0,
failures: 0
};
}
increment(key) {
if (key === 'open') {
return;
}
if (!this._counts[key]) {
this._counts[key] = 0;
}
if (this._counts[key] === Number.MAX_SAFE_INTEGER) {
this._counts[key] = 0;
}
this._counts[key] += 1;
if (key === 'open') {
return;
}
if (!this._counts[key]) {
this._counts[key] = 0;
}
if (this._counts[key] === Number.MAX_SAFE_INTEGER) {
this._counts[key] = 0;
}
this._counts[key] += 1;
}
reset(key) {
this._counts[key] = 0;
this._counts[key] = 0;
}
resetAll() {
Object.keys(this._counts).forEach((key) => {
this.reset(key);
});
Object.keys(this._counts).forEach((key) => {
this.reset(key);
});
}
snapshot() {
return Object.assign({ open : this._cbState.open, ...this._counts });
return Object.assign({ open: this._cbState.open, ...this._counts });
}
}
}
class CircuitBreakerState {
constructor({ maxFailures = 3, resetTime = 10000, resetManually = false } = {}) {
this._state = CLOSED;
this._maxFailures = maxFailures;
this._failures = 0;
this._resetTimer = undefined;
this._resetTime = resetTime;
this._resetManually = resetManually;
this._stats = new Stats(this);
class CircuitBreakerState {
constructor({ maxFailures = 3, resetTime = 10000 } = {}) {
this._state = CLOSED;
this._maxFailures = maxFailures;
this._failures = 0;
this._resetTimer = undefined;
this._resetTime = resetTime;
this._resetManually = resetTime <= 0 ? true : false;
this._stats = new Stats(this);
this._events = new CircuitBreakerEventEmitter();
}
static create(options) {
return new CircuitBreakerState(options);
return new CircuitBreakerState(options);
}
get maxFailures() {
return this._maxFailures;
return this._maxFailures;
}
get resetTime() {
return this._resetTime;
return this._resetTime;
}
get stats() {
return this._stats;
return this._stats;
}
get events() {
return this._events;
}
_open() {
clearTimeout(this._resetTimer);
this._state = OPEN;
if (!this._resetManually) {
this._resetTimer = setTimeout(() => {
this._halfOpen();
}, this._resetTime);
this._resetTimer.unref();
clearTimeout(this._resetTimer);
this._state = OPEN;
this._events.emit(OPEN_EVENT, this._stats.snapshot());
if (!this._resetManually) {
this._resetTimer = setTimeout(() => {
this._halfOpen();
}, this._resetTime);
if (this._resetTimer.unref) {
this._resetTimer.unref();
}
this._failures = 0;
}
this._failures = 0;
}
_halfOpen() {
this._state = HALF_OPEN;
this._state = HALF_OPEN;
this._events.emit(HALF_OPEN_EVENT, this._stats.snapshot());
}
_close() {
clearTimeout(this._resetTimer);
this._state = CLOSED;
clearTimeout(this._resetTimer);
this._state = CLOSED;
this._events.emit(CLOSED_EVENT, this._stats.snapshot());
}
fail() {
++this._failures;
if (this.halfOpen) {
this._open();
return;
}
if (this._failures === this._maxFailures) {
this._open();
}
this._stats.increment('executions');
this._stats.increment('failures');
++this._failures;
if (this.halfOpen) {
this._open();
return;
}
if (this._failures === this._maxFailures) {
this._open();
}
this._stats.increment(EXECUTIONS);
this._stats.increment(FAILURES);
this._events.emit(FAILED_EVENT, this._stats.snapshot());
}
succeed() {
this._failures = 0;
if (this.halfOpen) {
this._close();
}
this._stats.increment('executions');
if (this.closed) {
this._stats.increment('successes');
}
else {
this._stats.increment('failures');
}
this._failures = 0;
if (this.halfOpen) {
this._close();
}
this._stats.increment(EXECUTIONS);
if (this.closed) {
this._stats.increment(SUCCESSES);
}
else {
this._stats.increment(FAILURES);
}
this._events.emit(this.closed ? SUCCEEDED_EVENT : FAILED_EVENT, this._stats.snapshot());
}
tryReset() {
clearTimeout(this._resetTimer);
this._halfOpen();
clearTimeout(this._resetTimer);
this._halfOpen();
}
get open() {
return this._state === OPEN;
return this._state === OPEN;
}
get halfOpen() {
return this._state === HALF_OPEN;
return this._state === HALF_OPEN;
}
get closed() {
return this._state == CLOSED;
return this._state === CLOSED;
}
test() {
if (this.open) {
return new CircuitBreakerOpenError();
}
if (this.open) {
return new CircuitBreakerOpenError();
}
}
}
}
module.exports = CircuitBreakerState;
return CircuitBreakerState;
}));
{
"name": "circuit-state",
"version": "1.0.0",
"version": "3.0.0",
"description": "Circuit breaker state machine.",

@@ -24,6 +24,6 @@ "author": "Trevor Livingston",

"devDependencies": {
"eslint": "^4.18.1",
"nyc": "^11.4.1",
"tape": "^4.9.0"
"eslint": "^4.19.1",
"nyc": "^11.9.0",
"tape": "^4.17.0"
}
}

@@ -21,4 +21,3 @@

- `maxFailures` - Maximum number of failures before circuit breaker flips open. Default `3`.
- `resetTime` - Time in ms before an open circuit breaker returns to a half-open state. Default `10000`.
- `resetManually` - Boolean value representing whether or not to attempt reset manually vs on timer. Default `false`.
- `resetTime` - Time in ms before an open circuit breaker returns to a half-open state. Default `10000`. If 0 or less, manual resets will be used.
- `CircuitBreakerState.create(options)` - Creates a new `CircuitBreakerState` instance.

@@ -30,3 +29,3 @@

- `fail()` - Record a failure. This may trip open the circuit breaker.
- `test()` - Tests for the state being open. If so, returns an error (may be returned to user).
- `test()` - Utility function to test for the state being open. If so, returns an error (may be returned to user).
- `tryReset()` - Flips to half-open and cancels reset timer (if any).

@@ -39,2 +38,3 @@ - `open` - Is `true` if this circuit breaker is open. Read-only.

- `resetTime` - Read-only.
- `events` - read only event emitter

@@ -48,3 +48,14 @@ Stats object:

Event emitter:
The `events` property on the `CircuitBreakerState` is an event emitter to which you can listen to the following events:
- `opened`
- `closed`
- `half_opened`
- `succeeded`
- `failed`
All of these events will receive a `snapshot` of the `Stats` object.
### Example usage

@@ -69,3 +80,5 @@

if (error) {
callback(error);
setImmediate(() => {
callback(error);
});
return;

@@ -96,4 +109,4 @@ }

class Circuit {
constructor(promise) {
this._promise = promise;
constructor(asyncFunc) {
this._asyncFunc = asyncFunc;
this._cb = new CircuitBreakerState();

@@ -104,2 +117,3 @@ }

// Fail fast
if (error) {

@@ -110,3 +124,3 @@ throw error;

try {
const result = await this._promise(...args);
const result = await this._asyncFunc(...args);
this._cb.succeed();

@@ -113,0 +127,0 @@ return result;