Comparing version 2.0.3 to 2.0.4
538
lib/asc.js
'use strict'; | ||
const __ = require( 'doublescore' ); | ||
const async = require( 'async' ); | ||
const Memory = require( './memory' ); | ||
const util = require( './util' ); | ||
const async = require('async'); | ||
const Memory = require('./memory'); | ||
const util = require('./util'); | ||
function isObject(thing) { | ||
return typeof thing === 'object' && thing !== null; | ||
} | ||
class ASC { | ||
/** | ||
* Constructor Data Types | ||
* | ||
* @typedef MemoryParams | ||
* @type {object} | ||
* @property {boolean} [disabled=false] If TRUE, the in-memory cache is disabled | ||
* @property {number} [ttl=60000] The TTL in milliseconds for the in-memory cache | ||
* | ||
* @callback DataCallback | ||
* @param {Error} err - An instance of Error if an error occurred, null otherwise | ||
* @param {any} [data] - Any data, MUST only be omitted if err is set. | ||
* | ||
* @callback ErrorOnlyCallback | ||
* @param {Error} [err] - An instance of Error if an error occurred, empty or null otherwise | ||
* | ||
* @callback GetCallback | ||
* @param {string} key - The key of the data to return | ||
* @param {DataCallback} - The done callback to pass an error or data to | ||
* | ||
* @callback SetCallback | ||
* @param {string} key - The key of the data to return | ||
* @param {any} data - The data to set for respective key | ||
* @param {ErrorOnlyCallback} - The done callback | ||
* | ||
* @callback ClearCallback | ||
* @param {string} key - The key of the data to return | ||
* @param {ErrorOnlyCallback} - The done callback | ||
* | ||
* @typedef CacheLayer | ||
* @type {object} | ||
* @property {DataCallback} get The get function | ||
* @property {SetCallback} [set] The set data function | ||
* @property {ClearCallback} [clear] The clear function | ||
* | ||
* @typedef ConstructorParams | ||
* @type {object} | ||
* @param {MemoryParams} [ConstructorParams.memory] | ||
* @param {[CacheLayer]} [ConstructorParams.layers] | ||
* @param {GetCallback} [ConstructorParams.get] | ||
* | ||
*/ | ||
/** | ||
* Constructor Data Types | ||
* | ||
* @typedef MemoryParams | ||
* @type {object} | ||
* @property {boolean} [disabled=false] If TRUE, the in-memory cache is disabled | ||
* @property {number} [ttl=60000] The TTL in milliseconds for the in-memory cache | ||
* | ||
* @callback DataCallback | ||
* @param {Error} err - An instance of Error if an error occurred, null otherwise | ||
* @param {any} [data] - Any data, MUST only be omitted if err is set. | ||
* | ||
* @callback ErrorOnlyCallback | ||
* @param {Error} [err] - An instance of Error if an error occurred, empty or null otherwise | ||
* | ||
* @callback GetCallback | ||
* @param {string} key - The key of the data to return | ||
* @param {DataCallback} - The done callback to pass an error or data to | ||
* | ||
* @callback SetCallback | ||
* @param {string} key - The key of the data to return | ||
* @param {any} data - The data to set for respective key | ||
* @param {ErrorOnlyCallback} - The done callback | ||
* | ||
* @callback ClearCallback | ||
* @param {string} key - The key of the data to return | ||
* @param {ErrorOnlyCallback} - The done callback | ||
* | ||
* @typedef CacheLayer | ||
* @type {object} | ||
* @property {DataCallback} get The get function | ||
* @property {SetCallback} [set] The set data function | ||
* @property {ClearCallback} [clear] The clear function | ||
* | ||
* @typedef ConstructorParams | ||
* @type {object} | ||
* @param {MemoryParams} [ConstructorParams.memory] | ||
* @param {[CacheLayer]} [ConstructorParams.layers] | ||
* @param {GetCallback} [ConstructorParams.get] | ||
* | ||
*/ | ||
/** | ||
* Return an instance of ASC class. | ||
* | ||
* @param {ConstructorParams|[CacheLayer]|GetCallback} params Can either be a configuration object, an array of just | ||
* layers, or just a single get function for handling in-memory misses. | ||
* | ||
*/ | ||
constructor( params ) { | ||
/** | ||
* Return an instance of ASC class. | ||
* | ||
* @param {ConstructorParams|[CacheLayer]|GetCallback} params Can either be a configuration object, an array of just | ||
* layers, or just a single get function for handling in-memory misses. | ||
* | ||
*/ | ||
constructor(params) { | ||
this._init( params ); | ||
this._setup(); | ||
this._init(params); | ||
this._setup(); | ||
} | ||
} | ||
_init( params ) { | ||
_init(params) { | ||
// clamp to defaults | ||
params = typeof params === 'function' ? { // if params is just the get function, convert it to shortcut get | ||
get: params | ||
} : params; | ||
params = __.isArray( params ) ? { // if params is just the array of layers, convert it to a shortcut layers | ||
layers: params | ||
} : params; | ||
params = __.isObject( params ) ? params : {}; | ||
this._layers = __.isArray( params.layers ) ? params.layers : []; | ||
// clamp to defaults | ||
params = typeof params === 'function' ? { // if params is just the get function, convert it to shortcut get | ||
get: params | ||
} : params; | ||
params = Array.isArray(params) ? { // if params is just the array of layers, convert it to a shortcut layers | ||
layers: params | ||
} : params; | ||
params = isObject(params) ? params : {}; | ||
this._layers = Array.isArray(params.layers) ? params.layers : []; | ||
// shortcut for cache with no middle layers | ||
if ( typeof params.get === 'function' ) { | ||
this._layers.push( { | ||
get: params.get | ||
} ); | ||
} | ||
// shortcut for cache with no middle layers | ||
if (typeof params.get === 'function') { | ||
this._layers.push({ | ||
get: params.get | ||
}); | ||
} | ||
this._memoryParams = __( { | ||
disabled: false | ||
} ).mixin( params.memory || {} ); | ||
const memoryParams = params.memory || {}; | ||
if ( this._layers.length < 1 ) { | ||
throw new Error( 'no caching layers provided' ); | ||
this._memoryParams = { | ||
disabled: !!memoryParams.disabled, | ||
ttl: memoryParams.ttl || 1000, | ||
}; | ||
if (this._layers.length < 1) { | ||
throw new Error('no caching layers provided'); | ||
} | ||
} | ||
} | ||
_setup() { | ||
_setup() { | ||
this._serviceQueues = {}; | ||
this._serviceQueues = {}; | ||
this._getLayers = []; | ||
this._setLayers = []; | ||
this._clearLayers = []; | ||
this._getLayers = []; | ||
this._setLayers = []; | ||
this._clearLayers = []; | ||
// if memory cache enabled, make it first | ||
if (!this._memoryParams.disabled) { | ||
// if memory cache enabled, make it first | ||
if ( !this._memoryParams.disabled ) { | ||
delete this._memoryParams.disabled; | ||
delete this._memoryParams.disabled; | ||
const memory = new Memory(this._memoryParams); | ||
const memory = new Memory( this._memoryParams ); | ||
// prefix memory handler to layers | ||
this._layers.unshift({ | ||
get: (key, done) => { | ||
return memory.get(key, done); | ||
}, | ||
set: (key, data, done) => { | ||
return memory.set(key, data, done); | ||
}, | ||
clear: (key, done) => { | ||
return memory.clear(key, done); | ||
} | ||
}); | ||
// prefix memory handler to layers | ||
this._layers.unshift( { | ||
get: ( key, done ) => { | ||
return memory.get( key, done ); | ||
}, | ||
set: ( key, data, done ) => { | ||
return memory.set( key, data, done ); | ||
}, | ||
clear: ( key, done ) => { | ||
return memory.clear( key, done ); | ||
} | ||
} ); | ||
} | ||
// this handler ignores errors and data returned by the layer | ||
// this is used for set calls | ||
const generateSetHandler = (handler) => { | ||
// this handler ignores errors and data returned by the layer | ||
// this is used for set calls | ||
const generateSetHandler = ( handler ) => { | ||
return (key, data, done) => { | ||
return ( key, data, done ) => { | ||
handler(key, data, () => { | ||
// ignore any errors or data the handler returns | ||
done(); | ||
}); | ||
handler( key, data, () => { | ||
// ignore any errors or data the handler returns | ||
done(); | ||
} ); | ||
}; | ||
}; | ||
}; | ||
}; | ||
// this handler ignores errors and data returned by the layer | ||
// this is used for clear calls | ||
const generateClearHandler = (handler) => { | ||
return (key, done) => { | ||
handler(key, () => { | ||
// ignore any errors or data the handler returns | ||
done(); | ||
}); | ||
}; | ||
}; | ||
// this handler ignores errors and data returned by the layer | ||
// this is used for clear calls | ||
const generateClearHandler = ( handler ) => { | ||
return ( key, done ) => { | ||
handler( key, () => { | ||
// ignore any errors or data the handler returns | ||
done(); | ||
} ); | ||
}; | ||
}; | ||
this._layers.forEach((layer, i) => { | ||
this._layers.forEach( ( layer, i ) => { | ||
if (!isObject(layer)) { | ||
throw new Error('layer ' + i + ' is not an object'); | ||
} | ||
if ( !__.isObject( layer ) ) { | ||
throw new Error( 'layer ' + i + ' is not an object' ); | ||
} | ||
// get function is required | ||
if (typeof layer.get !== 'function') { | ||
throw new Error('layer ' + i + ' is missing get function'); | ||
} | ||
// get function is required | ||
if ( typeof layer.get !== 'function' ) { | ||
throw new Error( 'layer ' + i + ' is missing get function' ); | ||
} | ||
// get is required on each layer | ||
this._getLayers.push(layer.get); | ||
// get is required on each layer | ||
this._getLayers.push( layer.get ); | ||
// set is not required on any layer, but it makes sense to want to populate each layers cache on a miss. | ||
// due to other logic, we set a no-op method if the layer doesn't provide one | ||
let layerSet = layer.set; | ||
if (typeof layerSet !== 'function') { | ||
layerSet = (key, data, done) => { | ||
// NO-OP | ||
done(); | ||
}; | ||
} | ||
this._setLayers.push(generateSetHandler(layerSet)); | ||
// set is not required on any layer, but it makes sense to want to populate each layers cache on a miss. | ||
// due to other logic, we set a no-op method if the layer doesn't provide one | ||
let layerSet = layer.set; | ||
if ( typeof layerSet !== 'function' ) { | ||
layerSet = ( key, data, done ) => { | ||
// NO-OP | ||
done(); | ||
}; | ||
} | ||
this._setLayers.push( generateSetHandler( layerSet ) ); | ||
// clear is not required on any layer, but also makes sense to want to | ||
// be able to propagate clears to all layers | ||
if (typeof layer.clear === 'function') { | ||
this._clearLayers.push(generateClearHandler(layer.clear)); | ||
} | ||
// clear is not required on any layer, but also makes sense to want to | ||
// be able to propagate clears to all layers | ||
if ( typeof layer.clear === 'function' ) { | ||
this._clearLayers.push( generateClearHandler( layer.clear ) ); | ||
} | ||
}); | ||
} ); | ||
} | ||
} | ||
/** | ||
* Gets the corresponding key from the first layer to have the data. | ||
* | ||
* @param { any } key Can be any object or scalar, but must be serializable as JSON. | ||
* @param { any } data Can be anything. The in-memory layer built in to ASC can store anything, including resource handles, but it is up to your layers to be able to handle storage of whatever can be passed here. | ||
* @param { ErrorDataCallback } done Will call back with no arguments, or first argument will be instance of Error if any of the layers errors out. | ||
*/ | ||
get(key, done) { | ||
/** | ||
* Gets the corresponding key from the first layer to have the data. | ||
* | ||
* @param { any } key Can be any object or scalar, but must be serializable as JSON. | ||
* @param { any } data Can be anything. The in-memory layer built in to ASC can store anything, including resource handles, but it is up to your layers to be able to handle storage of whatever can be passed here. | ||
* @param { ErrorDataCallback } done Will call back with no arguments, or first argument will be instance of Error if any of the layers errors out. | ||
*/ | ||
get( key, done ) { | ||
// only use the marshalled key for ASC callback queues | ||
// pass original key to all cache layer handlers | ||
const marshalledKey = util.marshallKey(key); | ||
// only use the marshalled key for ASC callback queues | ||
// pass original key to all cache layer handlers | ||
const marshalledKey = util.marshallKey( key ); | ||
if (!this._serviceQueues.hasOwnProperty(marshalledKey)) { | ||
this._serviceQueues[marshalledKey] = []; | ||
} | ||
if ( !this._serviceQueues.hasOwnProperty( marshalledKey ) ) { | ||
this._serviceQueues[marshalledKey] = []; | ||
} | ||
this._serviceQueues[marshalledKey].push(done); | ||
this._serviceQueues[marshalledKey].push( done ); | ||
if (this._serviceQueues[marshalledKey].length > 1) { | ||
return; | ||
} | ||
if ( this._serviceQueues[marshalledKey].length > 1 ) { | ||
return; | ||
} | ||
let returnData; | ||
let hasData = false; | ||
let returnErr = null; | ||
let currentIndex = 0; | ||
let returnData; | ||
let hasData = false; | ||
let returnErr = null; | ||
let currentIndex = 0; | ||
async.whilst( | ||
done => done(null, !hasData && returnErr === null && currentIndex < this._getLayers.length), | ||
(done) => { | ||
async.whilst( | ||
done => done( null, !hasData && returnErr === null && currentIndex < this._getLayers.length ), | ||
( done ) => { | ||
const handler = this._getLayers[currentIndex]; | ||
const handler = this._getLayers[currentIndex]; | ||
handler(key, (err, data) => { | ||
handler( key, ( err, data ) => { | ||
// this layer failed to return data, either not found or any other issue | ||
if (err instanceof Error) { | ||
// this layer failed to return data, either not found or any other issue | ||
if ( err instanceof Error ) { | ||
if (currentIndex === this._getLayers.length - 1) { | ||
// this was the last layer, take the error | ||
returnErr = err; | ||
} else { | ||
// more layers to try | ||
currentIndex++; | ||
} | ||
if ( currentIndex === this._getLayers.length - 1 ) { | ||
// this was the last layer, take the error | ||
returnErr = err; | ||
} else { | ||
// more layers to try | ||
currentIndex++; | ||
} | ||
} else if (err) { | ||
} else if ( err ) { | ||
// this layer returned an invalid value for error | ||
returnErr = new Error('layer ' + currentIndex + ' failed to return an instance of Error, returned: ' + | ||
typeof err); | ||
// this layer returned an invalid value for error | ||
returnErr = new Error( 'layer ' + currentIndex + ' failed to return an instance of Error, returned: ' + | ||
__.getType( err ) ); | ||
} else { | ||
} else { | ||
// assume this layer had data | ||
returnData = data; | ||
hasData = true; | ||
// assume this layer had data | ||
returnData = data; | ||
hasData = true; | ||
} | ||
} | ||
done(); | ||
done(); | ||
}); | ||
} ); | ||
}, | ||
() => { | ||
}, | ||
() => { | ||
const finish = () => { | ||
const finish = () => { | ||
// clear out the queue and store it in a local variable so that | ||
// the callbacks we are about to fire don't create an endless loop if | ||
// they trigger another lookup on the same key | ||
const callbacks = this._serviceQueues[marshalledKey]; | ||
delete this._serviceQueues[marshalledKey]; | ||
// clear out the queue and store it in a local variable so that | ||
// the callbacks we are about to fire don't create an endless loop if | ||
// they trigger another lookup on the same key | ||
const callbacks = this._serviceQueues[marshalledKey]; | ||
delete this._serviceQueues[marshalledKey]; | ||
// fire all callbacks synchronously, in series | ||
callbacks.forEach((callback) => { | ||
// fire all callbacks synchronously, in series | ||
callbacks.forEach( ( callback ) => { | ||
// wrap this in a try/cache in case the external code is buggy | ||
try { | ||
// wrap this in a try/cache in case the external code is buggy | ||
try { | ||
if (hasData) { | ||
callback(null, returnData); | ||
} else { | ||
callback(returnErr); | ||
} | ||
if ( hasData ) { | ||
callback( null, returnData ); | ||
} else { | ||
callback( returnErr ); | ||
} | ||
} catch (e) { | ||
// NO-OP | ||
} | ||
} catch ( e ) { | ||
// NO-OP | ||
} | ||
}); | ||
} ); | ||
}; | ||
}; | ||
// if we have data, back-populate up the layers | ||
// otherwise just start the callbacks | ||
if (hasData) { | ||
this._populateMisses(key, returnData, currentIndex, finish); | ||
} else { | ||
finish(); | ||
} | ||
// if we have data, back-populate up the layers | ||
// otherwise just start the callbacks | ||
if ( hasData ) { | ||
this._populateMisses( key, returnData, currentIndex, finish ); | ||
} else { | ||
finish(); | ||
} | ||
} | ||
); | ||
} | ||
); | ||
} | ||
} | ||
_populateMisses(key, data, index, done) { | ||
_populateMisses( key, data, index, done ) { | ||
async.timesSeries(index, (i, done) => { | ||
async.timesSeries( index, ( i, done ) => { | ||
this._setLayers[i](key, data, done); | ||
this._setLayers[i]( key, data, done ); | ||
}, (err) => { | ||
}, ( err ) => { | ||
if (err) { | ||
return done(err); | ||
} | ||
if ( err ) { | ||
return done( err ); | ||
} | ||
done(); | ||
done(); | ||
}); | ||
} ); | ||
} | ||
} | ||
/** | ||
* Sets the corresponding key to store the passed data. | ||
* | ||
* | ||
* @param { any } key Can be any object or scalar, but must be serializable as JSON. | ||
* @param { any } data Can be anything. The in-memory layer built in to ASC can store anything, including resource handles, but it is up to your layers to be able to handle storage of whatever can be passed here. | ||
* @param { ErrorCallback } done Will call back with no arguments, or first argument will be instance of Error if any of the layers errors out. | ||
*/ | ||
set(key, data, done) { | ||
/** | ||
* Sets the corresponding key to store the passed data. | ||
* | ||
* | ||
* @param { any } key Can be any object or scalar, but must be serializable as JSON. | ||
* @param { any } data Can be anything. The in-memory layer built in to ASC can store anything, including resource handles, but it is up to your layers to be able to handle storage of whatever can be passed here. | ||
* @param { ErrorCallback } done Will call back with no arguments, or first argument will be instance of Error if any of the layers errors out. | ||
*/ | ||
set( key, data, done ) { | ||
async.applyEachSeries(this._setLayers, key, data)(err => { | ||
async.applyEachSeries( this._setLayers, key, data )( err => { | ||
if (err) { | ||
return done(err); | ||
} | ||
if ( err ) { | ||
return done( err ); | ||
} | ||
done(); | ||
done(); | ||
}); | ||
} ); | ||
} | ||
} | ||
/** | ||
* Clears the corresponding key. | ||
* | ||
* @param { any } key Can be any object or scalar, but must be serializable as JSON. | ||
* @param { function({Error} err?) } done Will call back with no arguments, or first argument will be instance of Error if any of the layers errors out. | ||
*/ | ||
clear(key, done) { | ||
/** | ||
* Clears the corresponding key. | ||
* | ||
* @param { any } key Can be any object or scalar, but must be serializable as JSON. | ||
* @param { function({Error} err?) } done Will call back with no arguments, or first argument will be instance of Error if any of the layers errors out. | ||
*/ | ||
clear( key, done ) { | ||
async.applyEachSeries(this._clearLayers, key)((err) => { | ||
async.applyEachSeries( this._clearLayers, key )( ( err ) => { | ||
if (err) { | ||
return done(err); | ||
} | ||
if ( err ) { | ||
return done( err ); | ||
} | ||
done(); | ||
done(); | ||
}); | ||
} ); | ||
} | ||
} | ||
} | ||
@@ -357,0 +363,0 @@ |
'use strict'; | ||
const __ = require( 'doublescore' ); | ||
const async = require( 'async' ); | ||
const util = require( './util' ); | ||
const async = require('async'); | ||
const util = require('./util'); | ||
function hasOwn( obj, field ) { | ||
return Object.hasOwnProperty.call( obj, field ); | ||
function hasOwn(obj, field) { | ||
return Object.hasOwnProperty.call(obj, field); | ||
} | ||
@@ -13,88 +12,88 @@ | ||
constructor( params ) { | ||
constructor(params) { | ||
this._init( params ); | ||
this._setup(); | ||
this._init(params); | ||
this._setup(); | ||
} | ||
} | ||
_init( params ) { | ||
_init(params) { | ||
this._params = __( { | ||
ttl: 60000 | ||
} ).mixin( params || {} ); | ||
this._params = { | ||
ttl: params.ttl || 60000 | ||
} | ||
if ( !__.isNumber( this._params.ttl ) ) { | ||
throw new Error( 'memory.ttl must be a number' ); | ||
} | ||
if (typeof this._params.ttl !== 'number' || !Number.isFinite(this._params.ttl)) { | ||
throw new Error('memory.ttl must be a number and cannot be Infinity or NaN'); | ||
} | ||
if ( this._params.ttl < 0 ) { | ||
throw new Error( 'memory.ttl must be >= 0' ); | ||
if (this._params.ttl < 0) { | ||
throw new Error('memory.ttl must be >= 0'); | ||
} | ||
} | ||
} | ||
_setup() { | ||
_setup() { | ||
this._memoryCache = {}; | ||
this._memoryCacheTimeouts = {}; | ||
this._memoryCache = {}; | ||
this._memoryCacheTimeouts = {}; | ||
} | ||
} | ||
get(key, done) { | ||
get( key, done ) { | ||
key = util.marshallKey(key); | ||
key = util.marshallKey( key ); | ||
if (hasOwn(this._memoryCache, key)) { | ||
return done(null, this._memoryCache[key]); | ||
} | ||
if ( hasOwn( this._memoryCache, key ) ) { | ||
return done( null, this._memoryCache[key] ); | ||
done(new Error('not found')); | ||
} | ||
done( new Error( 'not found' ) ); | ||
set(key, value, done) { | ||
} | ||
key = util.marshallKey(key); | ||
set( key, value, done ) { | ||
async.waterfall([ | ||
(done) => { | ||
this._memoryClear(key, done); | ||
}, | ||
(done) => { | ||
this._memoryCache[key] = value; | ||
key = util.marshallKey( key ); | ||
this._memoryCacheTimeouts[key] = setTimeout(() => { | ||
this._memoryClear(key, () => { | ||
// NO-OP | ||
}); | ||
}, this._params.ttl); | ||
async.waterfall( [ | ||
( done ) => { | ||
this._memoryClear( key, done ); | ||
}, | ||
( done ) => { | ||
this._memoryCache[key] = value; | ||
done(); | ||
} | ||
], done); | ||
this._memoryCacheTimeouts[key] = setTimeout( () => { | ||
this._memoryClear( key, () => { | ||
// NO-OP | ||
} ); | ||
}, this._params.ttl ); | ||
} | ||
done(); | ||
} | ||
], done ); | ||
clear(key, done) { | ||
key = util.marshallKey(key); | ||
this._memoryClear(key, done); | ||
} | ||
} | ||
_memoryClear(key, done) { | ||
clear( key, done ) { | ||
key = util.marshallKey( key ); | ||
this._memoryClear( key, done ); | ||
} | ||
if (hasOwn(this._memoryCacheTimeouts, key)) { | ||
clearTimeout(this._memoryCacheTimeouts[key]); | ||
} | ||
_memoryClear( key, done ) { | ||
if ( hasOwn( this._memoryCacheTimeouts, key ) ) { | ||
clearTimeout( this._memoryCacheTimeouts[key] ); | ||
} | ||
delete this._memoryCacheTimeouts[key]; | ||
delete this._memoryCache[key]; | ||
done(); | ||
delete this._memoryCacheTimeouts[key]; | ||
delete this._memoryCache[key]; | ||
} | ||
done(); | ||
} | ||
} | ||
module.exports = Memory; |
{ | ||
"name": "asc", | ||
"description": "A middleware layer between a service and a client. The service could be an in-process library, a file, an external web service, anything. Any time the results from a resource call can be cached, you can use ASC as a proxy to that resource.", | ||
"version": "2.0.3", | ||
"version": "2.0.4", | ||
"author": "Anthony Hildoer <anthony@bluerival.com>", | ||
@@ -10,15 +10,15 @@ "repository": { | ||
}, | ||
"types": "types/index.d.ts", | ||
"scripts": { | ||
"test-watch": "./node_modules/mocha/bin/mocha -b -u bdd -w --exit test/*.test.js", | ||
"test": "./node_modules/mocha/bin/mocha -b -u bdd --exit test/*.test.js" | ||
"test-watch": "node ./node_modules/mocha/bin/mocha.js -b -u bdd -w --exit test/*.test.js", | ||
"test": "node ./node_modules/mocha/bin/mocha.js -b -u bdd --exit test/*.test.js" | ||
}, | ||
"dependencies": { | ||
"async": "3.2.4", | ||
"doublescore": "1.0.1" | ||
"async": "^3.2.5" | ||
}, | ||
"devDependencies": { | ||
"@types/mocha": "9.1.1", | ||
"eslint": "7.32.0", | ||
"glob": "8.0.3", | ||
"mocha": "9.2.2" | ||
"@types/mocha": "^10.0.7", | ||
"eslint": "^9.8.0", | ||
"glob": "^11.0.0", | ||
"mocha": "10.7.0" | ||
}, | ||
@@ -34,3 +34,12 @@ "keywords": [ | ||
}, | ||
"files": [ | ||
"index.js", | ||
"lib", | ||
"LICENSE", | ||
"package-lock.json", | ||
"package.json", | ||
"README.md", | ||
"types" | ||
], | ||
"license": "MIT" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
33984
1
7
401
+ Addedasync@3.2.6(transitive)
- Removeddoublescore@1.0.1
- Removedasync@3.2.4(transitive)
- Removeddoublescore@1.0.1(transitive)
Updatedasync@^3.2.5