New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

ladda-cache

Package Overview
Dependencies
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ladda-cache - npm Package Compare versions

Comparing version 0.0.12 to 0.1.0

.nyc_output/898d382b77850bbb541ef31e57448bea.json

814

dist/bundle.js

@@ -1,813 +0,1 @@

module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var _builder = __webpack_require__(1);
module.exports = {
build: _builder.build
};
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.build = build;
var _decorator = __webpack_require__(2);
var _datastore = __webpack_require__(5);
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } // Concepts: AbstractEntity
function build(config) {
var abstractEntities = toAbstractFormat(config);
var indexedEntities = toObject(abstractEntities.filter(function (x) {
return !x.val.viewOf;
}), {});
abstractEntities.forEach(registerViews(indexedEntities));
abstractEntities.forEach(copyInvalidateToViews(indexedEntities));
abstractEntities.forEach(setDefaults);
abstractEntities.forEach((0, _decorator.decorateApi)((0, _datastore.createDatastore)(createTtlMap(abstractEntities))));
return compile(abstractEntities, {});
}
function createTtlMap(abstractEntities) {
return abstractEntities.reduce(function (memo, value) {
memo[value.name] = value.val.ttl;
return memo;
}, {});
}
function toAbstractFormat(config) {
return Object.keys(config).map(function (name) {
return {
name: name,
val: config[name]
};
});
}
function registerViews(indexedEntities) {
return function (abstractEntity) {
if (isView(abstractEntity)) {
registerView(indexedEntities, abstractEntity);
}
};
}
function registerView(indexedEntities, abstractEntity) {
assertEntityExist(indexedEntities, abstractEntity);
addView(indexedEntities[getViewOf(abstractEntity)], abstractEntity);
}
function assertEntityExist(indexedEntities, abstractEntity) {
if (!indexedEntities[getViewOf(abstractEntity)]) {
throw new Error('Tried to register view on ' + getViewOf(abstractEntity) + ' which does not exist');
}
}
function addView(entity, abstractEntity) {
if (!entity.views) {
entity.views = [];
}
entity.views.push(getName(abstractEntity));
}
function getName(abstractEntity) {
return abstractEntity.name;
}
function getViewOf(abstractEntity) {
return abstractEntity.val.viewOf;
}
function isView(abstractEntity) {
return !!abstractEntity.val.viewOf;
}
function copyInvalidateToViews(indexedEntities) {
return function (abstractEntity) {
if (isView(abstractEntity)) {
assertEntityExist(indexedEntities, abstractEntity);
addInvalidates(abstractEntity, getInvalidates(indexedEntities[getViewOf(abstractEntity)]));
}
};
}
function setDefaults(abstractEntity) {
if (!abstractEntity.val.invalidates) {
abstractEntity.val.invalidates = [];
}
if (!abstractEntity.val.views) {
abstractEntity.val.views = [];
}
}
function addInvalidates(abstractEntity, invalidates) {
if (abstractEntity.val.invalidates) {
var _abstractEntity$val$i;
(_abstractEntity$val$i = abstractEntity.val.invalidates).push.apply(_abstractEntity$val$i, _toConsumableArray(invalidates));
} else {
abstractEntity.val.invalidates = invalidates;
}
}
function getInvalidates(entity) {
return entity.invalidates;
}
function toObject(val, obj) {
if (!Array.isArray(val)) {
return val;
}
val.forEach(function (x) {
obj[x.name] = toObject(x.val, {});
});
return obj;
}
function compile(val, obj) {
if (!Array.isArray(val)) {
return val;
}
val.forEach(function (x) {
obj[x.name] = toObject(x.val.decoratedApi, {});
});
return obj;
}
/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.decorateApi = decorateApi;
var _create = __webpack_require__(3);
var _read = __webpack_require__(8);
var _update = __webpack_require__(9);
var _delete = __webpack_require__(10);
// Concepts: AbstractEntity
function decorateApi(datastore) {
return function (abstractEntity) {
var api = getApi(abstractEntity);
var decoratedApi = decorateEveryApiFn(api, abstractEntity, datastore);
addDecoratedApi(abstractEntity, decoratedApi);
};
}
function getApi(abstractEntity) {
return abstractEntity.val.api;
}
function decorateEveryApiFn(api, abstractEntity, datastore) {
return Object.keys(api).map(function (name) {
return {
name: name,
val: decorateApiFn(abstractEntity, api[name], datastore)
};
});
}
function addDecoratedApi(abstractEntity, decoratedApi) {
abstractEntity.val.decoratedApi = decoratedApi;
}
function decorateApiFn(abstractEntity, apiFn, datastore) {
apiFn.invalidates = apiFn.invalidates || [];
switch (apiFn.operation) {
case 'CREATE':
return (0, _create.decorateCreate)(apiFn, datastore, abstractEntity);
case 'READ':
return (0, _read.decorateRead)(apiFn, datastore, abstractEntity);
case 'UPDATE':
return (0, _update.decorateUpdate)(apiFn, datastore, abstractEntity);
case 'DELETE':
return (0, _delete.decorateDelete)(apiFn, datastore, abstractEntity);
}
}
/***/ },
/* 3 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.decorateCreate = decorateCreate;
var _invalidator = __webpack_require__(4);
var _query = __webpack_require__(6);
var _datastore = __webpack_require__(5);
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function decorateCreate(apiFn, datastore, abstractEntity) {
var type = abstractEntity.name;
return function (item) {
var createPromise = apiFn(item);
var insertId = (0, _datastore.createItem)(datastore, (0, _query.createQueryFromItem)(type, item), item, createPromise);
createInViews(datastore, abstractEntity, item, insertId);
createPromise.then(function (result) {
replaceTempIdInItemAndViews(datastore, abstractEntity, insertId, result);
(0, _invalidator.invalidateEntity)(datastore, abstractEntity, 'CREATE');
(0, _invalidator.invalidateFunction)(datastore, abstractEntity, apiFn);
});
return createPromise;
};
}
function replaceTempIdInItemAndViews(datastore, abstractEntity, insertId, result) {
var types = [abstractEntity.name].concat(_toConsumableArray(abstractEntity.val.views));
types.forEach(function (type) {
(0, _datastore.replaceTempId)(datastore, (0, _query.createQuery)(type, insertId), result.id);
});
}
function createInViews(datastore, abstractEntity, item, insertId) {
var types = abstractEntity.val.views;
types.forEach(function (type) {
(0, _datastore.addItem)(datastore, (0, _query.createQuery)(type, insertId), item);
});
}
/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.invalidateEntity = invalidateEntity;
exports.invalidateFunction = invalidateFunction;
var _datastore = __webpack_require__(5);
function invalidateEntity(datastore, abstractEntity, operation) {
var invalidateOn = abstractEntity.val.invalidatesOn || ['CREATE'];
if (invalidateOn.indexOf(operation) === -1) {
return;
}
abstractEntity.val.invalidates.forEach(function (type) {
(0, _datastore.invalidate)(datastore, type);
});
}
function invalidateFunction(datastore, abstractEntity, apiFn) {
var type = abstractEntity.name;
apiFn.invalidates.forEach(function (method) {
(0, _datastore.invalidate)(datastore, type, method);
});
}
/***/ },
/* 5 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; // Concepts:
// Datastore - reference for datastore
// Item - a single entity, which contains all info to perform operations
exports.createDatastore = createDatastore;
exports.createItem = createItem;
exports.replaceTempId = replaceTempId;
exports.addItem = addItem;
exports.getItem = getItem;
exports.updateItem = updateItem;
exports.deleteItem = deleteItem;
exports.addCollection = addCollection;
exports.getCollection = getCollection;
exports.invalidate = invalidate;
exports.patchItem = patchItem;
var _query = __webpack_require__(6);
var _merger = __webpack_require__(7);
function createDatastore(ttlMap) {
return {
queryCache: {},
entityCache: {},
incrementingId: 1,
ttlMap: ttlMap,
tempToRealId: {}
};
}
function createItem(datastore, query, value, promise) {
var id = 'tmp-' + datastore.incrementingId++;
query.value = id;
addItem(datastore, query, value);
addTemporaryToRealId(datastore, id, promise);
return id;
}
function replaceTempId(datastore, query, realId) {
var item = getItemSync(datastore, query);
deleteItemSync(datastore, query);
addItem(datastore, _extends({}, query, { value: realId }), item);
}
function addTemporaryToRealId(datastore, tmpId, realId) {
datastore.tempToRealId[tmpId] = realId;
}
function replaceTempIdInQuery(datastore, query) {
if (isQueryForTempId(query) && !entityExist(datastore, query)) {
// Try to get item with id, if not exist try with id from translation table
return datastore.tempToRealId[query.value].then(function (newId) {
return (0, _query.createQuery)(query.type, newId);
});
} else {
return Promise.resolve(query);
}
}
function isQueryForTempId(query) {
return (/^tmp-/.test(query.value)
);
}
function entityExist(datastore, query) {
return !!datastore.entityCache[createKey(query)];
}
function addItem(datastore, query, value) {
datastore.entityCache[createKey(query)] = withTimestamp(value, Date.now());
}
function getItem(datastore, query) {
return replaceTempIdInQuery(datastore, query).then(function (translatedQuery) {
return getItemSync(datastore, translatedQuery);
});
}
function getItemSync(datastore, query) {
var item = datastore.entityCache[createKey(query)];
var ttl = datastore.ttlMap[query.type];
if (item && (isQueryForTempId(query) || hasExpired(item.timestamp, ttl))) {
return withoutTimestamp(item);
}
}
function hasExpired(timestamp, ttl) {
return (Date.now() - timestamp) / 1000 < ttl;
}
function updateItem(datastore, query, value) {
return replaceTempIdInQuery(datastore, query).then(function (translatedQuery) {
return datastore.entityCache[createKey(translatedQuery)] = withTimestamp(value, Date.now());
});
}
function deleteItem(datastore, query) {
return replaceTempIdInQuery(datastore, query).then(function (translatedQuery) {
deleteItemSync(datastore, translatedQuery);
});
}
function deleteItemSync(datastore, query) {
delete datastore.entityCache[createKey(query)];
}
function addCollection(datastore, query, value) {
var ids = value.map(function (x) {
return x.id;
});
value.forEach(function (x) {
var queryForItem = (0, _query.createQuery)(query.type, x.id);
addItem(datastore, queryForItem, x);
});
if (!datastore.queryCache[query.type]) {
datastore.queryCache[query.type] = {};
}
return datastore.queryCache[query.type][createKey(query)] = withTimestamp(ids, Date.now());
}
function getCollection(datastore, query) {
var collection = safeCollectionLookup(datastore, query);
var ttl = datastore.ttlMap[query.type];
if (collection && hasExpired(collection.timestamp, ttl)) {
return Promise.all(withoutTimestamp(collection).map(function (id) {
return getItemSync(datastore, (0, _query.createQuery)(query.type, id));
}).filter(function (x) {
return x !== undefined;
}));
} else {
return Promise.resolve(undefined);
}
}
function invalidate(datastore, type, method) {
if (datastore.queryCache) {
if (method) {
invalidateQueryCacheByFunction(datastore, type, method);
} else {
if (type.indexOf('(*)') !== -1) {
var allMatcher = getAllMatcher(type.substring(0, type.length - 3));
var candidates = Object.keys(datastore.entityCache || {});
var toInvalidate = candidates.filter(function (x) {
return allMatcher.test(x);
});
toInvalidate.forEach(function (key) {
delete datastore.entityCache[key];
});
datastore.queryCache[type.substring(0, type.length - 3)] = {};
}
datastore.queryCache[type] = {};
}
}
}
function getAllMatcher(type) {
return new RegExp('^' + type + '.*$');
}
function invalidateQueryCacheByFunction(datastore, type, method) {
var matcher = getMatcher(type, method);
var candidates = Object.keys(datastore.queryCache[type] || {});
var toInvalidate = candidates.filter(function (x) {
return matcher.test(x);
});
toInvalidate.forEach(function (key) {
delete datastore.queryCache[type][key];
});
}
function getMatcher(type, method) {
if (method.slice(method.length - 3, method.length) === '(*)') {
var cleanMethod = method.slice(0, method.length - 3);
return new RegExp('^' + type + '-' + cleanMethod + '-.*$');
} else {
return new RegExp('^' + type + '-' + method + '-$');
}
}
function patchItem(datastore, query, item) {
getItem(datastore, query).then(function (superItem) {
if (superItem) {
updateItem(datastore, query, (0, _merger.merge)(item, superItem));
}
});
}
function safeCollectionLookup(datastore, query) {
return (datastore.queryCache[query.type] || {})[createKey(query)];
}
function createKey(query) {
return Object.keys(query).map(function (x) {
if (query[x] && _typeof(query[x]) === 'object') {
return createKey(query[x]);
} else {
return query[x];
}
}).join('-');
}
function withTimestamp(value, timestamp) {
return {
timestamp: timestamp,
value: value
};
}
function withoutTimestamp(valueWithTimestamp) {
return valueWithTimestamp.value;
}
/***/ },
/* 6 */
/***/ function(module, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports.createQuery = createQuery;
exports.createQueryFromItem = createQueryFromItem;
function createQuery(type, value, moreIdentifiers) {
if (moreIdentifiers) {
return _extends({
type: type
}, moreIdentifiers, {
value: value
});
} else {
return {
type: type,
value: value
};
}
}
function createQueryFromItem(type, item) {
return createQuery(type, item.id);
}
/***/ },
/* 7 */
/***/ function(module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports.merge = merge;
function merge(source, destination) {
var result = _extends({}, destination);
var keysForNonObjects = getNonObjectKeys(source);
keysForNonObjects.forEach(function (key) {
if (destination[key] !== undefined) {
result[key] = source[key];
}
});
var keysForObjects = getObjectKeys(source);
keysForObjects.forEach(function (key) {
if (destination[key] !== undefined) {
result[key] = merge(source[key], destination[key]);
}
});
return result;
}
function getNonObjectKeys(object) {
return Object.keys(object).filter(function (key) {
return object[key] === null || _typeof(object[key]) !== 'object' || Array.isArray(object[key]);
});
}
function getObjectKeys(object) {
return Object.keys(object).filter(function (key) {
return object[key] !== null && !Array.isArray(object[key]) && _typeof(object[key]) === 'object';
});
}
/***/ },
/* 8 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.decorateRead = decorateRead;
var _query = __webpack_require__(6);
var _invalidator = __webpack_require__(4);
var _datastore = __webpack_require__(5);
function decorateRead(apiFn, datastore, abstractEntity) {
return function (query) {
if (apiFn.alwaysGetFreshData === true) {
return executeApiFnAndCache(apiFn, datastore, abstractEntity, query);
}
var fromCache = getFromCache(apiFn, datastore, abstractEntity.name, query);
return fromCache.then(function (itemFromCache) {
if (itemFromCache) {
return itemFromCache;
} else {
return executeApiFnAndCache(apiFn, datastore, abstractEntity, query);
}
});
};
}
function executeApiFnAndCache(apiFn, datastore, abstractEntity, query) {
var result = apiFn(query);
result.then(addToCache(apiFn, datastore, abstractEntity, query));
return result;
}
function getFromCache(apiFn, datastore, type, query) {
if (apiFn.plural) {
return getFromQueryCache(datastore, type, query, apiFn.name);
} else {
return getFromEntityCache(datastore, type, query);
}
}
function getFromQueryCache(datastore, type, query, apiFnName) {
return (0, _datastore.getCollection)(datastore, createQueryForCollection(type, query, apiFnName));
}
function getFromEntityCache(datastore, type, id) {
return (0, _datastore.getItem)(datastore, (0, _query.createQuery)(type, id));
}
function addToCache(apiFn, datastore, abstractEntity, query) {
return function (data) {
if (apiFn.plural) {
var collectionQuery = createQueryForCollection(abstractEntity.name, query, apiFn.name);
(0, _datastore.addCollection)(datastore, collectionQuery, data);
} else {
(0, _datastore.addItem)(datastore, (0, _query.createQuery)(abstractEntity.name, query), data);
}
(0, _invalidator.invalidateEntity)(datastore, abstractEntity, 'READ');
(0, _invalidator.invalidateFunction)(datastore, abstractEntity, apiFn);
};
}
function createQueryForCollection(type, query, apiFnName) {
return (0, _query.createQuery)(type, query, { name: apiFnName });
}
/***/ },
/* 9 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.decorateUpdate = decorateUpdate;
var _invalidator = __webpack_require__(4);
var _query = __webpack_require__(6);
var _datastore = __webpack_require__(5);
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function decorateUpdate(apiFn, datastore, abstractEntity) {
return function (item) {
updateItemAndViews(datastore, abstractEntity, item);
updateSuperEntity(datastore, abstractEntity, item);
var result = apiFn(item);
result.then(function () {
(0, _invalidator.invalidateEntity)(datastore, abstractEntity, 'UPDATE');
(0, _invalidator.invalidateFunction)(datastore, abstractEntity, apiFn);
});
return result;
};
}
function updateItemAndViews(datastore, abstractEntity, item) {
var types = [abstractEntity.name].concat(_toConsumableArray(abstractEntity.val.views));
types.forEach(function (type) {
(0, _datastore.updateItem)(datastore, (0, _query.createQueryFromItem)(type, item), item);
});
}
function updateSuperEntity(datastore, abstractEntity, item) {
var superEntity = abstractEntity.val.viewOf;
if (!superEntity) {
return;
}
(0, _datastore.patchItem)(datastore, (0, _query.createQueryFromItem)(superEntity, item), item);
}
/***/ },
/* 10 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.decorateDelete = decorateDelete;
var _invalidator = __webpack_require__(4);
var _query = __webpack_require__(6);
var _datastore = __webpack_require__(5);
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function decorateDelete(apiFn, datastore, abstractEntity) {
return function (id) {
deleteFromEntityAndViews(datastore, abstractEntity, id);
var result = apiFn(id);
result.then(function () {
(0, _invalidator.invalidateEntity)(datastore, abstractEntity, 'DELETE');
(0, _invalidator.invalidateFunction)(datastore, abstractEntity, apiFn);
});
return result;
};
}
function deleteFromEntityAndViews(datastore, abstractEntity, id) {
var types = [abstractEntity.val.viewOf, abstractEntity.name].concat(_toConsumableArray(abstractEntity.val.views)).filter(function (x) {
return x;
});
types.forEach(function (type) {
(0, _datastore.deleteItem)(datastore, (0, _query.createQuery)(type, id));
});
}
/***/ }
/******/ ]);
module.exports=function(r){function t(e){if(n[e])return n[e].exports;var o=n[e]={exports:{},id:e,loaded:!1};return r[e].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=r,t.c=n,t.p="",t(0)}([function(r,t,n){"use strict";var e=n(4);r.exports={build:e.build}},function(r,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function r(r,t){var n=[],e=!0,o=!1,u=void 0;try{for(var i,a=r[Symbol.iterator]();!(e=(i=a.next()).done)&&(n.push(i.value),!t||n.length!==t);e=!0);}catch(c){o=!0,u=c}finally{try{!e&&a["return"]&&a["return"]()}finally{if(o)throw u}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return r(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),e=(t.debug=function(r){return console.log(r),r},t.identity=function(r){return r}),o=t.curry=function O(r){return function(){for(var t=arguments.length,n=Array(t),e=0;t>e;e++)n[e]=arguments[e];var o=r.length;return n.length<o?O(r.bind.apply(r,[null].concat(n))):r.apply(void 0,n)}},u=t.passThrough=o(function(r,t){return r(t),t}),i=(t.startsWith=o(function(r,t){return 0===t.indexOf(r)}),t.join=o(function(r,t,n){return t+r+n}),t.on=o(function(r,t,n,e){return r(t(e),n(e))}),t.isEqual=o(function(r,t){return r===t}),t.on2=o(function(r,t,n,e,o){return r(t(e),n(o))})),a=(t.init=function(r){return r.slice(0,r.length-1)},t.tail=function(r){return r.slice(1,r.length)}),c=t.last=function(r){return r[r.length-1]},f=(t.head=function(r){return r[0]},t.map=o(function(r,t){return t.map(r)})),l=t.map_=o(function(r,t){f(r,t)}),s=t.reverse=function(r){return r.slice().reverse()},p=t.reduce=o(function(r,t,n){return l(function(n){t=r(t,n)},n),t}),v=(t.compose=function(){for(var r=arguments.length,t=Array(r),n=0;r>n;n++)t[n]=arguments[n];return function(){return p(function(r,t){return t(r)},c(t).apply(void 0,arguments),a(s(t)))}},t.prop=o(function(r,t){return t[r]})),y=t.zip=function(r,t){for(var n=Math.min(r.length,t.length),e=[],o=0;n>o;o++)e.push([r[o],t[o]]);return e},d=t.flip=function(r){return o(function(t,n){return r(n,t)})},h=t.toPairs=function(r){var t=Object.keys(r),n=d(v)(r);return y(t,f(n,t))},m=(t.fromPairs=function(r){var t=function(r,t){var e=n(t,2),o=e[0],i=e[1];return u(function(){return r[o]=i},r)};return p(t,{},r)},t.mapObject=i(f,e,h),o(function(r,t,n){return r[t]=n,r})),b=(t.mapValues=o(function(r,t){var n=Object.keys(t);return p(function(n,e){return n[e]=r(t[e]),n},{},n)}),t.toObject=o(function(r,t){return p(function(t,n){return m(t,r(n),n)},{},t)}),o(function(r,t,n){return r(n)&&t.push(n),t}));t.filter=o(function(r,t){return p(b(r),[],t)})},function(r,t,n){"use strict";function e(r){if(Array.isArray(r)){for(var t=0,n=Array(r.length);t<r.length;t++)n[t]=r[t];return n}return Array.from(r)}Object.defineProperty(t,"__esModule",{value:!0}),t.createEntityStore=t.contains=t.get=t.put=t.remove=void 0;var o=function(){function r(r,t){var n=[],e=!0,o=!1,u=void 0;try{for(var i,a=r[Symbol.iterator]();!(e=(i=a.next()).done)&&(n.push(i.value),!t||n.length!==t);e=!0);}catch(c){o=!0,u=c}finally{try{!e&&a["return"]&&a["return"]()}finally{if(o)throw u}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return r(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),u=n(11),i=n(1),a=function(r){return{value:r,timestamp:Date.now()}},c=function(r,t){var n=o(r,2),e=(n[0],n[1]);return e[t]},f=function(r,t,n){var e=o(r,2),u=(e[0],e[1]);return u[t]=a(n)},l=(0,i.curry)(function(r,t){var n=o(r,2),e=(n[0],n[1]);return delete e[t]}),s=function(r){return r.viewOf||r.name},p=function(r,t){var n=o(r,2),u=n[0],a=n[1],c=s(t),f=[].concat(e(u[c]));(0,i.map_)(l([u,a]),f)},v=function(r,t){return s(r)+t.id},y=function(r,t){return r.name+t.id},d=function(r){return!!r.viewOf},h=(t.remove=function(r,t,n){l(r,v(t,{id:n})),p(r,t)},(0,i.curry)(function(r,t,n,e,o){return d(e)?r(n,e,o):t(n,e,o)})),m=function(r,t,n){return!!c(r,v(t,n))},b=function(r,t,n){if(!n.id)throw new Error("Value is missing id, tried to add to entity "+t.name);var e=v(t,n);return f(r,e,n),n},O=function(r,t,n){if(!n.id)throw new Error("Value is missing id, tried to add to view "+t.name);if(m(r,t,n)){var e=c(r,v(t,n)).value;b(r,t,(0,u.merge)(n,e)),p(r,t)}else{var o=y(t,n);f(r,o,n)}return n},g=(t.put=h(O,b),function(r,t,n){var e=v(t,{id:n});return c(r,e)}),j=function(r,t,n){var e=c(r,v(t,{id:n})),o=c(r,y(t,{id:n})),u=o&&!e;return u?o:e},A=(t.get=h(j,g),t.contains=function(r,t,n){return!!h(j,g)(r,t,n)},function(r,t){var n=o(r,2),e=n[0],u=n[1];return e[t.viewOf]||(e[t.viewOf]=[]),e[t.viewOf].push(t.name),[e,u]}),w=function(r,t){var n=o(r,2),e=n[0],u=n[1];return e[t.name]||(e[t.name]=[]),[e,u]},_=function(r,t){return d(t)?A(r,t):w(r,t)};t.createEntityStore=function(r){return(0,i.reduce)(_,[{},{}],r)}},function(r,t,n){"use strict";function e(r){if(Array.isArray(r)){for(var t=0,n=Array(r.length);t<r.length;t++)n[t]=r[t];return n}return Array.from(r)}Object.defineProperty(t,"__esModule",{value:!0}),t.createQueryCache=t.invalidate=t.get=t.contains=t.getValue=t.put=void 0;var o=Object.assign||function(r){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var e in n)Object.prototype.hasOwnProperty.call(n,e)&&(r[e]=n[e])}return r},u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(r){return typeof r}:function(r){return r&&"function"==typeof Symbol&&r.constructor===Symbol?"symbol":typeof r},i=n(2),a=n(1),c=function j(r){return Object.keys(r).map(function(t){return r[t]&&"object"===u(r[t])?j(r[t]):r[t]}).join("-")},f=function(r){var t=function(r){return r&&"object"===("undefined"==typeof r?"undefined":u(r))?c(r):r};return(0,a.map)(t,r)},l=(0,a.on2)((0,a.reduce)((0,a.join)("-")),(0,a.prop)("name"),f),s=function(r){return{value:r,timestamp:Date.now()}},p=(0,a.prop)("value"),v=function(r,t){return!!r.cache[t]},y=function(r,t,n){var e=p(r.cache[n]),u=Array.isArray(e)?(0,a.map)((0,i.get)(r.entityStore,t),e):(0,i.get)(r.entityStore,t,e);return o({},r.cache[n],{value:u})},d=(t.put=(0,a.curry)(function(r,t,n,o,u){var c=l(t,[n.name].concat(e((0,a.filter)(a.identity,o))));return Array.isArray(u)?r.cache[c]=s((0,a.map)((0,a.prop)("id"),u)):r.cache[c]=s((0,a.prop)("id",u)),(0,a.map_)((0,i.put)(r.entityStore,t),Array.isArray(u)?u:[u]),u}),t.getValue=function(r){return Array.isArray(r)?(0,a.map)(p,r):p(r)},t.contains=function(r,t,n,o){var u=l(t,[n.name].concat(e((0,a.filter)(a.identity,o))));return v(r,u)},t.get=function(r,t,n,o){var u=l(t,[n.name].concat(e((0,a.filter)(a.identity,o))));if(!v(r,u))throw new Error("Tried to access "+t.name+" with key "+u+" which doesn't exist.\n Do a contains check first!");return y(r,t,u)},function(r){return r.invalidatesOn||["CREATE","UPDATE","DELETE"]}),h=function(r,t){var n=d(r);return n&&n.indexOf(t)>-1},m=(0,a.curry)(function(r,t){var n=Object.keys(r.cache),e=function(n){(0,a.startsWith)(t+"-",n)&&delete r.cache[n]};(0,a.map_)(e,n)}),b=function(r){return r.invalidates||[]},O=function(r,t,n){h(t,n.operation)&&(0,a.map_)(m(r),b(t))},g=function(r,t,n){var e=function(r){return t.name+"-"+r},o=(0,a.compose)(m(r),e);(0,a.map_)(o,b(n))};t.invalidate=function(r,t,n){O(r,t,n),g(r,t,n)},t.createQueryCache=function(r){return{entityStore:r,cache:{}}}},function(r,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.build=void 0;var e=Object.assign||function(r){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var e in n)Object.prototype.hasOwnProperty.call(n,e)&&(r[e]=n[e])}return r},o=function(){function r(r,t){var n=[],e=!0,o=!1,u=void 0;try{for(var i,a=r[Symbol.iterator]();!(e=(i=a.next()).done)&&(n.push(i.value),!t||n.length!==t);e=!0);}catch(c){o=!0,u=c}finally{try{!e&&a["return"]&&a["return"]()}finally{if(o)throw u}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return r(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),u=n(1),i=n(2),a=n(3),c=n(7),f=function(r){var t=o(r,2),n=t[0],u=t[1];return e({name:n},u)},l=(0,u.compose)((0,u.mapValues)((0,u.prop)("api")),(0,u.toObject)((0,u.prop)("name")));t.build=function(r){var t=(0,u.mapObject)(f,r),n=(0,i.createEntityStore)(t),e=(0,a.createQueryCache)(n),o=(0,u.compose)(l,(0,u.map)((0,c.decorate)(n,e)));return o(t)}},function(r,t,n){"use strict";function e(r,t,n,e){return function(){return e.apply(void 0,arguments).then((0,i.passThrough)((0,o.put)(r,n))).then((0,i.passThrough)(function(){return(0,u.invalidate)(t,n,e)}))}}Object.defineProperty(t,"__esModule",{value:!0}),t.decorateCreate=e;var o=n(2),u=n(3),i=n(1)},function(r,t,n){"use strict";function e(r,t,n,e){return function(){for(var a=arguments.length,c=Array(a),f=0;a>f;f++)c[f]=arguments[f];return(0,o.remove)(r,n,c.join("")),e.apply(void 0,c).then((0,i.passThrough)(function(){return(0,u.invalidate)(t,n,e)}))}}Object.defineProperty(t,"__esModule",{value:!0}),t.decorateDelete=e;var o=n(2),u=n(3),i=n(1)},function(r,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decorate=void 0;var e=Object.assign||function(r){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var e in n)Object.prototype.hasOwnProperty.call(n,e)&&(r[e]=n[e])}return r},o=n(1),u=n(5),i=n(9),a=n(10),c=n(6),f=n(8),l=(0,o.curry)(function(r,t,n,e){var o={CREATE:u.decorateCreate,READ:i.decorateRead,UPDATE:a.decorateUpdate,DELETE:c.decorateDelete,NO_OPERATION:f.decorateNoOperation}[e.operation||"NO_OPERATION"];return o(r,t,n,e)});t.decorate=(0,o.curry)(function(r,t,n){var u=(0,o.mapValues)(l(r,t,n),n.api);return e({},n,{api:u})})},function(r,t,n){"use strict";function e(r,t,n,e){var i=e.bind(null);return i.operation="NO_OPERATION",function(){return e.apply(void 0,arguments).then((0,u.passThrough)(function(){return(0,o.invalidate)(t,n,i)}))}}Object.defineProperty(t,"__esModule",{value:!0}),t.decorateNoOperation=e;var o=n(3),u=n(1)},function(r,t,n){"use strict";function e(r,t,n,e){return e.byId?f(r,n,e):l(r,t,n,e)}Object.defineProperty(t,"__esModule",{value:!0}),t.decorateRead=e;var o=n(2),u=n(3),i=n(1),a=function(r){return 1e3*(r.ttl||0)},c=function(r,t){return Date.now()-t>a(r)},f=function(r,t,n){return function(e){if((0,o.contains)(r,t,e)&&!n.alwaysGetFreshData){var u=(0,o.get)(r,t,e);if(!c(t,u.timestamp))return Promise.resolve(u.value)}return n(e).then((0,i.passThrough)((0,o.put)(r,t)))}},l=function(r,t,n,e){return function(){for(var r=arguments.length,o=Array(r),a=0;r>a;a++)o[a]=arguments[a];if((0,u.contains)(t,n,e,o)&&!e.alwaysGetFreshData){var f=(0,u.get)(t,n,e,o);if(!c(n,f.timestamp))return Promise.resolve((0,u.getValue)(f.value))}return e.apply(void 0,o).then((0,i.passThrough)((0,u.put)(t,n,e,o)))}}},function(r,t,n){"use strict";function e(r,t,n,e){return function(a){for(var c=arguments.length,f=Array(c>1?c-1:0),l=1;c>l;l++)f[l-1]=arguments[l];return(0,o.put)(r,n,a),e.apply(void 0,[a].concat(f)).then((0,i.passThrough)(function(){return(0,u.invalidate)(t,n,e)}))}}Object.defineProperty(t,"__esModule",{value:!0}),t.decorateUpdate=e;var o=n(2),u=n(3),i=n(1)},function(r,t){"use strict";function n(r,t){var u=i({},t),a=e(r);a.forEach(function(n){void 0!==t[n]&&(u[n]=r[n])});var c=o(r);return c.forEach(function(e){void 0!==t[e]&&(u[e]=n(r[e],t[e]))}),u}function e(r){return Object.keys(r).filter(function(t){return null===r[t]||"object"!==u(r[t])||Array.isArray(r[t])})}function o(r){return Object.keys(r).filter(function(t){return null!==r[t]&&!Array.isArray(r[t])&&"object"===u(r[t])})}Object.defineProperty(t,"__esModule",{value:!0});var u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(r){return typeof r}:function(r){return r&&"function"==typeof Symbol&&r.constructor===Symbol?"symbol":typeof r},i=Object.assign||function(r){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var e in n)Object.prototype.hasOwnProperty.call(n,e)&&(r[e]=n[e])}return r};t.merge=n}]);
{
"name": "ladda-cache",
"version": "0.0.12",
"description": "Data fetching layaer with support for caching",
"version": "0.1.0",
"description": "Data fetching layer with support for caching",
"main": "dist/bundle.js",
"dependencies": {
},
"dependencies": {},
"devDependencies": {
"babel-core": "^6.9.0",
"babel-loader": "^6.2.4",
"babel-plugin-istanbul": "^4.0.0",
"babel-preset-es2015": "^6.9.0",

@@ -16,9 +16,16 @@ "babel-preset-stage-1": "^6.5.0",

"chai": "^3.5.0",
"mocha": "^2.5.3"
"mocha": "^2.5.3",
"sinon": "^1.17.7"
},
"scripts": {
"test": "./node_modules/.bin/mocha --compilers js:babel-register --reporter spec src/**/*spec.js --require mocha.config"
"test": "env NODE_PATH=$NODE_PATH:$PWD/src ./node_modules/.bin/mocha --compilers js:babel-register --reporter spec src/*.spec.js src/**/*.spec.js --require mocha.config",
"coverage": "env NODE_PATH=$NODE_PATH:$PWD/src nyc -x '**/*.spec.js' -x '**/*.config.js' --reporter=lcov --reporter=text mocha --compilers js:babel-register --reporter spec src/*.spec.js src/**/*.spec.js --require mocha.config"
},
"author": "Peter Crona <petercrona89@gmail.com> (http://www.icecoldcode.com)",
"license": "MIT"
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/petercrona/ladda.git"
},
"homepage": "https://github.com/petercrona/ladda"
}
# Ladda
JavaScript data fetching layer with caching.
Ladda is a tool that moves caching logic, in particular invalidation logic, out from our application code. It allows us to easily model what should be invalidated when an entity is created, updated or deleted. For example, when a user is deleted you might want to invalidate blog posts, since you know that deleting a user also delets all blog posts by the user. Rather than tainting application code with this we can observe that a user was deleted and do what is necessary.
# Background
When building a SPA, for example with React or Angular, you eventually need to have some kind of client side caching. You want stuff to be instant, but as the number of users increase the performance decline and your application's complexity increase because of all ad-hoc caching you start to add here and there. There are plenty of solutions out there, but typically they are very ambitious and pollute your application code with complexity. Ladda tries to keep things simple by moving data fetching, and especially caching, into a separate layer. **The goal is that you fetch your data everytime you want it**. Say that you have a user profile page which contains, really expensive to fetch, messages about the user. By far more than I would have liked to, I have seen "if (inCache(...)) { ... } else { ... }". Gone are those sights. With Ladda you fetch your data everytime you want it. Either it comes from the cache or from a HTTP request. You don't have to care, you can just sit back and see the server load decline as well as your app magically become faster.
# When to Use Ladda
Ladda is not meant to cover all cases. This is very intentional. Bad libraries are often the result of trying to do too much. You should only use Ladda when you have or intend to follow REST to a high extent. This means:
* Define an entity E such that E.id uniquely identifies the entity.
* Define create, read, update and delete such that:
* **create: { E \ {id} }** where E \ {id} means E without id.
* **update: { E }**
* **delete: { id }**
* **read: { id }** where the backend responds with E
* **readMultiple: { … }** where the backend responds with [E] and “…” is an unique query.
Ladda can be used in more cases. It is very flexible and often allows you to work around issues. But you need to think about how you are using it then. If you want to do some custom solution, where E don’t have an id or call it something else, then you need to use something else or be creative.
# Get Started

@@ -17,4 +27,2 @@ To use Ladda you need to configure it and export a API built from the configuration. You might create a file "api/index.js":

ttl: 300,
invalidates: ['projects', 'projectPreview'],
invalidatesOn: ['CREATE'],
api: project

@@ -31,4 +39,2 @@ }

createProject.operation = 'CREATE';
createProject.plural = false;
createProject.invalidates = ['getProjects(*)'];
export function createProject(project) {

@@ -45,19 +51,135 @@ return post(resource, { postData: project });

# Api Function Annotations
As you can see above we are adding information directly on the function object. Functions can be configured using:
# Main Concepts
**Type**
* alwaysGetFreshData : <default: false> : Always fetch data and save in cache
* plural : <required> : Used when operation is set to READ. Informs Ladda that a list of multiple entities is expected.
* invalidates : <default: []> : Invalidates the query cache for the specified api function in the same type. If suffixed with (*) query cache regardless of query will be cleared. Otherwise only api function called without parameters.
* operation : <required> : CREATE | READ | UPDATE | DELETE - necessary for Ladda to handle caching correclty.
For example “User” can be a type. API-methods are associated with a type. So for example a method `deleteById(id)` will automatically delete the entry from the cache for “User”. You can think of a type as defining a namespace for the cache.
# Entity Config
For Ladda to work optimally you need to specify the relationship between entities and other caching settings.
To define the type user:
```
User: {
api: userApi
}
```
**Item**
* viewOf : null : Specifies that the current entity is a view of another entity. The super entity will influence the cache of the view and vice versa. Eg. if a UserPreview is a view of User, then updating a user's name calling User.updateName({ id, name }) will update UserPreview.name and vice versa.
* ttl : 300 : How long the cache is valid in seconds. After the number of seconds specified Ladda will pretend the cached entity don't exist.
* invalidates : [] : Other entities to invalidate on operations specified in "invalidatesOn"
* invalidatesOn : [CREATE] : Operations to invalidate on, where operations can be CREATE, READ, UPDATE, DELETE.
An instance of a type. For example userA and userB are items of type User. Could look like `{ id: “randomId”, name: “Kalle” }`. Always have “id” specified except for when you create a new one.
**Operation**
Ladda follows the CRUD-model. Operations are:
* CREATE: Create a new item, for example a new User, given an item without id specified.
* READ: Get one or multiple items given a query or id.
* UPDATE: Update an item given an item with id specified.
* DELETE: Remove an item given a id.
**ID**
Ladda relies on IDs being available. These are assumed to uniquely identify an entity of a certain type. For example “user.id”, where user is an entity in the type User, is assumed to uniquely identify a specific user. The main assumptions, per operations, are:
*CREATE*:
A function declared with “operation = CREATE” is expected to as its only parameter get an item without the “id” being set. For example:
```
{ name: “Peter”, from: “Sweden”, livingIn: “Germany” }
```
The server is required to respond with `{ id: <uniqueIdForItem> }`. The response can contain more data, but only the id will be used. Note the assumption that the server will not manipulate the entity saved.
*READ - Singular:*
An id is expected to be provided as the only argument. The response from the server is expected to be an item with “id” set.
*READ - Plural:*
An query, which is just an object, is expected to be provided as the only argument. The response from the server is expected to be a list of item. Each item must have an ID specified. Eg. if `[userA, userB]` is returned, userA.id and userB.id are required to be set.
**Singular**
This is defined by setting for example `getUserById.plural = false;`. This is only important for READ. See example above under “READ - Singular”.
**Plural**
This is defined by setting for example `getUserById.plural = true;`. This is only important for READ. See example above under “READ - Plural”.
**Query**
This is only important for “READ - Plural”. Consider an endpoint that gives you all the users born in 1989 and that have names starting with A. A query might look like: `{ nameStartsWith: “A”, born: 1989 }`.
**API**
Every type is associated with an API, which is simply an object with functions. For instance `{ getById: fetchFromDBFunction }`. Each function in the API needs to be decorated with at least “operation”. For example:
```
getById.operation = “READ”;
function getById(id) { return fetchFromDBFunction(id); }
```
It has to return a Promise. As mentioned about, depending on the operation certain requirements are made:
CREATE: An object with id set is returned: { id }
READ - Singular: Single item is returned
READ - Plural: A list of items
UPDATE: No requirements
DELETE: No requirements
# Type Configuration
Example:
```
projects: {
ttl: 300,
invalidates: ['projects', 'projectPreview'],
invalidatesOn: ['CREATE'],
api: project
}
```
**viewOf**
Specifies that the current type is a view of another entity. The super type will influence the cache of the view and vice versa. Eg. if a UserPreview is a view of User, then updating a user's name calling `User.updateName({ id, name })` will update UserPreview.name and vice versa. Default is no super type.
**ttl**
How long the cache is valid in seconds. After the number of seconds specified Ladda will pretend the cached entity don't exist. Default is no TTL (meaning no caching at all).
**invalidates**
Other entities to invalidate on operations specified in "invalidatesOn". Default is none.
**invalidatesOn**
Operations to invalidate on, where operations can be CREATE, READ, UPDATE, DELETE. Default is CREATE.
# API Function Configuration
Example:
```
getAll.operation = 'READ';
getAll.plural = true;
export function getAll(query) {
return get('/api/v2/downloadable-file', { getData: query });
}
poll.operation = 'READ';
poll.plural = true;
poll.alwaysGetFreshData = true;
poll.invalidates = ['getAll(*)'];
export function poll(query) {
return get('/api/v2/downloadable-file', { getData: query });
}
```
**alwaysGetFreshData**
Always fetch data (even if it exists in the cache) and save in cache. Default is false.
**plural**
Used when operation is set to READ. Informs Ladda that a list of multiple entities is expected. Default is false.
**invalidates**
Invalidates the query cache for the specified api function in the same type. If suffixed with (*) all caching for the specified api function will be cleared (regardless of which arguments it was called with). Otherwise only api function called without parameters. Default is none.
**operation**
CREATE | READ | UPDATE | DELETE - necessary for Ladda to handle caching correclty. Always has to be specified.
# Try it out
Do a "npm install ladda-cache" in your project. Stay tuned for an example project.

@@ -1,127 +0,23 @@

// Concepts: AbstractEntity
import {mapObject, mapValues, compose, map, toObject, prop} from './fp';
import {createEntityStore} from './entity-store';
import {createQueryCache} from './query-cache';
import {decorate} from './decorator';
import { decorateApi } from './decorator';
import { createDatastore } from './datastore';
// [[EntityName, EntityConfig]] -> Entity
const toEntity = ([name, c]) => ({
name,
...c
});
export function build(config) {
const abstractEntities = toAbstractFormat(config);
const indexedEntities = toObject(abstractEntities.filter((x) => !x.val.viewOf), {});
abstractEntities.forEach(registerViews(indexedEntities));
abstractEntities.forEach(copyInvalidateToViews(indexedEntities));
abstractEntities.forEach(setDefaults);
abstractEntities.forEach(decorateApi(createDatastore(createTtlMap(abstractEntities))));
// [Entity] -> Api
const toApi = compose(mapValues(prop('api')), toObject(prop('name')));
return compile(abstractEntities, {});
}
// Config -> Api
export const build = (c) => {
const entities = mapObject(toEntity, c);
const entityStore = createEntityStore(entities);
const queryCache = createQueryCache(entityStore);
const createApi = compose(toApi, map(decorate(entityStore, queryCache)));
function createTtlMap(abstractEntities) {
return abstractEntities.reduce((memo, value) => {
memo[value.name] = value.val.ttl;
return memo;
}, {});
}
function toAbstractFormat(config) {
return Object.keys(config)
.map((name) => ({
name,
val: config[name]
}));
}
function registerViews(indexedEntities) {
return abstractEntity => {
if (isView(abstractEntity)) {
registerView(indexedEntities, abstractEntity);
}
};
}
function registerView(indexedEntities, abstractEntity) {
assertEntityExist(indexedEntities, abstractEntity);
addView(indexedEntities[getViewOf(abstractEntity)],
abstractEntity);
}
function assertEntityExist(indexedEntities, abstractEntity) {
if (!indexedEntities[getViewOf(abstractEntity)]) {
throw new Error('Tried to register view on ' +
getViewOf(abstractEntity) +
' which does not exist');
}
}
function addView(entity, abstractEntity) {
if (!entity.views) {
entity.views = [];
}
entity.views.push(getName(abstractEntity));
}
function getName(abstractEntity) {
return abstractEntity.name;
}
function getViewOf(abstractEntity) {
return abstractEntity.val.viewOf;
}
function isView(abstractEntity) {
return !!abstractEntity.val.viewOf;
}
function copyInvalidateToViews(indexedEntities) {
return (abstractEntity) => {
if (isView(abstractEntity)) {
assertEntityExist(indexedEntities, abstractEntity);
addInvalidates(abstractEntity,
getInvalidates(indexedEntities[getViewOf(abstractEntity)]));
}
};
}
function setDefaults(abstractEntity) {
if (!abstractEntity.val.invalidates) {
abstractEntity.val.invalidates = [];
}
if (!abstractEntity.val.views) {
abstractEntity.val.views = [];
}
}
function addInvalidates(abstractEntity, invalidates) {
if (abstractEntity.val.invalidates) {
abstractEntity.val.invalidates.push(...invalidates);
} else {
abstractEntity.val.invalidates = invalidates;
}
}
function getInvalidates(entity) {
return entity.invalidates;
}
function toObject(val, obj) {
if (!Array.isArray(val)) {
return val;
}
val.forEach((x) => {
obj[x.name] = toObject(x.val, {});
});
return obj;
}
function compile(val, obj) {
if (!Array.isArray(val)) {
return val;
}
val.forEach((x) => {
obj[x.name] = toObject(x.val.decoratedApi, {});
});
return obj;
}
return createApi(entities);
};

@@ -1,42 +0,11 @@

import { invalidateEntity, invalidateFunction } from 'invalidator';
import { createQuery, createQueryFromItem } from 'query';
import {
addItem,
createItem,
replaceTempId
} from 'datastore';
import {put} from 'entity-store';
import {invalidate} from 'query-cache';
import {passThrough} from 'fp';
export function decorateCreate(apiFn, datastore, abstractEntity) {
const type = abstractEntity.name;
return (item) => {
const createPromise = apiFn(item);
const insertId = createItem(datastore,
createQueryFromItem(type, item),
item,
createPromise);
createInViews(datastore, abstractEntity, item, insertId);
createPromise.then((result) => {
replaceTempIdInItemAndViews(datastore, abstractEntity, insertId, result);
invalidateEntity(datastore, abstractEntity, 'CREATE');
invalidateFunction(datastore, abstractEntity, apiFn);
});
return createPromise;
export function decorateCreate(es, qc, e, aFn) {
return (...args) => {
return aFn(...args)
.then(passThrough(put(es, e)))
.then(passThrough(() => invalidate(qc, e, aFn)));
};
}
function replaceTempIdInItemAndViews(datastore, abstractEntity, insertId, result) {
const types = [abstractEntity.name, ...abstractEntity.val.views];
types.forEach(type => {
replaceTempId(datastore, createQuery(type, insertId), result.id);
});
}
function createInViews(datastore, abstractEntity, item, insertId) {
const types = abstractEntity.val.views;
types.forEach(type => {
addItem(datastore, createQuery(type, insertId), item);
});
}

@@ -1,30 +0,11 @@

import { invalidateEntity, invalidateFunction } from 'invalidator';
import { createQuery } from 'query';
import {
deleteItem
} from 'datastore';
import {remove} from 'entity-store';
import {invalidate} from 'query-cache';
import {passThrough} from 'fp';
export function decorateDelete(apiFn, datastore, abstractEntity) {
return (id) => {
deleteFromEntityAndViews(datastore, abstractEntity, id);
const result = apiFn(id);
result.then(() => {
invalidateEntity(datastore, abstractEntity, 'DELETE');
invalidateFunction(datastore, abstractEntity, apiFn);
});
return result;
export function decorateDelete(es, qc, e, aFn) {
return (...args) => {
remove(es, e, args.join(''));
return aFn(...args)
.then(passThrough(() => invalidate(qc, e, aFn)));
};
}
function deleteFromEntityAndViews(datastore, abstractEntity, id) {
const types = [
abstractEntity.val.viewOf,
abstractEntity.name,
...abstractEntity.val.views
].filter(x => x);
types.forEach(type => {
deleteItem(datastore, createQuery(type, id));
});
}

@@ -1,44 +0,25 @@

// Concepts: AbstractEntity
import { decorateCreate } from './create';
import { decorateRead } from './read';
import { decorateUpdate } from './update';
import { decorateDelete } from './delete';
import {curry, mapValues} from 'fp';
import {decorateCreate} from './create';
import {decorateRead} from './read';
import {decorateUpdate} from './update';
import {decorateDelete} from './delete';
import {decorateNoOperation} from './no-operation';
export function decorateApi(datastore) {
return abstractEntity => {
const api = getApi(abstractEntity);
const decoratedApi = decorateEveryApiFn(api, abstractEntity, datastore);
addDecoratedApi(abstractEntity, decoratedApi);
const decorateApi = curry((entityStore, queryCache, entity, apiFn) => {
const handler = {
CREATE: decorateCreate,
READ: decorateRead,
UPDATE: decorateUpdate,
DELETE: decorateDelete,
NO_OPERATION: decorateNoOperation
}[apiFn.operation || 'NO_OPERATION'];
return handler(entityStore, queryCache, entity, apiFn);
});
export const decorate = curry((entityStore, queryCache, entity) => {
const decoratedApi = mapValues(decorateApi(entityStore, queryCache, entity), entity.api);
return {
...entity,
api: decoratedApi
};
}
function getApi(abstractEntity) {
return abstractEntity.val.api;
}
function decorateEveryApiFn(api, abstractEntity, datastore) {
return Object.keys(api)
.map((name) => ({
name,
val: decorateApiFn(abstractEntity, api[name], datastore)
}));
}
function addDecoratedApi(abstractEntity, decoratedApi) {
abstractEntity.val.decoratedApi = decoratedApi;
}
function decorateApiFn(abstractEntity, apiFn, datastore) {
apiFn.invalidates = apiFn.invalidates || [];
switch (apiFn.operation) {
case 'CREATE':
return decorateCreate(apiFn, datastore, abstractEntity);
case 'READ':
return decorateRead(apiFn, datastore, abstractEntity);
case 'UPDATE':
return decorateUpdate(apiFn, datastore, abstractEntity);
case 'DELETE':
return decorateDelete(apiFn, datastore, abstractEntity);
}
}
});

@@ -1,69 +0,49 @@

import { createQuery } from 'query';
import { invalidateEntity, invalidateFunction } from 'invalidator';
import {
getItem,
getCollection,
addItem,
addCollection,
} from 'datastore';
import {get as getFromEs,
put as putInEs,
contains as inEs} from 'entity-store';
import {get as getFromQc,
put as putInQc,
contains as inQc,
getValue} from 'query-cache';
import {passThrough} from 'fp';
export function decorateRead(apiFn, datastore, abstractEntity) {
return (query) => {
if (apiFn.alwaysGetFreshData === true) {
return executeApiFnAndCache(apiFn, datastore, abstractEntity, query);
const getTtl = e => (e.ttl || 0) * 1000;
// Entity -> Int -> Bool
const hasExpired = (e, timestamp) => {
return (Date.now() - timestamp) > getTtl(e);
};
const decorateReadSingle = (es, e, aFn) => {
return (id) => {
if (inEs(es, e, id) && !aFn.alwaysGetFreshData) {
const v = getFromEs(es, e, id);
if (!hasExpired(e, v.timestamp)) {
return Promise.resolve(v.value);
}
}
const fromCache = getFromCache(apiFn, datastore, abstractEntity.name, query);
return fromCache.then(itemFromCache => {
if (itemFromCache) {
return itemFromCache;
} else {
return executeApiFnAndCache(apiFn, datastore, abstractEntity, query);
}
});
return aFn(id).then(passThrough(putInEs(es, e)));
};
}
};
function executeApiFnAndCache(apiFn, datastore, abstractEntity, query) {
const result = apiFn(query);
result.then(addToCache(apiFn, datastore, abstractEntity, query));
return result;
}
function getFromCache(apiFn, datastore, type, query) {
if (apiFn.plural) {
return getFromQueryCache(datastore, type, query, apiFn.name);
} else {
return getFromEntityCache(datastore, type, query);
}
}
function getFromQueryCache(datastore, type, query, apiFnName) {
return getCollection(datastore, createQueryForCollection(type, query, apiFnName));
}
function getFromEntityCache(datastore, type, id) {
return getItem(datastore, createQuery(type, id));
}
function addToCache(apiFn, datastore, abstractEntity, query) {
return data => {
if (apiFn.plural) {
const collectionQuery = createQueryForCollection(abstractEntity.name,
query,
apiFn.name);
addCollection(datastore,
collectionQuery,
data);
} else {
addItem(datastore, createQuery(abstractEntity.name, query), data);
const decorateReadQuery = (es, qc, e, aFn) => {
return (...args) => {
if (inQc(qc, e, aFn, args) && !aFn.alwaysGetFreshData) {
const v = getFromQc(qc, e, aFn, args);
if (!hasExpired(e, v.timestamp)) {
return Promise.resolve(getValue(v.value));
}
}
invalidateEntity(datastore, abstractEntity, 'READ');
invalidateFunction(datastore, abstractEntity, apiFn);
return aFn(...args).then(passThrough(putInQc(qc, e, aFn, args)));
};
}
};
function createQueryForCollection(type, query, apiFnName) {
return createQuery(type, query, { name: apiFnName });
export function decorateRead(es, qc, e, aFn) {
if (aFn.byId) {
return decorateReadSingle(es, e, aFn);
} else {
return decorateReadQuery(es, qc, e, aFn);
}
}

@@ -1,34 +0,11 @@

import { invalidateEntity, invalidateFunction } from 'invalidator';
import { createQueryFromItem } from 'query';
import {
updateItem,
patchItem
} from 'datastore';
import {put} from 'entity-store';
import {invalidate} from 'query-cache';
import {passThrough} from 'fp';
export function decorateUpdate(apiFn, datastore, abstractEntity) {
return (item) => {
updateItemAndViews(datastore, abstractEntity, item);
updateSuperEntity(datastore, abstractEntity, item);
const result = apiFn(item);
result.then(() => {
invalidateEntity(datastore, abstractEntity, 'UPDATE');
invalidateFunction(datastore, abstractEntity, apiFn);
});
return result;
export function decorateUpdate(es, qc, e, aFn) {
return (eValue, ...args) => {
put(es, e, eValue);
return aFn(eValue, ...args)
.then(passThrough(() => invalidate(qc, e, aFn)));
};
}
function updateItemAndViews(datastore, abstractEntity, item) {
const types = [abstractEntity.name, ...abstractEntity.val.views];
types.forEach(type => {
updateItem(datastore, createQueryFromItem(type, item), item);
});
}
function updateSuperEntity(datastore, abstractEntity, item) {
const superEntity = abstractEntity.val.viewOf;
if (!superEntity) {
return;
}
patchItem(datastore, createQueryFromItem(superEntity, item), item);
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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