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

json-refs

Package Overview
Dependencies
Maintainers
1
Versions
60
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

json-refs - npm Package Compare versions

Comparing version 2.1.2 to 2.1.3

633

index.js

@@ -134,110 +134,180 @@ /*

function filterRefs (options, refs) {
var refFilter = makeRefFilter(options);
var filtered = {};
var subDocPrefix = pathToPtr(makeSubDocPath(options));
function findAncestors (obj, path) {
var ancestors = [];
var node;
Object.keys(refs).forEach(function (refPtr) {
var refDetails = refs[refPtr];
if (path.length > 0) {
node = obj;
if (refFilter(refDetails, pathFromPtr(refPtr)) === true &&
refPtr.indexOf(subDocPrefix) > -1 &&
(refDetails.type !== 'invalid' || options.includeInvalid === true)) {
filtered[refPtr] = refDetails;
}
});
path.slice(0, path.length - 1).forEach(function (seg) {
if (seg in node) {
node = node[seg];
return filtered;
ancestors.push(node);
}
});
}
return ancestors;
}
function findAncestors (obj, path) {
var ancestors = [];
var node = obj;
function processSubDocument (mode, doc, subDocPath, refDetails, options, parents, parentPtrs, allRefs, indirect) {
var refValue;
var rOptions;
path.slice(0, path.length - 1).forEach(function (seg) {
if (seg in node) {
node = node[seg];
if (subDocPath.length > 0) {
try {
refValue = findValue(doc, subDocPath);
} catch (err) {
// We only mark missing remote references as missing because local references can have deferred values
if (mode === 'remote') {
refDetails.error = err.message;
refDetails.missing = true;
}
}
} else {
refValue = doc;
}
ancestors.push(node);
if (!isType(refValue, 'Undefined')) {
refDetails.value = clone(refValue);
}
if (isType(refValue, 'Array') || isType(refValue, 'Object')) {
rOptions = clone(options);
if (mode === 'local') {
delete rOptions.subDocPath;
// Traverse the dereferenced value
doc = refValue;
} else {
rOptions.relativeBase = path.dirname(parents[parents.length - 1]);
if (subDocPath.length === 0) {
delete rOptions.subDocPath;
} else {
rOptions.subDocPath = subDocPath;
}
}
});
return ancestors;
return findRefsRecursive(doc, rOptions, parents, parentPtrs, allRefs, indirect);
}
}
// Should this be its own exported API?
function findAllRefs (obj, options, parents, parentPath, documents) {
function findRefsRecursive (obj, options, parents, parentPtrs, allRefs, indirect) {
var allTasks = Promise.resolve();
var parentPath = parentPtrs.length ? pathFromPtr(parentPtrs[parentPtrs.length - 1]) : [];
var refs = findRefs(obj, options);
var subDocPath = options.subDocPath || [];
var subDocPtr = pathToPtr(subDocPath);
var ancestorPtrs = ['#'];
parents.forEach(function (parent, index) {
if (parent.charAt(0) !== '#') {
ancestorPtrs.push(parentPtrs[index]);
}
});
// Reverse the order so we search them in the proper order
ancestorPtrs.reverse();
if ((parents[parents.length - 1] || '').charAt(0) !== '#') {
allRefs.documents[pathToPtr(parentPath)] = obj;
}
Object.keys(refs).forEach(function (refPtr) {
var refDetails = refs[refPtr];
var refPath = pathFromPtr(refPtr);
var location;
var parentIndex;
var refFullPath;
var refFullPtr;
// Only process remote references
if (remoteTypes.indexOf(refDetails.type) > -1) {
location = combineURIs(options.relativeBase, refDetails.uri);
parentIndex = parents.indexOf(location);
// If there are no parents, treat the reference pointer as-is. Otherwise, the reference is a reference within a
// remote document and its sub document path prefix must be removed.
if (parents.length === 0) {
refFullPath = parentPath.concat(pathFromPtr(refPtr));
} else {
refFullPath = parentPath.concat(pathFromPtr(refPtr).slice(parents.length === 0 ? 0 : subDocPath.length));
}
if (parentIndex === -1) {
allTasks = allTasks
.then(function () {
var rParentPath = parentPath.concat(refPath);
var rOptions = clone(options);
refFullPtr = pathToPtr(refFullPath);
// Remove the sub document path
delete rOptions.subDocPath;
// It is possible to process the same reference more than once in the event of hierarchical references so we avoid
// processing a reference if we've already done so.
if (!isType(allRefs[refFullPtr], 'Undefined')) {
return;
}
// Remove the relative base
delete rOptions.relativeBase;
// Record the reference metadata
allRefs.refs[refFullPtr] = refs[refPtr];
return findRefsAt(location, rOptions)
.then(function (rRefs) {
// Record the location for circular reference identification
rRefs.location = location;
// Do not process invalid references
if (isType(refDetails.error, 'Undefined') && refDetails.type !== 'invalid') {
if (remoteTypes.indexOf(refDetails.type) > -1) {
location = combineURIs(options.relativeBase, refDetails.uri);
parentIndex = parents.indexOf(location);
} else {
location = refDetails.uri;
parentIndex = parentPtrs.indexOf(location);
}
if (refDetails.uriDetails.fragment) {
// If the remote reference was for a fragment, do not include the reference details
rRefs.refs = {};
// Record ancestor paths
refDetails.ancestorPtrs = ancestorPtrs;
// Record the remote document
documents[pathToPtr(rParentPath)] = rRefs;
// Record if the reference is indirect based on its parent
refDetails.indirect = indirect;
return rRefs;
} else {
// Record the location in the document where the parent document was resolved
Object.keys(rRefs.refs).forEach(function (refPtr) {
rRefs.refs[refPtr].parentLocation = pathToPtr(rParentPath);
});
// Record the remote document
documents[pathToPtr(rParentPath)] = rRefs;
// Update the relative base based on the retrieved location
rOptions.relativeBase = path.dirname(location);
// Find all important references within the document
return findAllRefs(rRefs.value, rOptions, parents.concat(location), rParentPath, documents);
}
}, function (err) {
refDetails.error = err.message;
refDetails.missing = true;
// Only process non-circular references further
if (parentIndex === -1) {
if (remoteTypes.indexOf(refDetails.type) > -1) {
allTasks = allTasks
.then(function () {
return getRemoteDocument(location, options)
.then(function (doc) {
return processSubDocument('remote',
doc,
isType(refDetails.uriDetails.fragment, 'Undefined') ?
[] :
pathFromPtr(decodeURI(refDetails.uriDetails.fragment)),
refDetails,
options,
parents.concat(location),
parentPtrs.concat(refFullPtr),
allRefs,
indirect);
})
.catch(function (err) {
refDetails.error = err.message;
refDetails.missing = true;
});
});
} else {
if (refFullPtr.indexOf(location + '/') !== 0 && refFullPtr !== location &&
subDocPtr.indexOf(location + '/') !== 0 && subDocPtr !== location) {
allTasks = allTasks
.then(function () {
return processSubDocument('local',
obj,
pathFromPtr(location),
refDetails,
options,
parents.concat(location),
parentPtrs.concat(refFullPtr),
allRefs,
indirect || (location.indexOf(subDocPtr + '/') === -1 && location !== subDocPtr));
});
});
} else {
refDetails.circular = true;
}
}
} else {
// Mark seen ancestors as circular
parents.slice(parentIndex).forEach(function (parent) {
Object.keys(documents).forEach(function (cRefPtr) {
var document = documents[cRefPtr];
if (document.location === parent) {
document.circular = true;
}
});
parentPtrs.slice(parentIndex).forEach(function (parentPtr) {
allRefs.refs[parentPtr].circular = true;
});
// Mark self as circular
documents[pathToPtr(parentPath)].refs[refPtr].circular = true;
refDetails.circular = true;
}

@@ -249,31 +319,3 @@ }

.then(function () {
// Only collapse the documents when we're back at the top of the promise stack
if (parentPath.length === 0) {
// Collapse all references together into one list
Object.keys(documents).forEach(function (refPtr) {
var document = documents[refPtr];
if (Object.keys(refs).length > 0) {
// Merge each reference into the root document's references
Object.keys(document.refs).forEach(function (cRefPtr) {
var fPtr = pathToPtr(pathFromPtr(refPtr).concat(pathFromPtr(cRefPtr)));
var refDetails = refs[fPtr];
if (isType(refDetails, 'Undefined')) {
refs[fPtr] = document.refs[cRefPtr];
}
});
// Record the value of the remote reference
refs[refPtr].value = document.value;
// Mark the remote reference itself as circular
if (document.circular) {
refs[refPtr].circular = true;
}
}
});
}
return refs;
return allRefs;
});

@@ -284,22 +326,14 @@

function findValue (obj, path, ignore) {
function findValue (obj, path) {
var value = obj;
try {
path.forEach(function (seg) {
seg = decodeURI(seg);
path.forEach(function (seg) {
seg = decodeURI(seg);
if (seg in value) {
value = value[seg];
} else {
throw Error('JSON Pointer points to missing location: ' + pathToPtr(path));
}
});
} catch (err) {
if (ignore === true) {
value = undefined;
if (seg in value) {
value = value[seg];
} else {
throw err;
throw Error('JSON Pointer points to missing location: ' + pathToPtr(path));
}
}
});

@@ -413,7 +447,7 @@ return value;

var refFilter;
var validTypes;
if (isType(options.filter, 'Array') || isType(options.filter, 'String')) {
validTypes = isType(options.filter, 'String') ? [options.filter] : options.filter;
refFilter = function (refDetails) {
var validTypes = isType(options.filter, 'String') ? [options.filter] : options.filter;
// Check the exact type or for invalid URIs, check its original type

@@ -424,3 +458,3 @@ return validTypes.indexOf(refDetails.type) > -1 || validTypes.indexOf(getRefType(refDetails)) > -1;

refFilter = options.filter;
} else {
} else if (isType(options.filter, 'Undefined')) {
refFilter = function () {

@@ -431,15 +465,19 @@ return true;

return refFilter;
return function (refDetails, path) {
return (refDetails.type !== 'invalid' || options.includeInvalid === true) && refFilter(refDetails, path);
};
}
function makeSubDocPath (options) {
var fromPath = [];
var subDocPath;
if (isType(options.subDocPath, 'Array')) {
fromPath = options.subDocPath;
subDocPath = options.subDocPath;
} else if (isType(options.subDocPath, 'String')) {
fromPath = pathFromPtr(options.subDocPath);
subDocPath = pathFromPtr(options.subDocPath);
} else if (isType(options.subDocPath, 'Undefined')) {
subDocPath = [];
}
return fromPath;
return subDocPath;
}

@@ -490,27 +528,50 @@

function validateOptions (options) {
if (!isType(options, 'Undefined')) {
if (!isType(options, 'Object')) {
throw new TypeError('options must be an Object');
} else if (!isType(options.filter, 'Undefined') &&
!isType(options.filter, 'Array') &&
!isType(options.filter, 'Function') &&
!isType(options.filter, 'String')) {
throw new TypeError('options.filter must be an Array, a Function of a String');
} else if (!isType(options.includeInvalid, 'Undefined') &&
!isType(options.includeInvalid, 'Boolean')) {
throw new TypeError('options.includeInvalid must be a Boolean');
} else if (!isType(options.refPreProcessor, 'Undefined') &&
!isType(options.refPreProcessor, 'Function')) {
throw new TypeError('options.refPreProcessor must be a Function');
} else if (!isType(options.refPostProcessor, 'Undefined') &&
!isType(options.refPostProcessor, 'Function')) {
throw new TypeError('options.refPostProcessor must be a Function');
} else if (!isType(options.subDocPath, 'Undefined') &&
!isType(options.subDocPath, 'Array') &&
!isPtr(options.subDocPath)) {
// If a pointer is provided, throw an error if it's not the proper type
throw new TypeError('options.subDocPath must be an Array of path segments or a valid JSON Pointer');
function validateOptions (options, obj) {
if (isType(options, 'Undefined')) {
// Default to an empty options object
options = {};
} else {
// Clone the options so we do not alter the ones passed in
options = clone(options);
}
if (!isType(options, 'Object')) {
throw new TypeError('options must be an Object');
} else if (!isType(options.filter, 'Undefined') &&
!isType(options.filter, 'Array') &&
!isType(options.filter, 'Function') &&
!isType(options.filter, 'String')) {
throw new TypeError('options.filter must be an Array, a Function of a String');
} else if (!isType(options.includeInvalid, 'Undefined') &&
!isType(options.includeInvalid, 'Boolean')) {
throw new TypeError('options.includeInvalid must be a Boolean');
} else if (!isType(options.refPreProcessor, 'Undefined') &&
!isType(options.refPreProcessor, 'Function')) {
throw new TypeError('options.refPreProcessor must be a Function');
} else if (!isType(options.refPostProcessor, 'Undefined') &&
!isType(options.refPostProcessor, 'Function')) {
throw new TypeError('options.refPostProcessor must be a Function');
} else if (!isType(options.subDocPath, 'Undefined') &&
!isType(options.subDocPath, 'Array') &&
!isPtr(options.subDocPath)) {
// If a pointer is provided, throw an error if it's not the proper type
throw new TypeError('options.subDocPath must be an Array of path segments or a valid JSON Pointer');
}
options.filter = makeRefFilter(options);
// Set the subDocPath to avoid everyone else having to compute it
options.subDocPath = makeSubDocPath(options);
if (!isType(obj, 'Undefined')) {
try {
findValue(obj, options.subDocPath);
} catch (err) {
err.message = err.message.replace('JSON Pointer', 'options.subDocPath');
throw err;
}
}
return options;
}

@@ -735,7 +796,3 @@

function findRefs (obj, options) {
var ancestors = [];
var fromObj = obj;
var fromPath;
var refs = {};
var refFilter;

@@ -747,54 +804,39 @@ // Validate the provided document

// Set default for options
if (isType(options, 'Undefined')) {
options = {};
}
// Validate options
validateOptions(options);
options = validateOptions(options, obj);
// Convert from to a pointer
fromPath = makeSubDocPath(options);
// Convert options.filter from an Array/String to a Function
refFilter = makeRefFilter(options);
if (fromPath.length > 0) {
ancestors = findAncestors(obj, fromPath);
fromObj = findValue(obj, fromPath);
}
// Walk the document (or sub document) and find all JSON References
walk(ancestors, fromObj, fromPath, function (ancestors, node, path) {
var processChildren = true;
var refDetails;
walk(findAncestors(obj, options.subDocPath),
findValue(obj, options.subDocPath),
clone(options.subDocPath),
function (ancestors, node, path) {
var processChildren = true;
var refDetails;
if (isRefLike(node)) {
// Pre-process the node when necessary
if (!isType(options.refPreProcessor, 'Undefined')) {
node = options.refPreProcessor(clone(node), path);
}
if (isRefLike(node)) {
// Pre-process the node when necessary
if (!isType(options.refPreProcessor, 'Undefined')) {
node = options.refPreProcessor(clone(node), path);
}
refDetails = getRefDetails(node);
refDetails = getRefDetails(node);
if (refDetails.type !== 'invalid' || options.includeInvalid === true) {
if (refFilter(refDetails, path) === true) {
// Post-process the reference details when necessary
if (!isType(options.refPostProcessor, 'Undefined')) {
refDetails = options.refPostProcessor(clone(refDetails), path);
}
// Post-process the reference details
if (!isType(options.refPostProcessor, 'Undefined')) {
refDetails = options.refPostProcessor(refDetails, path);
}
refs[pathToPtr(path)] = refDetails;
}
if (options.filter(refDetails, path)) {
refs[pathToPtr(path)] = refDetails;
}
// Whenever a JSON Reference has extra children, its children should be ignored so we want to stop processing.
// See: http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03#section-3
if (getExtraRefKeys(node).length > 0) {
processChildren = false;
}
}
}
// Whenever a JSON Reference has extra children, its children should not be processed.
// See: http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03#section-3
if (getExtraRefKeys(node).length > 0) {
processChildren = false;
}
}
return processChildren;
});
return processChildren;
});

@@ -844,10 +886,5 @@ return refs;

// Set default for options
if (isType(options, 'Undefined')) {
options = {};
}
// Validate options
options = validateOptions(options);
// Validate options (Doing this here for a quick)
validateOptions(options);
// Combine the location and the optional relative base

@@ -860,7 +897,6 @@ location = combineURIs(options.relativeBase, location);

var cacheEntry = clone(remoteCache[location]);
var cOptions;
var cOptions = clone(options);
var uriDetails = parseURI(location);
if (isType(cacheEntry.refs, 'Undefined')) {
cOptions = clone(options);
// Do not filter any references so the cache is complete

@@ -875,6 +911,18 @@ delete cOptions.filter;

// Filter out the references based on options.filter and options.subDocPath
cacheEntry.refs = filterRefs(options, remoteCache[location].refs);
// Add the filter options back
if (!isType(options.filter, 'Undefined')) {
cOptions.filter = options.filter;
}
return cacheEntry;
if (!isType(uriDetails.fragment, 'Undefined')) {
cOptions.subDocPath = pathFromPtr(decodeURI(uriDetails.fragment));
} else if (!isType(uriDetails.subDocPath, 'Undefined')) {
cOptions.subDocPath = options.subDocPath;
}
// This will use the cache so don't worry about calling it twice
return {
refs: findRefs(res, cOptions),
value: res
};
});

@@ -1124,51 +1172,49 @@

// Set default for options
if (isType(options, 'Undefined')) {
options = {};
}
// Validate options
validateOptions(options);
options = validateOptions(options, obj);
})
.then(function () {
// Find all references recursively
return findAllRefs(obj, options, [], [], {});
return findRefsRecursive(obj, options, [], [], {
documents: {},
refs: {}
});
})
.then(function (aRefs) {
var cloned = clone(obj);
var parentLocations = [];
.then(function (allRefs) {
var resolved = clone(obj);
var deferredRefs = {};
var refs = {};
// Replace remote references first
Object.keys(aRefs).forEach(function (refPtr) {
var ptrPath = pathFromPtr(refPtr);
var refDetails = aRefs[refPtr];
var value;
function pathSorter (p1, p2) {
return pathFromPtr(p1).length - pathFromPtr(p2).length;
}
if (remoteTypes.indexOf(refDetails.type) > -1) {
if (isType(refDetails.error, 'Undefined') && refDetails.type !== 'invalid') {
try {
value = findValue(refDetails.value || {},
refDetails.uriDetails.fragment ?
pathFromPtr(refDetails.uriDetails.fragment) :
[]);
// Resolve all references with a known value
Object.keys(allRefs.refs).sort(pathSorter).forEach(function (refPtr) {
var refDetails = allRefs.refs[refPtr];
if (ptrPath.length === 0) {
cloned = value;
} else {
setValue(cloned, pathFromPtr(refPtr), value);
}
// Record all direct references
if (!refDetails.indirect) {
refs[refPtr] = refDetails;
}
// The reference includes a fragment so update the reference details
if (!isType(refDetails.value, 'Undefined')) {
refDetails.value = value;
} else if (refDetails.circular) {
// If there is no value and it's circular, set its value to an empty value
refDetails.value = {};
}
} catch (err) {
refDetails.error = err.message;
refDetails.missing = true;
// Delete helper property
delete refDetails.indirect;
if (isType(refDetails.error, 'Undefined') && refDetails.type !== 'invalid') {
if (isType(refDetails.value, 'Undefined') && refDetails.circular) {
refDetails.value = refDetails.def;
}
// We defer processing all references without a value until later
if (isType(refDetails.value, 'Undefined')) {
deferredRefs[refPtr] = refDetails;
} else {
if (refPtr === '#') {
resolved = refDetails.value;
} else {
setValue(resolved, pathFromPtr(refPtr), refDetails.value);
}
} else {
refDetails.missing = true;
// Delete helper property
delete refDetails.ancestorPtrs;
}

@@ -1178,62 +1224,32 @@ }

// Replace local references
Object.keys(aRefs).forEach(function (refPtr) {
var refDetails = aRefs[refPtr];
var refPath = pathFromPtr(refPtr);
var parentLocation = refDetails.parentLocation;
var value;
// Resolve all deferred references
Object.keys(deferredRefs).forEach(function (refPtr) {
var refDetails = deferredRefs[refPtr];
// Record that this reference has parent location details so we can clean it up later
if (!isType(parentLocation, 'Undefined') && parentLocations.indexOf(refPtr) === -1) {
parentLocations.push(refPtr);
}
// Attempt to resolve the value against all if its ancestors in order
refDetails.ancestorPtrs.forEach(function (ancestorPtr, index) {
if (isType(refDetails.value, 'Undefined')) {
try {
refDetails.value = clone(findValue(allRefs.documents[ancestorPtr], pathFromPtr(refDetails.uri)));
if (remoteTypes.indexOf(refDetails.type) === -1 && refDetails.type !== 'invalid') {
if (isType(refDetails.error, 'Undefined')) {
if (refPtr.indexOf(refDetails.uri + '/') > -1 || refPtr === refDetails.uri) {
refDetails.circular = true;
value = {};
} else {
if (!isType(parentLocation, 'Undefined')) {
// Attempt to get the referenced value from the remote document first
value = findValue(findValue(cloned, pathFromPtr(parentLocation)),
refDetails.uriDetails.fragment ?
pathFromPtr(refDetails.uriDetails.fragment) :
[], true);
}
}
// Delete helper property
delete refDetails.ancestorPtrs;
try {
if (isType(value, 'Undefined')) {
value = findValue(cloned,
refDetails.uriDetails.fragment ?
pathFromPtr(refDetails.uriDetails.fragment) :
[]);
}
setValue(resolved, pathFromPtr(refPtr), refDetails.value);
} catch (err) {
if (index === refDetails.ancestorPtrs.length - 1) {
refDetails.error = err.message;
refDetails.missing = true;
if (refPath.length === 0) {
cloned = value;
} else {
setValue(cloned, pathFromPtr(refPtr), value);
// Delete helper property
delete refDetails.ancestorPtrs;
}
refDetails.value = value;
} catch (err) {
refDetails.error = err.message;
refDetails.missing = true;
}
} else {
refDetails.missing = true;
}
}
});
});
// Remove all parentLocation values
parentLocations.forEach(function (refPtr) {
delete aRefs[refPtr].parentLocation;
});
return {
refs: aRefs,
resolved: cloned
refs: refs,
resolved: resolved
};

@@ -1284,10 +1300,5 @@ });

// Set default for options
if (isType(options, 'Undefined')) {
options = {};
}
// Validate options
options = validateOptions(options);
// Validate options (Doing this here for a quick)
validateOptions(options);
// Combine the location and the optional relative base

@@ -1300,3 +1311,9 @@ location = combineURIs(options.relativeBase, location);

var cOptions = clone(options);
var uriDetails = parseURI(location);
// Set the sub document path if necessary
if (!isType(uriDetails.fragment, 'Undefined')) {
cOptions.subDocPath = pathFromPtr(decodeURI(uriDetails.fragment));
}
// Update the relative base based on the retrieved location

@@ -1303,0 +1320,0 @@ cOptions.relativeBase = path.dirname(location);

{
"name": "json-refs",
"version": "2.1.2",
"version": "2.1.3",
"description": "Various utilities for JSON References (http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03).",

@@ -5,0 +5,0 @@ "main": "index.js",

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