@graphistry/falcor
Version:
A JavaScript library for efficient data fetching.
154 lines (134 loc) • 5.7 kB
JavaScript
var clone = require('../clone');
var onValueType = require('./onValueType');
var FalcorJSON = require('./json/FalcorJSON');
var NullInPathError = require('../../errors/NullInPathError');
var InvalidKeySetError = require('../../errors/InvalidKeySetError');
var materializedAtom = require('@graphistry/falcor-path-utils/lib/support/materializedAtom');
module.exports = onMaterializeFlatBuffer;
/* eslint-disable camelcase */
/* eslint-disable no-cond-assign */
/* eslint-disable no-constant-condition */
function onMaterializeFlatBuffer(jsonArg, path, depth, length,
branchSelector, boxValues, modelRoot, reportMissing,
results, requestedPath, optimizedPath, optimizedLength,
fromReference, onMissing) {
var json = jsonArg, type, refTarget;
// ============ Check for base cases ================
// If there's nowhere to go, we've reached a terminal node, or hit
// the end of the path, stop now. Either build missing paths or report the value.
if (undefined === path) {
onValueType(undefined, undefined, json,
path, depth, undefined, results,
requestedPath, depth, optimizedPath,
optimizedLength, fromReference, modelRoot, undefined,
false, branchSelector, boxValues, false, reportMissing,
false, undefined, onMissing, undefined);
return boxValues ? clone(materializedAtom) : undefined;
}
var f_meta, f_old_keys, f_new_keys;
var nextKey,
keyset, keyIsRange,
keys = path['$keys'],
nextDepth = depth + 1, rangeEnd,
nextOptimizedLength = optimizedLength + 1;
if (!json || 'object' !== typeof json) {
json = {};
json.__proto__ = FalcorJSON.prototype;
json[f_meta_data] = f_meta = {};
f_meta['$code'] = '';
f_meta[f_meta_status] = reportMissing ? 'pending' : 'resolved';
f_meta[f_meta_version] = modelRoot.version;
f_meta[f_meta_abs_path] = optimizedPath.slice(
0, optimizedLength
);
if (branchSelector) {
json = branchSelector(json);
}
} else if (!(f_meta = json[f_meta_data])) {
json[f_meta_data] = f_meta = {};
f_meta['$code'] = '';
f_meta[f_meta_status] = reportMissing ? 'pending' : 'resolved';
f_meta[f_meta_version] = modelRoot.version;
f_meta[f_meta_abs_path] = optimizedPath.slice(
0, optimizedLength
);
} else {
f_old_keys = f_meta[f_meta_keys];
f_meta['$code'] = '';
f_meta[f_meta_status] = reportMissing ? 'pending' : 'resolved';
f_meta[f_meta_version] = modelRoot.version;
f_meta[f_meta_abs_path] = optimizedPath.slice(
0, optimizedLength
);
}
f_new_keys = Object.create(null);
var nextPath;
var keysIndex = -1;
var keysLength = keys.length;
iteratingKeyset:
while (++keysIndex < keysLength) {
keyset = keys[keysIndex];
nextPath = path[keysIndex];
// If `null` appears before the end of the path, throw an error.
// If `null` is at the end of the path, but the reference doesn't
// point to a sentinel value, throw an error.
//
// Inserting `null` at the end of the path indicates the target of a ref
// should be returned, rather than the ref itself. When `null` is the last
// key, the path is lengthened by one, ensuring that if a ref is encountered
// just before the `null` key, the reference is followed before terminating.
if (null === keyset) {
if (nextPath !== undefined) {
throw new NullInPathError();
}
continue;
}
// If the keyset is a primitive value, we've found our `nextKey`.
else if ('object' !== typeof keyset) {
nextKey = keyset;
rangeEnd = undefined;
keyIsRange = false;
}
// If the Keyset isn't null or primitive, then it must be a Range.
else {
rangeEnd = keyset.to;
nextKey = keyset.from || 0;
if ('number' !== typeof rangeEnd) {
rangeEnd = nextKey + (keyset.length || 0) - 1;
}
if ((rangeEnd - nextKey) < 0) {
break iteratingKeyset;
}
keyIsRange = true;
}
// Now that we have the next key, step down one level in the cache.
do {
requestedPath[depth] = nextKey;
optimizedPath[optimizedLength] = nextKey;
f_new_keys[nextKey] = true;
if (f_old_keys && (nextKey in f_old_keys)) {
f_old_keys[nextKey] = false;
}
// insert the materialized branch
json[nextKey] = onMaterializeFlatBuffer(
json[nextKey], nextPath, nextDepth,
nextDepth, branchSelector, boxValues, modelRoot, reportMissing,
results, requestedPath, optimizedPath, nextOptimizedLength,
fromReference, onMissing
);
}
// Re-enter the inner loop and continue iterating the Range, or exit
// here if we encountered a Key.
while (keyIsRange && ++nextKey <= rangeEnd);
}
f_meta[f_meta_keys] = f_new_keys;
if (f_old_keys) {
for (nextKey in f_old_keys) {
if (f_old_keys[nextKey]) {
delete json[nextKey];
}
}
}
// `json` will be a branch if any cache hits, or undefined if all cache misses
return json;
}