Outlook_Addin_LLM/node_modules/reftools/lib/dereference.js

106 lines
4.0 KiB
JavaScript
Raw Permalink Normal View History

'use strict';
const recurse = require('./recurse.js').recurse;
const clone = require('./clone.js').shallowClone;
const jptr = require('./jptr.js').jptr;
const isRef = require('./isref.js').isRef;
var getLogger = function (options) {
if (options && options.verbose) {
return {
warn: function() {
var args = Array.prototype.slice.call(arguments);
console.warn.apply(console, args);
}
}
}
else {
return {
warn: function() {
//nop
}
}
}
}
/**
* dereferences the given object
* @param o the object to dereference
* @definitions a source of definitions to reference
* @options optional settings (used recursively)
* @return the dereferenced object
*/
function dereference(o,definitions,options) {
if (!options) options = {};
if (!options.cache) options.cache = {};
if (!options.state) options.state = {};
options.state.identityDetection = true;
// options.depth allows us to limit cloning to the first invocation
options.depth = (options.depth ? options.depth+1 : 1);
let obj = (options.depth > 1 ? o : clone(o));
let container = { data: obj };
let defs = (options.depth > 1 ? definitions : clone(definitions));
// options.master is the top level object, regardless of depth
if (!options.master) options.master = obj;
let logger = getLogger(options);
let changes = 1;
while (changes > 0) {
changes = 0;
recurse(container,options.state,function(obj,key,state){
if (isRef(obj,key)) {
let $ref = obj[key]; // immutable
changes++;
if (!options.cache[$ref]) {
let entry = {};
entry.path = state.path.split('/$ref')[0];
entry.key = $ref;
logger.warn('Dereffing %s at %s',$ref,entry.path);
entry.source = defs;
entry.data = jptr(entry.source,entry.key);
if (entry.data === false) {
entry.data = jptr(options.master,entry.key);
entry.source = options.master;
}
if (entry.data === false) {
logger.warn('Missing $ref target',entry.key);
}
options.cache[$ref] = entry;
entry.data = state.parent[state.pkey] = dereference(jptr(entry.source,entry.key),entry.source,options);
if (options.$ref && (typeof state.parent[state.pkey] === 'object') && (state.parent[state.pkey] !== null)) state.parent[state.pkey][options.$ref] = $ref;
entry.resolved = true;
}
else {
let entry = options.cache[$ref];
if (entry.resolved) {
// we have already seen and resolved this reference
logger.warn('Patching %s for %s',$ref,entry.path);
state.parent[state.pkey] = entry.data;
if (options.$ref && (typeof state.parent[state.pkey] === 'object') && (state.parent[state.pkey] !== null)) state.parent[state.pkey][options.$ref] = $ref;
}
else if ($ref === entry.path) {
// reference to itself, throw
throw new Error(`Tight circle at ${entry.path}`);
}
else {
// we're dealing with a circular reference here
logger.warn('Unresolved ref');
state.parent[state.pkey] = jptr(entry.source,entry.path);
if (state.parent[state.pkey] === false) {
state.parent[state.pkey] = jptr(entry.source,entry.key);
}
if (options.$ref && (typeof state.parent[state.pkey] === 'object') && (state.parent[state.pkey] !== null)) state.parent[options.$ref] = $ref;
}
}
}
});
}
return container.data;
}
module.exports = {
dereference : dereference
};