UNPKG

@data-client/normalizr

Version:

Normalizes and denormalizes JSON according to schema for Redux and Flux applications

85 lines (79 loc) 14 kB
import GlobalCache from './globalCache.js'; import WeakDependencyMap from './WeakDependencyMap.js'; import buildQueryKey from '../buildQueryKey.js'; import { getEntityCaches } from './entitiesCache.js'; import { MemoPolicy } from './Policy.js'; import { getDependency } from '../delegate/BaseDelegate.js'; import getUnvisit from '../denormalize/unvisit.js'; // TODO: make MemoCache generic on the arguments sent to Delegate constructor /** Singleton to store the memoization cache for denormalization methods */ export default class MemoCache { constructor(policy = MemoPolicy) { /** Cache for every entity based on its dependencies and its own input */ /** Caches the final denormalized form based on input, entities */ this.endpoints = new WeakDependencyMap(); /** Caches the queryKey based on schema, args, and any used entities or indexes */ this.queryKeys = new Map(); this.policy = policy; this._getCache = getEntityCaches(new Map()); } /** Compute denormalized form maintaining referential equality for same inputs */ denormalize(schema, input, entities, args = []) { // we already vary based on input, so we don't need endpointKey? TODO: verify // if (!this.endpoints[endpointKey]) // this.endpoints[endpointKey] = new WeakDependencyMap<EntityPath>(); // undefined means don't do anything if (schema === undefined) { return { data: input, paths: [] }; } if (input === undefined) { return { data: undefined, paths: [] }; } const getEntity = this.policy.getEntities(entities); return getUnvisit(getEntity, new GlobalCache(getEntity, this._getCache, this.endpoints), args)(schema, input); } /** Compute denormalized form maintaining referential equality for same inputs */ query(schema, args, state, // NOTE: different orders can result in cache busting here; but since it's just a perf penalty we will allow for now argsKey = JSON.stringify(args)) { const input = this.buildQueryKey(schema, args, state, argsKey); if (!input) { return { data: undefined, paths: [] }; } return this.denormalize(schema, input, state.entities, args); } buildQueryKey(schema, args, state, // NOTE: different orders can result in cache busting here; but since it's just a perf penalty we will allow for now argsKey = JSON.stringify(args)) { // This is redundant for buildQueryKey checks, but that was is used for recursion so we still need the checks there // TODO: If we make each recursive call include cache lookups, we combine these checks together // null is object so we need double check if (typeof schema !== 'object' && typeof schema.queryKey !== 'function' || !schema) return schema; // cache lookup: argsKey -> schema -> ...touched indexes or entities let queryCache = this.queryKeys.get(argsKey); if (!queryCache) { queryCache = new WeakDependencyMap(); this.queryKeys.set(argsKey, queryCache); } const baseDelegate = new this.policy.QueryDelegate(state); // eslint-disable-next-line prefer-const let [value, paths] = queryCache.get(schema, getDependency(baseDelegate)); // paths undefined is the only way to truly tell nothing was found (the value could have actually been undefined) if (!paths) { const [delegate, dependencies] = baseDelegate.tracked(schema); value = buildQueryKey(delegate)(schema, args); queryCache.set(dependencies, value); } return value; } } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJHbG9iYWxDYWNoZSIsIldlYWtEZXBlbmRlbmN5TWFwIiwiYnVpbGRRdWVyeUtleSIsImdldEVudGl0eUNhY2hlcyIsIk1lbW9Qb2xpY3kiLCJnZXREZXBlbmRlbmN5IiwiZ2V0VW52aXNpdCIsIk1lbW9DYWNoZSIsImNvbnN0cnVjdG9yIiwicG9saWN5IiwiZW5kcG9pbnRzIiwicXVlcnlLZXlzIiwiTWFwIiwiX2dldENhY2hlIiwiZGVub3JtYWxpemUiLCJzY2hlbWEiLCJpbnB1dCIsImVudGl0aWVzIiwiYXJncyIsInVuZGVmaW5lZCIsImRhdGEiLCJwYXRocyIsImdldEVudGl0eSIsImdldEVudGl0aWVzIiwicXVlcnkiLCJzdGF0ZSIsImFyZ3NLZXkiLCJKU09OIiwic3RyaW5naWZ5IiwicXVlcnlLZXkiLCJxdWVyeUNhY2hlIiwiZ2V0Iiwic2V0IiwiYmFzZURlbGVnYXRlIiwiUXVlcnlEZWxlZ2F0ZSIsInZhbHVlIiwiZGVsZWdhdGUiLCJkZXBlbmRlbmNpZXMiLCJ0cmFja2VkIl0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL21lbW8vTWVtb0NhY2hlLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBHbG9iYWxDYWNoZSBmcm9tICcuL2dsb2JhbENhY2hlLmpzJztcbmltcG9ydCBXZWFrRGVwZW5kZW5jeU1hcCBmcm9tICcuL1dlYWtEZXBlbmRlbmN5TWFwLmpzJztcbmltcG9ydCBidWlsZFF1ZXJ5S2V5IGZyb20gJy4uL2J1aWxkUXVlcnlLZXkuanMnO1xuaW1wb3J0IHsgR2V0RW50aXR5Q2FjaGUsIGdldEVudGl0eUNhY2hlcyB9IGZyb20gJy4vZW50aXRpZXNDYWNoZS5qcyc7XG5pbXBvcnQgeyBNZW1vUG9saWN5IH0gZnJvbSAnLi9Qb2xpY3kuanMnO1xuaW1wb3J0IHsgZ2V0RGVwZW5kZW5jeSB9IGZyb20gJy4uL2RlbGVnYXRlL0Jhc2VEZWxlZ2F0ZS5qcyc7XG5pbXBvcnQgdHlwZSB7IElOVkFMSUQgfSBmcm9tICcuLi9kZW5vcm1hbGl6ZS9zeW1ib2wuanMnO1xuaW1wb3J0IGdldFVudmlzaXQgZnJvbSAnLi4vZGVub3JtYWxpemUvdW52aXNpdC5qcyc7XG5pbXBvcnQgdHlwZSB7XG4gIEVudGl0eVBhdGgsXG4gIE5vcm1hbGl6ZWRJbmRleCxcbiAgUXVlcnlQYXRoLFxuICBTY2hlbWEsXG59IGZyb20gJy4uL2ludGVyZmFjZS5qcyc7XG5pbXBvcnQgdHlwZSB7IERlbm9ybWFsaXplTnVsbGFibGUsIE5vcm1hbGl6ZU51bGxhYmxlIH0gZnJvbSAnLi4vdHlwZXMuanMnO1xuaW1wb3J0IHR5cGUgeyBJTWVtb1BvbGljeSwgRW5kcG9pbnRzQ2FjaGUgfSBmcm9tICcuL3R5cGVzLmpzJztcblxuLy8gVE9ETzogbWFrZSBNZW1vQ2FjaGUgZ2VuZXJpYyBvbiB0aGUgYXJndW1lbnRzIHNlbnQgdG8gRGVsZWdhdGUgY29uc3RydWN0b3JcblxuLyoqIFNpbmdsZXRvbiB0byBzdG9yZSB0aGUgbWVtb2l6YXRpb24gY2FjaGUgZm9yIGRlbm9ybWFsaXphdGlvbiBtZXRob2RzICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBNZW1vQ2FjaGUge1xuICAvKiogQ2FjaGUgZm9yIGV2ZXJ5IGVudGl0eSBiYXNlZCBvbiBpdHMgZGVwZW5kZW5jaWVzIGFuZCBpdHMgb3duIGlucHV0ICovXG4gIGRlY2xhcmUgcHJvdGVjdGVkIF9nZXRDYWNoZTogR2V0RW50aXR5Q2FjaGU7XG4gIC8qKiBDYWNoZXMgdGhlIGZpbmFsIGRlbm9ybWFsaXplZCBmb3JtIGJhc2VkIG9uIGlucHV0LCBlbnRpdGllcyAqL1xuICBwcm90ZWN0ZWQgZW5kcG9pbnRzOiBFbmRwb2ludHNDYWNoZSA9IG5ldyBXZWFrRGVwZW5kZW5jeU1hcDxFbnRpdHlQYXRoPigpO1xuICAvKiogQ2FjaGVzIHRoZSBxdWVyeUtleSBiYXNlZCBvbiBzY2hlbWEsIGFyZ3MsIGFuZCBhbnkgdXNlZCBlbnRpdGllcyBvciBpbmRleGVzICovXG4gIHByb3RlY3RlZCBxdWVyeUtleXM6IE1hcDxzdHJpbmcsIFdlYWtEZXBlbmRlbmN5TWFwPFF1ZXJ5UGF0aD4+ID0gbmV3IE1hcCgpO1xuXG4gIGRlY2xhcmUgcHJvdGVjdGVkIHBvbGljeTogSU1lbW9Qb2xpY3k7XG5cbiAgY29uc3RydWN0b3IocG9saWN5OiBJTWVtb1BvbGljeSA9IE1lbW9Qb2xpY3kpIHtcbiAgICB0aGlzLnBvbGljeSA9IHBvbGljeTtcbiAgICB0aGlzLl9nZXRDYWNoZSA9IGdldEVudGl0eUNhY2hlcyhuZXcgTWFwKCkpO1xuICB9XG5cbiAgLyoqIENvbXB1dGUgZGVub3JtYWxpemVkIGZvcm0gbWFpbnRhaW5pbmcgcmVmZXJlbnRpYWwgZXF1YWxpdHkgZm9yIHNhbWUgaW5wdXRzICovXG4gIGRlbm9ybWFsaXplPFMgZXh0ZW5kcyBTY2hlbWE+KFxuICAgIHNjaGVtYTogUyB8IHVuZGVmaW5lZCxcbiAgICBpbnB1dDogdW5rbm93bixcbiAgICBlbnRpdGllczogYW55LFxuICAgIGFyZ3M6IHJlYWRvbmx5IGFueVtdID0gW10sXG4gICk6IHtcbiAgICBkYXRhOiBEZW5vcm1hbGl6ZU51bGxhYmxlPFM+IHwgdHlwZW9mIElOVkFMSUQ7XG4gICAgcGF0aHM6IEVudGl0eVBhdGhbXTtcbiAgfSB7XG4gICAgLy8gd2UgYWxyZWFkeSB2YXJ5IGJhc2VkIG9uIGlucHV0LCBzbyB3ZSBkb24ndCBuZWVkIGVuZHBvaW50S2V5PyBUT0RPOiB2ZXJpZnlcbiAgICAvLyBpZiAoIXRoaXMuZW5kcG9pbnRzW2VuZHBvaW50S2V5XSlcbiAgICAvLyAgIHRoaXMuZW5kcG9pbnRzW2VuZHBvaW50S2V5XSA9IG5ldyBXZWFrRGVwZW5kZW5jeU1hcDxFbnRpdHlQYXRoPigpO1xuXG4gICAgLy8gdW5kZWZpbmVkIG1lYW5zIGRvbid0IGRvIGFueXRoaW5nXG4gICAgaWYgKHNjaGVtYSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4geyBkYXRhOiBpbnB1dCBhcyBhbnksIHBhdGhzOiBbXSB9O1xuICAgIH1cbiAgICBpZiAoaW5wdXQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIHsgZGF0YTogdW5kZWZpbmVkIGFzIGFueSwgcGF0aHM6IFtdIH07XG4gICAgfVxuICAgIGNvbnN0IGdldEVudGl0eSA9IHRoaXMucG9saWN5LmdldEVudGl0aWVzKGVudGl0aWVzKTtcblxuICAgIHJldHVybiBnZXRVbnZpc2l0KFxuICAgICAgZ2V0RW50aXR5LFxuICAgICAgbmV3IEdsb2JhbENhY2hlKGdldEVudGl0eSwgdGhpcy5fZ2V0Q2FjaGUsIHRoaXMuZW5kcG9pbnRzKSxcbiAgICAgIGFyZ3MsXG4gICAgKShzY2hlbWEsIGlucHV0KTtcbiAgfVxuXG4gIC8qKiBDb21wdXRlIGRlbm9ybWFsaXplZCBmb3JtIG1haW50YWluaW5nIHJlZmVyZW50aWFsIGVxdWFsaXR5IGZvciBzYW1lIGlucHV0cyAqL1xuICBxdWVyeTxTIGV4dGVuZHMgU2NoZW1hPihcbiAgICBzY2hlbWE6IFMsXG4gICAgYXJnczogcmVhZG9ubHkgYW55W10sXG4gICAgc3RhdGU6IFN0YXRlSW50ZXJmYWNlLFxuICAgIC8vIE5PVEU6IGRpZmZlcmVudCBvcmRlcnMgY2FuIHJlc3VsdCBpbiBjYWNoZSBidXN0aW5nIGhlcmU7IGJ1dCBzaW5jZSBpdCdzIGp1c3QgYSBwZXJmIHBlbmFsdHkgd2Ugd2lsbCBhbGxvdyBmb3Igbm93XG4gICAgYXJnc0tleTogc3RyaW5nID0gSlNPTi5zdHJpbmdpZnkoYXJncyksXG4gICk6IHtcbiAgICBkYXRhOiBEZW5vcm1hbGl6ZU51bGxhYmxlPFM+IHwgdHlwZW9mIElOVkFMSUQ7XG4gICAgcGF0aHM6IEVudGl0eVBhdGhbXTtcbiAgfSB7XG4gICAgY29uc3QgaW5wdXQgPSB0aGlzLmJ1aWxkUXVlcnlLZXkoc2NoZW1hLCBhcmdzLCBzdGF0ZSwgYXJnc0tleSk7XG5cbiAgICBpZiAoIWlucHV0KSB7XG4gICAgICByZXR1cm4geyBkYXRhOiB1bmRlZmluZWQgYXMgYW55LCBwYXRoczogW10gfTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5kZW5vcm1hbGl6ZShzY2hlbWEsIGlucHV0LCBzdGF0ZS5lbnRpdGllcywgYXJncyk7XG4gIH1cblxuICBidWlsZFF1ZXJ5S2V5PFMgZXh0ZW5kcyBTY2hlbWE+KFxuICAgIHNjaGVtYTogUyxcbiAgICBhcmdzOiByZWFkb25seSBhbnlbXSxcbiAgICBzdGF0ZTogU3RhdGVJbnRlcmZhY2UsXG4gICAgLy8gTk9URTogZGlmZmVyZW50IG9yZGVycyBjYW4gcmVzdWx0IGluIGNhY2hlIGJ1c3RpbmcgaGVyZTsgYnV0IHNpbmNlIGl0J3MganVzdCBhIHBlcmYgcGVuYWx0eSB3ZSB3aWxsIGFsbG93IGZvciBub3dcbiAgICBhcmdzS2V5OiBzdHJpbmcgPSBKU09OLnN0cmluZ2lmeShhcmdzKSxcbiAgKTogTm9ybWFsaXplTnVsbGFibGU8Uz4ge1xuICAgIC8vIFRoaXMgaXMgcmVkdW5kYW50IGZvciBidWlsZFF1ZXJ5S2V5IGNoZWNrcywgYnV0IHRoYXQgd2FzIGlzIHVzZWQgZm9yIHJlY3Vyc2lvbiBzbyB3ZSBzdGlsbCBuZWVkIHRoZSBjaGVja3MgdGhlcmVcbiAgICAvLyBUT0RPOiBJZiB3ZSBtYWtlIGVhY2ggcmVjdXJzaXZlIGNhbGwgaW5jbHVkZSBjYWNoZSBsb29rdXBzLCB3ZSBjb21iaW5lIHRoZXNlIGNoZWNrcyB0b2dldGhlclxuICAgIC8vIG51bGwgaXMgb2JqZWN0IHNvIHdlIG5lZWQgZG91YmxlIGNoZWNrXG4gICAgaWYgKFxuICAgICAgKHR5cGVvZiBzY2hlbWEgIT09ICdvYmplY3QnICYmXG4gICAgICAgIHR5cGVvZiAoc2NoZW1hIGFzIGFueSkucXVlcnlLZXkgIT09ICdmdW5jdGlvbicpIHx8XG4gICAgICAhc2NoZW1hXG4gICAgKVxuICAgICAgcmV0dXJuIHNjaGVtYSBhcyBhbnk7XG5cbiAgICAvLyBjYWNoZSBsb29rdXA6IGFyZ3NLZXkgLT4gc2NoZW1hIC0+IC4uLnRvdWNoZWQgaW5kZXhlcyBvciBlbnRpdGllc1xuICAgIGxldCBxdWVyeUNhY2hlID0gdGhpcy5xdWVyeUtleXMuZ2V0KGFyZ3NLZXkpIGFzXG4gICAgICB8IFdlYWtEZXBlbmRlbmN5TWFwPFF1ZXJ5UGF0aCwgb2JqZWN0LCBhbnk+XG4gICAgICB8IHVuZGVmaW5lZDtcbiAgICBpZiAoIXF1ZXJ5Q2FjaGUpIHtcbiAgICAgIHF1ZXJ5Q2FjaGUgPSBuZXcgV2Vha0RlcGVuZGVuY3lNYXA8UXVlcnlQYXRoPigpO1xuICAgICAgdGhpcy5xdWVyeUtleXMuc2V0KGFyZ3NLZXksIHF1ZXJ5Q2FjaGUpO1xuICAgIH1cblxuICAgIGNvbnN0IGJhc2VEZWxlZ2F0ZSA9IG5ldyB0aGlzLnBvbGljeS5RdWVyeURlbGVnYXRlKHN0YXRlKTtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgcHJlZmVyLWNvbnN0XG4gICAgbGV0IFt2YWx1ZSwgcGF0aHNdID0gcXVlcnlDYWNoZS5nZXQoXG4gICAgICBzY2hlbWEgYXMgYW55LFxuICAgICAgZ2V0RGVwZW5kZW5jeShiYXNlRGVsZWdhdGUpLFxuICAgICk7XG5cbiAgICAvLyBwYXRocyB1bmRlZmluZWQgaXMgdGhlIG9ubHkgd2F5IHRvIHRydWx5IHRlbGwgbm90aGluZyB3YXMgZm91bmQgKHRoZSB2YWx1ZSBjb3VsZCBoYXZlIGFjdHVhbGx5IGJlZW4gdW5kZWZpbmVkKVxuICAgIGlmICghcGF0aHMpIHtcbiAgICAgIGNvbnN0IFtkZWxlZ2F0ZSwgZGVwZW5kZW5jaWVzXSA9IGJhc2VEZWxlZ2F0ZS50cmFja2VkKHNjaGVtYSk7XG5cbiAgICAgIHZhbHVlID0gYnVpbGRRdWVyeUtleShkZWxlZ2F0ZSkoc2NoZW1hLCBhcmdzKTtcbiAgICAgIHF1ZXJ5Q2FjaGUuc2V0KGRlcGVuZGVuY2llcywgdmFsdWUpO1xuICAgIH1cbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbn1cblxudHlwZSBTdGF0ZUludGVyZmFjZSA9IHtcbiAgZW50aXRpZXM6XG4gICAgfCBSZWNvcmQ8c3RyaW5nLCBSZWNvcmQ8c3RyaW5nLCBhbnk+IHwgdW5kZWZpbmVkPlxuICAgIHwge1xuICAgICAgICBnZXRJbihrOiBzdHJpbmdbXSk6IGFueTtcbiAgICAgIH07XG4gIGluZGV4ZXM6XG4gICAgfCBOb3JtYWxpemVkSW5kZXhcbiAgICB8IHtcbiAgICAgICAgZ2V0SW4oazogc3RyaW5nW10pOiBhbnk7XG4gICAgICB9O1xufTtcbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBT0EsV0FBVyxNQUFNLGtCQUFrQjtBQUMxQyxPQUFPQyxpQkFBaUIsTUFBTSx3QkFBd0I7QUFDdEQsT0FBT0MsYUFBYSxNQUFNLHFCQUFxQjtBQUMvQyxTQUF5QkMsZUFBZSxRQUFRLG9CQUFvQjtBQUNwRSxTQUFTQyxVQUFVLFFBQVEsYUFBYTtBQUN4QyxTQUFTQyxhQUFhLFFBQVEsNkJBQTZCO0FBRTNELE9BQU9DLFVBQVUsTUFBTSwyQkFBMkI7QUFVbEQ7O0FBRUE7QUFDQSxlQUFlLE1BQU1DLFNBQVMsQ0FBQztFQVU3QkMsV0FBV0EsQ0FBQ0MsTUFBbUIsR0FBR0wsVUFBVSxFQUFFO0lBVDlDO0lBRUE7SUFBQSxLQUNVTSxTQUFTLEdBQW1CLElBQUlULGlCQUFpQixDQUFhLENBQUM7SUFDekU7SUFBQSxLQUNVVSxTQUFTLEdBQThDLElBQUlDLEdBQUcsQ0FBQyxDQUFDO0lBS3hFLElBQUksQ0FBQ0gsTUFBTSxHQUFHQSxNQUFNO0lBQ3BCLElBQUksQ0FBQ0ksU0FBUyxHQUFHVixlQUFlLENBQUMsSUFBSVMsR0FBRyxDQUFDLENBQUMsQ0FBQztFQUM3Qzs7RUFFQTtFQUNBRSxXQUFXQSxDQUNUQyxNQUFxQixFQUNyQkMsS0FBYyxFQUNkQyxRQUFhLEVBQ2JDLElBQW9CLEdBQUcsRUFBRSxFQUl6QjtJQUNBO0lBQ0E7SUFDQTs7SUFFQTtJQUNBLElBQUlILE1BQU0sS0FBS0ksU0FBUyxFQUFFO01BQ3hCLE9BQU87UUFBRUMsSUFBSSxFQUFFSixLQUFZO1FBQUVLLEtBQUssRUFBRTtNQUFHLENBQUM7SUFDMUM7SUFDQSxJQUFJTCxLQUFLLEtBQUtHLFNBQVMsRUFBRTtNQUN2QixPQUFPO1FBQUVDLElBQUksRUFBRUQsU0FBZ0I7UUFBRUUsS0FBSyxFQUFF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