botframework-webchat-component
Version:
React component of botframework-webchat
87 lines (67 loc) • 2.85 kB
text/typescript
import deepFreeze from 'deep-freeze-strict';
import { type BlankNode } from './BlankNode';
import getSafeOwnPropertyNames from './getSafeOwnPropertyNames';
import isBlankNode from './isBlankNode';
import isUnconnectedBlankNode from './isUnconnectedBlankNode';
import visitOnce from './visitOnce';
function dereferenceBlankNodesInline(objects: object[]): void {
const blankNodeIdMap = new Map<string, BlankNode>();
const objectsToVisit1: any[] = [objects];
const shouldVisit1 = visitOnce<object>();
while (objectsToVisit1.length) {
const object = objectsToVisit1.shift();
if (!object) {
continue;
}
const indices = getSafeOwnPropertyNames(object);
for (const index of indices) {
// eslint-disable-next-line security/detect-object-injection
const value = object[index];
if (isBlankNode(value) && !isUnconnectedBlankNode(value)) {
blankNodeIdMap.set(value['@id'], value);
}
shouldVisit1(value) && objectsToVisit1.push(value);
}
}
const objectsToVisit2: unknown[] = [objects];
const shouldVisit2 = visitOnce<object>();
while (objectsToVisit2.length) {
const object = objectsToVisit2.shift();
if (!object) {
continue;
}
const indices = getSafeOwnPropertyNames(object);
for (const index of indices) {
// eslint-disable-next-line security/detect-object-injection
const value = object[index];
if (isBlankNode(value) && isUnconnectedBlankNode(value)) {
const blankNode = blankNodeIdMap.get(value['@id']);
if (blankNode) {
// eslint-disable-next-line security/detect-object-injection
object[index] = blankNode;
}
} else {
shouldVisit2(value) && objectsToVisit2.push(value);
}
}
}
}
/**
* Dereferences all unconnected blank nodes to their corresponding blank node. This is done by replacing all unconnected blank nodes in a graph and purposefully introduce cyclic dependencies to help querying the graph.
*
* This function will always return a new instance of all objects in the graph.
*
* This function assumes the graph conforms to JSON-LD, notably:
*
* - For nodes that share the same blank node identifier, one of them should be connected and the other must be unconnected
* - If none of them are connected node, these unconnected blank node will not be replaced
*
* @see https://json-ld.github.io/json-ld.org/spec/latest/json-ld/#data-model-overview
* @param graph A list of nodes in the graph.
* @returns A structured clone of graph with unconnected blank nodes replaced by their corresponding blank node.
*/
export default function dereferenceBlankNodes<T extends object>(graph: T[]): readonly T[] {
const nextObjects = structuredClone(graph);
dereferenceBlankNodesInline(nextObjects);
return deepFreeze(nextObjects);
}