Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

asc

Package Overview
Dependencies
Maintainers
0
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

asc - npm Package Compare versions

Comparing version 2.0.3 to 2.0.4

types/index.d.ts

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"
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc