Comparing version 0.6.3 to 0.7.0
@@ -24,38 +24,39 @@ /// <reference types="node" /> | ||
getPortName(port?: string): string; | ||
startTransaction(id: string, metadata?: JournalMetadata): void; | ||
endTransaction(id: string, metadata?: JournalMetadata): void; | ||
checkTransactionStart(): void; | ||
checkTransactionEnd(): void; | ||
setProperties(properties: PropertyMap): void; | ||
addInport(publicPort: string, nodeKey: GraphNodeID, portKey: string, metadata?: GraphNodeMetadata): void; | ||
removeInport(publicPort: string): void; | ||
renameInport(oldPort: string, newPort: string): void; | ||
setInportMetadata(publicPort: string, metadata: GraphNodeMetadata): void; | ||
addOutport(publicPort: string, nodeKey: GraphNodeID, portKey: string, metadata?: GraphNodeMetadata): void; | ||
removeOutport(publicPort: string): void; | ||
renameOutport(oldPort: string, newPort: string): void; | ||
setOutportMetadata(publicPort: string, metadata: GraphNodeMetadata): void; | ||
addGroup(group: string, nodes: Array<GraphNodeID>, metadata: GraphGroupMetadata): GraphGroup; | ||
renameGroup(oldName: string, newName: string): void; | ||
removeGroup(groupName: string): void; | ||
setGroupMetadata(groupName: string, metadata: GraphGroupMetadata): void; | ||
addNode(id: GraphNodeID, component: string, metadata?: GraphNodeMetadata): GraphNode; | ||
removeNode(id: GraphNodeID): void; | ||
startTransaction(id: string, metadata?: JournalMetadata): Graph; | ||
endTransaction(id: string, metadata?: JournalMetadata): Graph; | ||
checkTransactionStart(): Graph; | ||
checkTransactionEnd(): Graph; | ||
setProperties(properties: PropertyMap): Graph; | ||
addInport(publicPort: string, nodeKey: GraphNodeID, portKey: string, metadata?: GraphNodeMetadata): Graph; | ||
removeInport(publicPort: string): Graph; | ||
renameInport(oldPort: string, newPort: string): Graph; | ||
setInportMetadata(publicPort: string, metadata: GraphNodeMetadata): Graph; | ||
addOutport(publicPort: string, nodeKey: GraphNodeID, portKey: string, metadata?: GraphNodeMetadata): Graph; | ||
removeOutport(publicPort: string): Graph; | ||
renameOutport(oldPort: string, newPort: string): Graph; | ||
setOutportMetadata(publicPort: string, metadata: GraphNodeMetadata): Graph; | ||
addGroup(group: string, nodes: Array<GraphNodeID>, metadata: GraphGroupMetadata): Graph; | ||
renameGroup(oldName: string, newName: string): Graph; | ||
removeGroup(groupName: string): Graph; | ||
setGroupMetadata(groupName: string, metadata: GraphGroupMetadata): Graph; | ||
addNode(id: GraphNodeID, component: string, metadata?: GraphNodeMetadata): Graph; | ||
removeNode(id: GraphNodeID): Graph; | ||
getNode(id: GraphNodeID): GraphNode | null; | ||
renameNode(oldId: GraphNodeID, newId: GraphNodeID): void; | ||
setNodeMetadata(id: GraphNodeID, metadata: GraphNodeMetadata): void; | ||
addEdge(outNode: GraphNodeID, outPort: string, inNode: GraphNodeID, inPort: string, metadata?: GraphEdgeMetadata): GraphEdge | null; | ||
addEdgeIndex(outNode: GraphNodeID, outPort: string, outIndex: number | undefined, inNode: GraphNodeID, inPort: string, inIndex: number | undefined, metadata?: GraphEdgeMetadata): GraphEdge | null; | ||
removeEdge(node: GraphNodeID, port: string, node2: GraphNodeID, port2: string): void; | ||
renameNode(oldId: GraphNodeID, newId: GraphNodeID): Graph; | ||
setNodeMetadata(id: GraphNodeID, metadata: GraphNodeMetadata): Graph; | ||
addEdge(outNode: GraphNodeID, outPort: string, inNode: GraphNodeID, inPort: string, metadata?: GraphEdgeMetadata): Graph; | ||
addEdgeIndex(outNode: GraphNodeID, outPort: string, outIndex: number | undefined, inNode: GraphNodeID, inPort: string, inIndex: number | undefined, metadata?: GraphEdgeMetadata): Graph; | ||
removeEdge(node: GraphNodeID, port: string, node2: GraphNodeID, port2: string): Graph; | ||
getEdge(node: GraphNodeID, port: string, node2: GraphNodeID, port2: string): GraphEdge | null; | ||
setEdgeMetadata(node: GraphNodeID, port: string, node2: GraphNodeID, port2: string, metadata: GraphEdgeMetadata): void; | ||
addInitial(data: any, node: GraphNodeID, port: string, metadata?: GraphIIPMetadata): GraphIIP | null; | ||
addInitialIndex(data: any, node: GraphNodeID, port: string, index: number, metadata?: GraphIIPMetadata): GraphIIP | null; | ||
addGraphInitial(data: any, node: string, metadata?: GraphIIPMetadata): GraphIIP | null; | ||
addGraphInitialIndex(data: any, node: string, index: number, metadata?: GraphIIPMetadata): GraphIIP | null; | ||
removeInitial(node: GraphNodeID, port: string): void; | ||
removeGraphInitial(node: string): void; | ||
setEdgeMetadata(node: GraphNodeID, port: string, node2: GraphNodeID, port2: string, metadata: GraphEdgeMetadata): Graph; | ||
addInitial(data: any, node: GraphNodeID, port: string, metadata?: GraphIIPMetadata): Graph; | ||
addInitialIndex(data: any, node: GraphNodeID, port: string, index: number, metadata?: GraphIIPMetadata): Graph; | ||
addGraphInitial(data: any, node: string, metadata?: GraphIIPMetadata): Graph; | ||
addGraphInitialIndex(data: any, node: string, index: number, metadata?: GraphIIPMetadata): Graph; | ||
removeInitial(node: GraphNodeID, port: string): Graph; | ||
removeGraphInitial(node: string): Graph; | ||
toDOT(): string; | ||
toYUML(): string; | ||
toJSON(): GraphJson; | ||
save(file: string): Promise<string>; | ||
save(file: string, callback: (err: Error | null, filename?: string) => void): void; | ||
@@ -67,4 +68,7 @@ } | ||
} | ||
declare function loadJSON(passedDefinition: string | GraphJson): Promise<Graph>; | ||
declare function loadJSON(passedDefinition: string | GraphJson, callback: GraphLoadingCallback, metadata?: JournalMetadata): void; | ||
declare function loadFBP(fbpData: string, callback: GraphLoadingCallback, metadata?: JournalMetadata, caseSensitive?: boolean): void; | ||
declare function loadFBP(fbpData: string): Promise<Graph>; | ||
declare function loadFBP(fbpData: string, callback: GraphLoadingCallback, metadata?: JournalMetadata): void; | ||
declare function loadFile(file: string): Promise<Graph>; | ||
declare function loadFile(file: string, callback: GraphLoadingCallback, metadata?: JournalMetadata, caseSensitive?: boolean): void; | ||
@@ -71,0 +75,0 @@ declare function mergeResolveTheirsNaive(base: Graph, to: Graph): void; |
372
lib/Graph.js
@@ -14,2 +14,3 @@ "use strict"; | ||
const clone = require("clone"); | ||
const fs_1 = require("fs"); | ||
const Platform_1 = require("./Platform"); | ||
@@ -56,2 +57,3 @@ // This class represents an abstract FBP graph containing nodes | ||
this.emit('startTransaction', id, metadata); | ||
return this; | ||
} | ||
@@ -65,2 +67,3 @@ endTransaction(id, metadata = {}) { | ||
this.emit('endTransaction', id, metadata); | ||
return this; | ||
} | ||
@@ -74,2 +77,3 @@ checkTransactionStart() { | ||
} | ||
return this; | ||
} | ||
@@ -83,2 +87,3 @@ checkTransactionEnd() { | ||
} | ||
return this; | ||
} | ||
@@ -97,2 +102,3 @@ // ## Modifying Graph properties | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -102,3 +108,3 @@ addInport(publicPort, nodeKey, portKey, metadata = {}) { | ||
if (!this.getNode(nodeKey)) { | ||
return; | ||
return this; | ||
} | ||
@@ -114,2 +120,3 @@ const portName = this.getPortName(publicPort); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -119,3 +126,3 @@ removeInport(publicPort) { | ||
if (!this.inports[portName]) { | ||
return; | ||
return this; | ||
} | ||
@@ -128,2 +135,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -134,6 +142,6 @@ renameInport(oldPort, newPort) { | ||
if (!this.inports[oldPortName]) { | ||
return; | ||
return this; | ||
} | ||
if (newPortName === oldPortName) { | ||
return; | ||
return this; | ||
} | ||
@@ -145,2 +153,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -150,3 +159,3 @@ setInportMetadata(publicPort, metadata) { | ||
if (!this.inports[portName]) { | ||
return; | ||
return this; | ||
} | ||
@@ -173,2 +182,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -178,3 +188,3 @@ addOutport(publicPort, nodeKey, portKey, metadata = {}) { | ||
if (!this.getNode(nodeKey)) { | ||
return; | ||
return this; | ||
} | ||
@@ -190,2 +200,3 @@ const portName = this.getPortName(publicPort); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -195,3 +206,3 @@ removeOutport(publicPort) { | ||
if (!this.outports[portName]) { | ||
return; | ||
return this; | ||
} | ||
@@ -204,2 +215,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -210,3 +222,3 @@ renameOutport(oldPort, newPort) { | ||
if (!this.outports[oldPortName]) { | ||
return; | ||
return this; | ||
} | ||
@@ -218,2 +230,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -223,3 +236,3 @@ setOutportMetadata(publicPort, metadata) { | ||
if (!this.outports[portName]) { | ||
return; | ||
return this; | ||
} | ||
@@ -246,2 +259,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -260,3 +274,3 @@ // ## Grouping nodes in a graph | ||
this.checkTransactionEnd(); | ||
return g; | ||
return this; | ||
} | ||
@@ -277,2 +291,3 @@ renameGroup(oldName, newName) { | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -293,2 +308,3 @@ removeGroup(groupName) { | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -321,2 +337,3 @@ setGroupMetadata(groupName, metadata) { | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -346,3 +363,3 @@ // ## Adding a node to the graph | ||
this.checkTransactionEnd(); | ||
return node; | ||
return this; | ||
} | ||
@@ -361,3 +378,3 @@ // ## Removing a node from the graph | ||
if (!node) { | ||
return; | ||
return this; | ||
} | ||
@@ -405,2 +422,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -426,3 +444,3 @@ // ## Getting a node | ||
if (!node) { | ||
return; | ||
return this; | ||
} | ||
@@ -476,2 +494,3 @@ node.id = newId; | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -484,3 +503,3 @@ // ## Changing a node's metadata | ||
if (!node) { | ||
return; | ||
return this; | ||
} | ||
@@ -506,2 +525,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -530,9 +550,9 @@ // ## Connecting nodes | ||
})) { | ||
return null; | ||
return this; | ||
} | ||
if (!this.getNode(outNode)) { | ||
return null; | ||
return this; | ||
} | ||
if (!this.getNode(inNode)) { | ||
return null; | ||
return this; | ||
} | ||
@@ -554,3 +574,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return edge; | ||
return this; | ||
} | ||
@@ -575,9 +595,9 @@ // Adding an edge will emit the `addEdge` event. | ||
})) { | ||
return null; | ||
return this; | ||
} | ||
if (!this.getNode(outNode)) { | ||
return null; | ||
return this; | ||
} | ||
if (!this.getNode(inNode)) { | ||
return null; | ||
return this; | ||
} | ||
@@ -601,3 +621,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return edge; | ||
return this; | ||
} | ||
@@ -614,3 +634,3 @@ // ## Disconnected nodes | ||
if (!this.getEdge(node, port, node2, port2)) { | ||
return; | ||
return this; | ||
} | ||
@@ -640,2 +660,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -673,3 +694,3 @@ // ## Getting an edge | ||
if (!edge) { | ||
return; | ||
return this; | ||
} | ||
@@ -695,2 +716,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -718,3 +740,3 @@ // ## Adding Initial Information Packets | ||
if (!this.getNode(node)) { | ||
return null; | ||
return this; | ||
} | ||
@@ -736,7 +758,7 @@ const portName = this.getPortName(port); | ||
this.checkTransactionEnd(); | ||
return initializer; | ||
return this; | ||
} | ||
addInitialIndex(data, node, port, index, metadata = {}) { | ||
if (!this.getNode(node)) { | ||
return null; | ||
return this; | ||
} | ||
@@ -760,3 +782,3 @@ const indexVal = (index === null) ? undefined : index; | ||
this.checkTransactionEnd(); | ||
return initializer; | ||
return this; | ||
} | ||
@@ -766,3 +788,3 @@ addGraphInitial(data, node, metadata = {}) { | ||
if (!inport) { | ||
return null; | ||
return this; | ||
} | ||
@@ -774,3 +796,3 @@ return this.addInitial(data, inport.process, inport.port, metadata); | ||
if (!inport) { | ||
return null; | ||
return this; | ||
} | ||
@@ -803,2 +825,3 @@ return this.addInitialIndex(data, inport.process, inport.port, index, metadata); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -808,5 +831,6 @@ removeGraphInitial(node) { | ||
if (!inport) { | ||
return; | ||
return this; | ||
} | ||
this.removeInitial(inport.process, inport.port); | ||
return this; | ||
} | ||
@@ -938,19 +962,29 @@ toDOT() { | ||
save(file, callback) { | ||
let promise; | ||
if (Platform_1.isBrowser()) { | ||
callback(new Error('Saving graphs not supported on browser')); | ||
promise = Promise.reject(new Error('Saving graphs not supported on browser')); | ||
} | ||
else { | ||
promise = new Promise((resolve, reject) => { | ||
const json = JSON.stringify(this.toJSON(), null, 4); | ||
let filename = file; | ||
if (!filename.match(/\.json$/)) { | ||
filename = `${file}.json`; | ||
} | ||
fs_1.writeFile(filename, json, 'utf-8', (err) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
resolve(filename); | ||
}); | ||
}); | ||
} | ||
if (callback) { | ||
promise.then((filename) => { | ||
callback(null, filename); | ||
}, callback); | ||
return; | ||
} | ||
const json = JSON.stringify(this.toJSON(), null, 4); | ||
let filename = file; | ||
if (!filename.match(/\.json$/)) { | ||
filename = `${file}.json`; | ||
} | ||
// eslint-disable-next-line global-require | ||
require('fs').writeFile(filename, json, 'utf-8', (err) => { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
callback(null, filename); | ||
}); | ||
return promise; | ||
} | ||
@@ -965,150 +999,166 @@ } | ||
function loadJSON(passedDefinition, callback, metadata = {}) { | ||
let definition; | ||
if (typeof passedDefinition === 'string') { | ||
definition = JSON.parse(passedDefinition); | ||
} | ||
else { | ||
definition = clone(passedDefinition); | ||
} | ||
if (!definition.properties) { | ||
definition.properties = {}; | ||
} | ||
if (!definition.processes) { | ||
definition.processes = {}; | ||
} | ||
if (!definition.connections) { | ||
definition.connections = []; | ||
} | ||
const graph = new Graph(definition.properties.name, { | ||
caseSensitive: definition.caseSensitive || false, | ||
}); | ||
graph.startTransaction('loadJSON', metadata); | ||
const properties = {}; | ||
Object.keys(definition.properties).forEach((property) => { | ||
if (property === 'name') { | ||
return; | ||
const promise = new Promise((resolve) => { | ||
let definition; | ||
if (typeof passedDefinition === 'string') { | ||
definition = JSON.parse(passedDefinition); | ||
} | ||
else { | ||
definition = clone(passedDefinition); | ||
} | ||
if (!definition.properties) { | ||
return; | ||
definition.properties = {}; | ||
} | ||
const value = definition.properties[property]; | ||
properties[property] = value; | ||
}); | ||
graph.setProperties(properties); | ||
Object.keys(definition.processes).forEach((id) => { | ||
if (!definition.processes) { | ||
return; | ||
definition.processes = {}; | ||
} | ||
const def = definition.processes[id]; | ||
if (!def.metadata) { | ||
def.metadata = {}; | ||
if (!definition.connections) { | ||
definition.connections = []; | ||
} | ||
graph.addNode(id, def.component, def.metadata); | ||
}); | ||
definition.connections.forEach((conn) => { | ||
const meta = conn.metadata ? conn.metadata : {}; | ||
if (typeof conn.data !== 'undefined') { | ||
if (typeof conn.tgt.index === 'number') { | ||
graph.addInitialIndex(conn.data, conn.tgt.process, graph.getPortName(conn.tgt.port), conn.tgt.index, meta); | ||
const graph = new Graph(definition.properties.name, { | ||
caseSensitive: definition.caseSensitive || false, | ||
}); | ||
graph.startTransaction('loadJSON', metadata); | ||
const properties = {}; | ||
Object.keys(definition.properties).forEach((property) => { | ||
if (property === 'name') { | ||
return; | ||
} | ||
else { | ||
graph.addInitial(conn.data, conn.tgt.process, graph.getPortName(conn.tgt.port), meta); | ||
if (!definition.properties) { | ||
return; | ||
} | ||
return; | ||
} | ||
if (typeof conn.src === 'undefined') { | ||
return; | ||
} | ||
if ((typeof conn.src.index === 'number') || (typeof conn.tgt.index === 'number')) { | ||
graph.addEdgeIndex(conn.src.process, graph.getPortName(conn.src.port), conn.src.index, conn.tgt.process, graph.getPortName(conn.tgt.port), conn.tgt.index, meta); | ||
return; | ||
} | ||
graph.addEdge(conn.src.process, graph.getPortName(conn.src.port), conn.tgt.process, graph.getPortName(conn.tgt.port), meta); | ||
}); | ||
if (definition.inports) { | ||
Object.keys(definition.inports).forEach((pub) => { | ||
if (!definition.inports || !definition.inports[pub]) { | ||
const value = definition.properties[property]; | ||
properties[property] = value; | ||
}); | ||
graph.setProperties(properties); | ||
Object.keys(definition.processes).forEach((id) => { | ||
if (!definition.processes) { | ||
return; | ||
} | ||
const priv = definition.inports[pub]; | ||
graph.addInport(pub, priv.process, graph.getPortName(priv.port), priv.metadata || {}); | ||
const def = definition.processes[id]; | ||
if (!def.metadata) { | ||
def.metadata = {}; | ||
} | ||
graph.addNode(id, def.component, def.metadata); | ||
}); | ||
} | ||
if (definition.outports) { | ||
Object.keys(definition.outports).forEach((pub) => { | ||
if (!definition.outports || !definition.outports[pub]) { | ||
definition.connections.forEach((conn) => { | ||
const meta = conn.metadata ? conn.metadata : {}; | ||
if (typeof conn.data !== 'undefined') { | ||
if (typeof conn.tgt.index === 'number') { | ||
graph.addInitialIndex(conn.data, conn.tgt.process, graph.getPortName(conn.tgt.port), conn.tgt.index, meta); | ||
} | ||
else { | ||
graph.addInitial(conn.data, conn.tgt.process, graph.getPortName(conn.tgt.port), meta); | ||
} | ||
return; | ||
} | ||
const priv = definition.outports[pub]; | ||
graph.addOutport(pub, priv.process, graph.getPortName(priv.port), priv.metadata || {}); | ||
if (typeof conn.src === 'undefined') { | ||
return; | ||
} | ||
if ((typeof conn.src.index === 'number') || (typeof conn.tgt.index === 'number')) { | ||
graph.addEdgeIndex(conn.src.process, graph.getPortName(conn.src.port), conn.src.index, conn.tgt.process, graph.getPortName(conn.tgt.port), conn.tgt.index, meta); | ||
return; | ||
} | ||
graph.addEdge(conn.src.process, graph.getPortName(conn.src.port), conn.tgt.process, graph.getPortName(conn.tgt.port), meta); | ||
}); | ||
if (definition.inports) { | ||
Object.keys(definition.inports).forEach((pub) => { | ||
if (!definition.inports || !definition.inports[pub]) { | ||
return; | ||
} | ||
const priv = definition.inports[pub]; | ||
graph.addInport(pub, priv.process, graph.getPortName(priv.port), priv.metadata || {}); | ||
}); | ||
} | ||
if (definition.outports) { | ||
Object.keys(definition.outports).forEach((pub) => { | ||
if (!definition.outports || !definition.outports[pub]) { | ||
return; | ||
} | ||
const priv = definition.outports[pub]; | ||
graph.addOutport(pub, priv.process, graph.getPortName(priv.port), priv.metadata || {}); | ||
}); | ||
} | ||
if (definition.groups) { | ||
definition.groups.forEach((group) => { | ||
graph.addGroup(group.name, group.nodes, group.metadata || {}); | ||
}); | ||
} | ||
graph.endTransaction('loadJSON'); | ||
resolve(graph); | ||
}); | ||
if (callback) { | ||
promise.then((graph) => { | ||
callback(null, graph); | ||
}, callback); | ||
} | ||
if (definition.groups) { | ||
definition.groups.forEach((group) => { | ||
graph.addGroup(group.name, group.nodes, group.metadata || {}); | ||
}); | ||
} | ||
graph.endTransaction('loadJSON'); | ||
return callback(null, graph); | ||
return promise; | ||
} | ||
exports.loadJSON = loadJSON; | ||
function loadFBP(fbpData, callback, metadata = {}, caseSensitive = false) { | ||
let definition; | ||
try { | ||
const promise = new Promise((resolve) => { | ||
// eslint-disable-next-line global-require | ||
definition = require('fbp').parse(fbpData, { caseSensitive }); | ||
resolve(require('fbp').parse(fbpData, { caseSensitive })); | ||
}) | ||
.then((def) => loadJSON(def)); | ||
if (callback) { | ||
promise.then((graph) => { | ||
callback(null, graph); | ||
}, callback); | ||
} | ||
catch (e) { | ||
return callback(e); | ||
} | ||
return loadJSON(definition, callback, metadata); | ||
return promise; | ||
} | ||
exports.loadFBP = loadFBP; | ||
function loadHTTP(url, callback) { | ||
const req = new XMLHttpRequest(); | ||
req.onreadystatechange = () => { | ||
if (req.readyState !== 4) { | ||
return; | ||
} | ||
if (req.status !== 200) { | ||
callback(new Error(`Failed to load ${url}: HTTP ${req.status}`)); | ||
} | ||
callback(null, req.responseText); | ||
}; | ||
req.open('GET', url, true); | ||
req.send(); | ||
const promise = new Promise((resolve, reject) => { | ||
const req = new XMLHttpRequest(); | ||
req.onreadystatechange = () => { | ||
if (req.readyState !== 4) { | ||
return; | ||
} | ||
if (req.status !== 200) { | ||
reject(new Error(`Failed to load ${url}: HTTP ${req.status}`)); | ||
return; | ||
} | ||
resolve(req.responseText); | ||
}; | ||
req.open('GET', url, true); | ||
req.send(); | ||
}); | ||
if (callback) { | ||
promise.then((content) => { | ||
callback(null, content); | ||
}, callback); | ||
} | ||
return promise; | ||
} | ||
function loadFile(file, callback, metadata = {}, caseSensitive = false) { | ||
let ioPromise; | ||
if (Platform_1.isBrowser()) { | ||
// On browser we can try getting the file via AJAX | ||
loadHTTP(file, (err, data) => { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
if (!data) { | ||
callback(new Error('No data received')); | ||
return; | ||
} | ||
if (file.split('.').pop() === 'fbp') { | ||
loadFBP(data, callback, metadata); | ||
return; | ||
} | ||
loadJSON(data, callback, metadata); | ||
ioPromise = loadHTTP(file); | ||
} | ||
else { | ||
ioPromise = new Promise((resolve, reject) => { | ||
// Node.js graph file | ||
fs_1.readFile(file, 'utf-8', (err, data) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
resolve(data); | ||
}); | ||
}); | ||
return; | ||
} | ||
// Node.js graph file | ||
// eslint-disable-next-line global-require | ||
require('fs').readFile(file, 'utf-8', (err, data) => { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
const promise = ioPromise.then((content) => { | ||
if (file.split('.').pop() === 'fbp') { | ||
loadFBP(data, callback, {}, caseSensitive); | ||
return; | ||
return loadFBP(content); | ||
} | ||
loadJSON(data, callback, {}); | ||
return loadJSON(content); | ||
}); | ||
if (callback) { | ||
promise.then((content) => { | ||
callback(null, content); | ||
}, callback); | ||
} | ||
return promise; | ||
} | ||
@@ -1115,0 +1165,0 @@ exports.loadFile = loadFile; |
@@ -26,4 +26,5 @@ /// <reference types="node" /> | ||
toJSON(startRev?: number, endRevParam?: null): string[]; | ||
save(file: string): Promise<void>; | ||
save(file: string, callback: (err: NodeJS.ErrnoException | null) => void): void; | ||
} | ||
export { Journal, JournalStore, MemoryJournalStore, }; |
@@ -503,6 +503,20 @@ "use strict"; | ||
save(file, callback) { | ||
const json = JSON.stringify(this.toJSON(), null, 4); | ||
const { writeFile } = require('fs'); | ||
// eslint-disable-next-line global-require | ||
writeFile(`${file}.json`, json, 'utf-8', callback); | ||
const promise = new Promise((resolve, reject) => { | ||
const json = JSON.stringify(this.toJSON(), null, 4); | ||
const { writeFile } = require('fs'); | ||
writeFile(`${file}.json`, json, 'utf-8', (err) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
resolve(); | ||
}); | ||
}); | ||
if (callback) { | ||
promise.then(() => { | ||
callback(null); | ||
}, callback); | ||
return; | ||
} | ||
return promise; | ||
} | ||
@@ -509,0 +523,0 @@ } |
{ | ||
"name": "fbp-graph", | ||
"version": "0.6.3", | ||
"version": "0.7.0", | ||
"description": "JavaScript FBP graph library", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -30,2 +30,5 @@ FBP Graph library for JavaScript | ||
* 0.7.0 (December 08th 2020) | ||
- All graph modification methods are now chainable, allowing you to do things like `graph.addNode().addEdge().toJSON()` | ||
- Graph I/O methods (like `loadFile` and `save`) now return Promises in case no callback is supplied | ||
* 0.6.3 (December 03rd 2020) | ||
@@ -32,0 +35,0 @@ - Fixed exporting of base Graph and Journal types in index |
@@ -264,19 +264,11 @@ "use strict"; | ||
let g; | ||
it('should produce a Graph when input is string', (done) => lib.graph.loadJSON(jsonString, (err, instance) => { | ||
if (err) { | ||
done(err); | ||
return; | ||
} | ||
it('should produce a Graph when input is string', () => lib.graph.loadJSON(jsonString) | ||
.then((instance) => { | ||
g = instance; | ||
chai.expect(g).to.be.an('object'); | ||
done(); | ||
})); | ||
it('should produce a Graph when input is json', (done) => lib.graph.loadJSON(json, (err, instance) => { | ||
if (err) { | ||
done(err); | ||
return; | ||
} | ||
it('should produce a Graph when input is JSON', () => lib.graph.loadJSON(json) | ||
.then((instance) => { | ||
g = instance; | ||
chai.expect(g).to.be.an('object'); | ||
done(); | ||
})); | ||
@@ -703,20 +695,33 @@ it('should not mutate the inputted json object', (done) => { | ||
}); | ||
it('should be possible to save a graph to a file', (done) => { | ||
it('should be possible to save a graph to a file', () => { | ||
const g = new lib.graph.Graph(); | ||
g.addNode('Foo', 'Bar'); | ||
originalGraph = g.toJSON(); | ||
return g.save(graphPath); | ||
}); | ||
it('should be possible to save a graph to a file with a callback', (done) => { | ||
const g = new lib.graph.Graph(); | ||
g.addNode('Foo', 'Bar'); | ||
originalGraph = g.toJSON(); | ||
g.save(graphPath, done); | ||
}); | ||
it('should be possible to load a graph from a file', (done) => lib.graph.loadFile(graphPath, (err, g) => { | ||
if (err) { | ||
done(err); | ||
return; | ||
} | ||
if (!g) { | ||
done(new Error('No graph')); | ||
return; | ||
} | ||
it('should be possible to load a graph from a file', () => lib.graph.loadFile(graphPath) | ||
.then((g) => { | ||
chai.expect(g).to.be.an('object'); | ||
chai.expect(g.toJSON()).to.eql(originalGraph); | ||
done(); | ||
})); | ||
it('should be possible to load a graph from a file with a callback', (done) => { | ||
lib.graph.loadFile(graphPath, (err, g) => { | ||
if (err) { | ||
done(err); | ||
return; | ||
} | ||
if (!g) { | ||
done(new Error('No graph')); | ||
return; | ||
} | ||
chai.expect(g.toJSON()).to.eql(originalGraph); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
@@ -723,0 +728,0 @@ describe('without .json suffix', () => { |
@@ -267,21 +267,13 @@ import * as lib from '../lib/index'; | ||
it('should produce a Graph when input is string', (done) => lib.graph.loadJSON(jsonString, (err, instance) => { | ||
if (err) { | ||
done(err); | ||
return; | ||
} | ||
g = instance; | ||
chai.expect(g).to.be.an('object'); | ||
done(); | ||
})); | ||
it('should produce a Graph when input is string', () => lib.graph.loadJSON(jsonString) | ||
.then((instance) => { | ||
g = instance; | ||
chai.expect(g).to.be.an('object'); | ||
})); | ||
it('should produce a Graph when input is json', (done) => lib.graph.loadJSON(json, (err, instance) => { | ||
if (err) { | ||
done(err); | ||
return; | ||
} | ||
g = instance; | ||
chai.expect(g).to.be.an('object'); | ||
done(); | ||
})); | ||
it('should produce a Graph when input is JSON', () => lib.graph.loadJSON(json) | ||
.then((instance) => { | ||
g = instance; | ||
chai.expect(g).to.be.an('object'); | ||
})); | ||
@@ -697,20 +689,33 @@ it('should not mutate the inputted json object', (done) => { | ||
}); | ||
it('should be possible to save a graph to a file', (done) => { | ||
it('should be possible to save a graph to a file', () => { | ||
const g = new lib.graph.Graph(); | ||
g.addNode('Foo', 'Bar'); | ||
originalGraph = g.toJSON(); | ||
return g.save(graphPath); | ||
}); | ||
it('should be possible to save a graph to a file with a callback', (done) => { | ||
const g = new lib.graph.Graph(); | ||
g.addNode('Foo', 'Bar'); | ||
originalGraph = g.toJSON(); | ||
g.save(graphPath, done); | ||
}); | ||
it('should be possible to load a graph from a file', (done) => lib.graph.loadFile(graphPath, (err, g) => { | ||
if (err) { | ||
done(err); | ||
return; | ||
} | ||
if (!g) { | ||
done(new Error('No graph')); | ||
return; | ||
} | ||
chai.expect(g.toJSON()).to.eql(originalGraph); | ||
done(); | ||
})); | ||
it('should be possible to load a graph from a file', () => lib.graph.loadFile(graphPath) | ||
.then((g) => { | ||
chai.expect(g).to.be.an('object'); | ||
chai.expect(g.toJSON()).to.eql(originalGraph); | ||
})); | ||
it('should be possible to load a graph from a file with a callback', (done) => { | ||
lib.graph.loadFile(graphPath, (err, g) => { | ||
if (err) { | ||
done(err); | ||
return; | ||
} | ||
if (!g) { | ||
done(new Error('No graph')); | ||
return; | ||
} | ||
chai.expect(g.toJSON()).to.eql(originalGraph); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
@@ -717,0 +722,0 @@ describe('without .json suffix', () => { |
514
src/Graph.ts
@@ -11,2 +11,3 @@ // FBP Graph | ||
import * as clone from 'clone'; | ||
import { writeFile, readFile } from 'fs'; | ||
import { isBrowser } from './Platform'; | ||
@@ -90,3 +91,3 @@ import { | ||
// the graph API will implicitly create a transaction for that change | ||
startTransaction(id: string, metadata: JournalMetadata = {}) { | ||
startTransaction(id: string, metadata: JournalMetadata = {}): Graph { | ||
if (this.transaction.id) { | ||
@@ -99,5 +100,6 @@ throw Error('Nested transactions not supported'); | ||
this.emit('startTransaction', id, metadata); | ||
return this; | ||
} | ||
endTransaction(id: string, metadata: JournalMetadata = {}) { | ||
endTransaction(id: string, metadata: JournalMetadata = {}): Graph { | ||
if (!this.transaction.id) { | ||
@@ -110,5 +112,6 @@ throw Error('Attempted to end non-existing transaction'); | ||
this.emit('endTransaction', id, metadata); | ||
return this; | ||
} | ||
checkTransactionStart() { | ||
checkTransactionStart(): Graph { | ||
if (!this.transaction.id) { | ||
@@ -119,5 +122,6 @@ this.startTransaction('implicit'); | ||
} | ||
return this; | ||
} | ||
checkTransactionEnd() { | ||
checkTransactionEnd(): Graph { | ||
if (this.transaction.id === 'implicit') { | ||
@@ -129,2 +133,3 @@ this.transaction.depth -= 1; | ||
} | ||
return this; | ||
} | ||
@@ -135,3 +140,3 @@ | ||
// This method allows changing properties of the graph. | ||
setProperties(properties: PropertyMap) { | ||
setProperties(properties: PropertyMap): Graph { | ||
this.checkTransactionStart(); | ||
@@ -145,7 +150,10 @@ const before = clone(this.properties); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
addInport(publicPort: string, nodeKey: GraphNodeID, portKey: string, metadata: GraphNodeMetadata = {}) { | ||
addInport(publicPort: string, nodeKey: GraphNodeID, portKey: string, metadata: GraphNodeMetadata = {}): Graph { | ||
// Check that node exists | ||
if (!this.getNode(nodeKey)) { return; } | ||
if (!this.getNode(nodeKey)) { | ||
return this; | ||
} | ||
@@ -161,7 +169,10 @@ const portName = this.getPortName(publicPort); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
removeInport(publicPort: string) { | ||
removeInport(publicPort: string): Graph { | ||
const portName = this.getPortName(publicPort); | ||
if (!this.inports[portName]) { return; } | ||
if (!this.inports[portName]) { | ||
return this; | ||
} | ||
@@ -174,9 +185,14 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
renameInport(oldPort: string, newPort: string) { | ||
renameInport(oldPort: string, newPort: string): Graph { | ||
const oldPortName = this.getPortName(oldPort); | ||
const newPortName = this.getPortName(newPort); | ||
if (!this.inports[oldPortName]) { return; } | ||
if (newPortName === oldPortName) { return; } | ||
if (!this.inports[oldPortName]) { | ||
return this; | ||
} | ||
if (newPortName === oldPortName) { | ||
return this; | ||
} | ||
@@ -188,7 +204,10 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
setInportMetadata(publicPort:string, metadata: GraphNodeMetadata) { | ||
setInportMetadata(publicPort:string, metadata: GraphNodeMetadata): Graph { | ||
const portName = this.getPortName(publicPort); | ||
if (!this.inports[portName]) { return; } | ||
if (!this.inports[portName]) { | ||
return this; | ||
} | ||
@@ -214,7 +233,10 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
addOutport(publicPort: string, nodeKey: GraphNodeID, portKey: string, metadata: GraphNodeMetadata = {}) { | ||
addOutport(publicPort: string, nodeKey: GraphNodeID, portKey: string, metadata: GraphNodeMetadata = {}): Graph { | ||
// Check that node exists | ||
if (!this.getNode(nodeKey)) { return; } | ||
if (!this.getNode(nodeKey)) { | ||
return this; | ||
} | ||
@@ -231,7 +253,10 @@ const portName = this.getPortName(publicPort); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
removeOutport(publicPort: string) { | ||
removeOutport(publicPort: string): Graph { | ||
const portName = this.getPortName(publicPort); | ||
if (!this.outports[portName]) { return; } | ||
if (!this.outports[portName]) { | ||
return this; | ||
} | ||
@@ -246,8 +271,11 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
renameOutport(oldPort: string, newPort: string) { | ||
renameOutport(oldPort: string, newPort: string): Graph { | ||
const oldPortName = this.getPortName(oldPort); | ||
const newPortName = this.getPortName(newPort); | ||
if (!this.outports[oldPortName]) { return; } | ||
if (!this.outports[oldPortName]) { | ||
return this; | ||
} | ||
@@ -259,7 +287,10 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
setOutportMetadata(publicPort: string, metadata: GraphNodeMetadata) { | ||
setOutportMetadata(publicPort: string, metadata: GraphNodeMetadata): Graph { | ||
const portName = this.getPortName(publicPort); | ||
if (!this.outports[portName]) { return; } | ||
if (!this.outports[portName]) { | ||
return this; | ||
} | ||
@@ -285,2 +316,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -290,3 +322,3 @@ | ||
// | ||
addGroup(group: string, nodes: Array<GraphNodeID>, metadata: GraphGroupMetadata): GraphGroup { | ||
addGroup(group: string, nodes: Array<GraphNodeID>, metadata: GraphGroupMetadata): Graph { | ||
this.checkTransactionStart(); | ||
@@ -304,6 +336,6 @@ | ||
return g; | ||
return this; | ||
} | ||
renameGroup(oldName: string, newName: string) { | ||
renameGroup(oldName: string, newName: string): Graph { | ||
this.checkTransactionStart(); | ||
@@ -318,5 +350,6 @@ this.groups.forEach((group) => { | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
removeGroup(groupName: string) { | ||
removeGroup(groupName: string): Graph { | ||
this.checkTransactionStart(); | ||
@@ -331,5 +364,6 @@ this.groups = this.groups.filter((group) => { | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
setGroupMetadata(groupName: string, metadata: GraphGroupMetadata) { | ||
setGroupMetadata(groupName: string, metadata: GraphGroupMetadata): Graph { | ||
this.checkTransactionStart(); | ||
@@ -355,2 +389,3 @@ this.groups.forEach((group) => { | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -371,3 +406,3 @@ | ||
// Addition of a node will emit the `addNode` event. | ||
addNode(id: GraphNodeID, component: string, metadata: GraphNodeMetadata = {}): GraphNode { | ||
addNode(id: GraphNodeID, component: string, metadata: GraphNodeMetadata = {}): Graph { | ||
this.checkTransactionStart(); | ||
@@ -383,3 +418,3 @@ const node = { | ||
this.checkTransactionEnd(); | ||
return node; | ||
return this; | ||
} | ||
@@ -396,6 +431,6 @@ | ||
// emitted. | ||
removeNode(id: GraphNodeID) { | ||
removeNode(id: GraphNodeID): Graph { | ||
const node = this.getNode(id); | ||
if (!node) { | ||
return; | ||
return this; | ||
} | ||
@@ -445,2 +480,3 @@ | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -464,7 +500,9 @@ | ||
// Nodes IDs can be changed by calling this method. | ||
renameNode(oldId: GraphNodeID, newId: GraphNodeID) { | ||
renameNode(oldId: GraphNodeID, newId: GraphNodeID): Graph { | ||
this.checkTransactionStart(); | ||
const node = this.getNode(oldId); | ||
if (!node) { return; } | ||
if (!node) { | ||
return this; | ||
} | ||
node.id = newId; | ||
@@ -514,2 +552,3 @@ | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -520,5 +559,7 @@ | ||
// Node metadata can be set or changed by calling this method. | ||
setNodeMetadata(id: GraphNodeID, metadata: GraphNodeMetadata) { | ||
setNodeMetadata(id: GraphNodeID, metadata: GraphNodeMetadata): Graph { | ||
const node = this.getNode(id); | ||
if (!node) { return; } | ||
if (!node) { | ||
return this; | ||
} | ||
@@ -546,2 +587,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -558,3 +600,3 @@ | ||
// Adding an edge will emit the `addEdge` event. | ||
addEdge(outNode: GraphNodeID, outPort: string, inNode: GraphNodeID, inPort: string, metadata: GraphEdgeMetadata = {}): GraphEdge | null { | ||
addEdge(outNode: GraphNodeID, outPort: string, inNode: GraphNodeID, inPort: string, metadata: GraphEdgeMetadata = {}): Graph { | ||
const outPortName = this.getPortName(outPort); | ||
@@ -572,9 +614,9 @@ const inPortName = this.getPortName(inPort); | ||
})) { | ||
return null; | ||
return this; | ||
} | ||
if (!this.getNode(outNode)) { | ||
return null; | ||
return this; | ||
} | ||
if (!this.getNode(inNode)) { | ||
return null; | ||
return this; | ||
} | ||
@@ -599,7 +641,7 @@ | ||
this.checkTransactionEnd(); | ||
return edge; | ||
return this; | ||
} | ||
// Adding an edge will emit the `addEdge` event. | ||
addEdgeIndex(outNode: GraphNodeID, outPort: string, outIndex: number | undefined, inNode: GraphNodeID, inPort: string, inIndex: number | undefined, metadata: GraphEdgeMetadata = {}): GraphEdge | null { | ||
addEdgeIndex(outNode: GraphNodeID, outPort: string, outIndex: number | undefined, inNode: GraphNodeID, inPort: string, inIndex: number | undefined, metadata: GraphEdgeMetadata = {}): Graph { | ||
const outPortName = this.getPortName(outPort); | ||
@@ -621,9 +663,9 @@ const inPortName = this.getPortName(inPort); | ||
})) { | ||
return null; | ||
return this; | ||
} | ||
if (!this.getNode(outNode)) { | ||
return null; | ||
return this; | ||
} | ||
if (!this.getNode(inNode)) { | ||
return null; | ||
return this; | ||
} | ||
@@ -650,3 +692,3 @@ | ||
this.checkTransactionEnd(); | ||
return edge; | ||
return this; | ||
} | ||
@@ -662,5 +704,5 @@ | ||
// Removing a connection will emit the `removeEdge` event. | ||
removeEdge(node: GraphNodeID, port: string, node2: GraphNodeID, port2: string) { | ||
removeEdge(node: GraphNodeID, port: string, node2: GraphNodeID, port2: string): Graph { | ||
if (!this.getEdge(node, port, node2, port2)) { | ||
return; | ||
return this; | ||
} | ||
@@ -690,2 +732,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -722,5 +765,7 @@ | ||
// Edge metadata can be set or changed by calling this method. | ||
setEdgeMetadata(node: GraphNodeID, port: string, node2: GraphNodeID, port2: string, metadata: GraphEdgeMetadata) { | ||
setEdgeMetadata(node: GraphNodeID, port: string, node2: GraphNodeID, port2: string, metadata: GraphEdgeMetadata): Graph { | ||
const edge = this.getEdge(node, port, node2, port2); | ||
if (!edge) { return; } | ||
if (!edge) { | ||
return this; | ||
} | ||
@@ -745,2 +790,3 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
@@ -767,5 +813,5 @@ | ||
// Adding an IIP will emit a `addInitial` event. | ||
addInitial(data: any, node: GraphNodeID, port: string, metadata: GraphIIPMetadata = {}): GraphIIP | null { | ||
addInitial(data: any, node: GraphNodeID, port: string, metadata: GraphIIPMetadata = {}): Graph { | ||
if (!this.getNode(node)) { | ||
return null; | ||
return this; | ||
} | ||
@@ -789,7 +835,9 @@ const portName = this.getPortName(port); | ||
this.checkTransactionEnd(); | ||
return initializer; | ||
return this; | ||
} | ||
addInitialIndex(data: any, node: GraphNodeID, port: string, index: number, metadata: GraphIIPMetadata = {}): GraphIIP | null { | ||
if (!this.getNode(node)) { return null; } | ||
addInitialIndex(data: any, node: GraphNodeID, port: string, index: number, metadata: GraphIIPMetadata = {}): Graph { | ||
if (!this.getNode(node)) { | ||
return this; | ||
} | ||
@@ -815,14 +863,14 @@ const indexVal = (index === null) ? undefined : index; | ||
this.checkTransactionEnd(); | ||
return initializer; | ||
return this; | ||
} | ||
addGraphInitial(data: any, node: string, metadata: GraphIIPMetadata = {}): GraphIIP | null { | ||
addGraphInitial(data: any, node: string, metadata: GraphIIPMetadata = {}): Graph { | ||
const inport = this.inports[node]; | ||
if (!inport) { return null; } | ||
if (!inport) { return this; } | ||
return this.addInitial(data, inport.process, inport.port, metadata); | ||
} | ||
addGraphInitialIndex(data: any, node: string, index: number, metadata: GraphIIPMetadata = {}): GraphIIP | null { | ||
addGraphInitialIndex(data: any, node: string, index: number, metadata: GraphIIPMetadata = {}): Graph { | ||
const inport = this.inports[node]; | ||
if (!inport) { return null; } | ||
if (!inport) { return this; } | ||
return this.addInitialIndex(data, inport.process, inport.port, index, metadata); | ||
@@ -844,3 +892,3 @@ } | ||
// Remove an IIP will emit a `removeInitial` event. | ||
removeInitial(node: GraphNodeID, port: string) { | ||
removeInitial(node: GraphNodeID, port: string): Graph { | ||
const portName = this.getPortName(port); | ||
@@ -858,8 +906,12 @@ this.checkTransactionStart(); | ||
this.checkTransactionEnd(); | ||
return this; | ||
} | ||
removeGraphInitial(node: string) { | ||
removeGraphInitial(node: string): Graph { | ||
const inport = this.inports[node]; | ||
if (!inport) { return; } | ||
if (!inport) { | ||
return this; | ||
} | ||
this.removeInitial(inport.process, inport.port); | ||
return this; | ||
} | ||
@@ -1006,21 +1058,31 @@ | ||
save(file: string, callback: (err: Error | null, filename?: string) => void) { | ||
save(file: string): Promise<string>; | ||
save(file: string, callback: (err: Error | null, filename?: string) => void): void; | ||
save(file: string, callback?: (err: Error | null, filename?: string) => void): void|Promise<string> { | ||
let promise; | ||
if (isBrowser()) { | ||
callback(new Error('Saving graphs not supported on browser')); | ||
promise = Promise.reject(new Error('Saving graphs not supported on browser')); | ||
} else { | ||
promise = new Promise<string>((resolve, reject) => { | ||
const json = JSON.stringify(this.toJSON(), null, 4); | ||
let filename = file; | ||
if (!filename.match(/\.json$/)) { | ||
filename = `${file}.json`; | ||
} | ||
writeFile(filename, json, 'utf-8', (err: Error | null) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
resolve(filename); | ||
}); | ||
}); | ||
} | ||
if (callback) { | ||
promise.then((filename) => { | ||
callback(null, filename); | ||
}, callback); | ||
return; | ||
} | ||
const json = JSON.stringify(this.toJSON(), null, 4); | ||
let filename = file; | ||
if (!filename.match(/\.json$/)) { | ||
filename = `${file}.json`; | ||
} | ||
// eslint-disable-next-line global-require | ||
require('fs').writeFile(filename, json, 'utf-8', (err: Error | null) => { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
callback(null, filename); | ||
}); | ||
return promise; | ||
} | ||
@@ -1040,47 +1102,73 @@ } | ||
function loadJSON(passedDefinition: string | GraphJson, callback: GraphLoadingCallback, metadata: JournalMetadata = {}) { | ||
let definition: GraphJson; | ||
if (typeof passedDefinition === 'string') { | ||
definition = JSON.parse(passedDefinition); | ||
} else { | ||
definition = clone(passedDefinition); | ||
} | ||
function loadJSON(passedDefinition: string | GraphJson): Promise<Graph>; | ||
function loadJSON(passedDefinition: string | GraphJson, callback: GraphLoadingCallback, metadata?: JournalMetadata): void; | ||
function loadJSON(passedDefinition: string | GraphJson, callback?: GraphLoadingCallback, metadata: JournalMetadata = {}): void|Promise<Graph> { | ||
const promise = new Promise<Graph>((resolve) => { | ||
let definition: GraphJson; | ||
if (typeof passedDefinition === 'string') { | ||
definition = JSON.parse(passedDefinition); | ||
} else { | ||
definition = clone(passedDefinition); | ||
} | ||
if (!definition.properties) { definition.properties = {}; } | ||
if (!definition.processes) { definition.processes = {}; } | ||
if (!definition.connections) { definition.connections = []; } | ||
if (!definition.properties) { definition.properties = {}; } | ||
if (!definition.processes) { definition.processes = {}; } | ||
if (!definition.connections) { definition.connections = []; } | ||
const graph = new Graph(definition.properties.name, { | ||
caseSensitive: definition.caseSensitive || false, | ||
}); | ||
const graph = new Graph(definition.properties.name, { | ||
caseSensitive: definition.caseSensitive || false, | ||
}); | ||
graph.startTransaction('loadJSON', metadata); | ||
const properties: PropertyMap = {}; | ||
Object.keys(definition.properties).forEach((property) => { | ||
if (property === 'name') { | ||
return; | ||
} | ||
if (!definition.properties) { | ||
return; | ||
} | ||
const value: any = definition.properties[property]; | ||
properties[property] = value; | ||
}); | ||
graph.setProperties(properties); | ||
graph.startTransaction('loadJSON', metadata); | ||
const properties: PropertyMap = {}; | ||
Object.keys(definition.properties).forEach((property) => { | ||
if (property === 'name') { | ||
return; | ||
} | ||
if (!definition.properties) { | ||
return; | ||
} | ||
const value: any = definition.properties[property]; | ||
properties[property] = value; | ||
}); | ||
graph.setProperties(properties); | ||
Object.keys(definition.processes).forEach((id) => { | ||
if (!definition.processes) { | ||
return; | ||
} | ||
const def = definition.processes[id]; | ||
if (!def.metadata) { def.metadata = {}; } | ||
graph.addNode(id, def.component, def.metadata); | ||
}); | ||
Object.keys(definition.processes).forEach((id) => { | ||
if (!definition.processes) { | ||
return; | ||
} | ||
const def = definition.processes[id]; | ||
if (!def.metadata) { def.metadata = {}; } | ||
graph.addNode(id, def.component, def.metadata); | ||
}); | ||
definition.connections.forEach((conn) => { | ||
const meta = conn.metadata ? conn.metadata : {}; | ||
if (typeof conn.data !== 'undefined') { | ||
if (typeof conn.tgt.index === 'number') { | ||
graph.addInitialIndex( | ||
conn.data, | ||
definition.connections.forEach((conn) => { | ||
const meta = conn.metadata ? conn.metadata : {}; | ||
if (typeof conn.data !== 'undefined') { | ||
if (typeof conn.tgt.index === 'number') { | ||
graph.addInitialIndex( | ||
conn.data, | ||
conn.tgt.process, | ||
graph.getPortName(conn.tgt.port), | ||
conn.tgt.index, | ||
meta, | ||
); | ||
} else { | ||
graph.addInitial( | ||
conn.data, | ||
conn.tgt.process, | ||
graph.getPortName(conn.tgt.port), | ||
meta, | ||
); | ||
} | ||
return; | ||
} | ||
if (typeof conn.src === 'undefined') { | ||
return; | ||
} | ||
if ((typeof conn.src.index === 'number') || (typeof conn.tgt.index === 'number')) { | ||
graph.addEdgeIndex( | ||
conn.src.process, | ||
graph.getPortName(conn.src.port), | ||
conn.src.index, | ||
conn.tgt.process, | ||
@@ -1091,125 +1179,121 @@ graph.getPortName(conn.tgt.port), | ||
); | ||
} else { | ||
graph.addInitial( | ||
conn.data, | ||
conn.tgt.process, | ||
graph.getPortName(conn.tgt.port), | ||
meta, | ||
); | ||
return; | ||
} | ||
return; | ||
} | ||
if (typeof conn.src === 'undefined') { | ||
return; | ||
} | ||
if ((typeof conn.src.index === 'number') || (typeof conn.tgt.index === 'number')) { | ||
graph.addEdgeIndex( | ||
graph.addEdge( | ||
conn.src.process, | ||
graph.getPortName(conn.src.port), | ||
conn.src.index, | ||
conn.tgt.process, | ||
graph.getPortName(conn.tgt.port), | ||
conn.tgt.index, | ||
meta, | ||
); | ||
return; | ||
}); | ||
if (definition.inports) { | ||
Object.keys(definition.inports).forEach((pub) => { | ||
if (!definition.inports || !definition.inports[pub]) { | ||
return; | ||
} | ||
const priv = definition.inports[pub]; | ||
graph.addInport(pub, priv.process, graph.getPortName(priv.port), priv.metadata || {}); | ||
}); | ||
} | ||
graph.addEdge( | ||
conn.src.process, | ||
graph.getPortName(conn.src.port), | ||
conn.tgt.process, | ||
graph.getPortName(conn.tgt.port), | ||
meta, | ||
); | ||
}); | ||
if (definition.outports) { | ||
Object.keys(definition.outports).forEach((pub) => { | ||
if (!definition.outports || !definition.outports[pub]) { | ||
return; | ||
} | ||
const priv = definition.outports[pub]; | ||
graph.addOutport(pub, priv.process, graph.getPortName(priv.port), priv.metadata || {}); | ||
}); | ||
} | ||
if (definition.inports) { | ||
Object.keys(definition.inports).forEach((pub) => { | ||
if (!definition.inports || !definition.inports[pub]) { | ||
return; | ||
} | ||
const priv = definition.inports[pub]; | ||
graph.addInport(pub, priv.process, graph.getPortName(priv.port), priv.metadata || {}); | ||
}); | ||
} | ||
if (definition.outports) { | ||
Object.keys(definition.outports).forEach((pub) => { | ||
if (!definition.outports || !definition.outports[pub]) { | ||
return; | ||
} | ||
const priv = definition.outports[pub]; | ||
graph.addOutport(pub, priv.process, graph.getPortName(priv.port), priv.metadata || {}); | ||
}); | ||
} | ||
if (definition.groups) { | ||
definition.groups.forEach((group) => { | ||
graph.addGroup(group.name, group.nodes, group.metadata || {}); | ||
}); | ||
} | ||
if (definition.groups) { | ||
definition.groups.forEach((group) => { | ||
graph.addGroup(group.name, group.nodes, group.metadata || {}); | ||
}); | ||
graph.endTransaction('loadJSON'); | ||
resolve(graph); | ||
}); | ||
if (callback) { | ||
promise.then((graph) => { | ||
callback(null, graph); | ||
}, callback); | ||
} | ||
graph.endTransaction('loadJSON'); | ||
return callback(null, graph); | ||
return promise; | ||
} | ||
function loadFBP(fbpData: string, callback: GraphLoadingCallback, metadata: JournalMetadata = {}, caseSensitive = false) { | ||
let definition; | ||
try { | ||
function loadFBP(fbpData: string): Promise<Graph>; | ||
function loadFBP(fbpData: string, callback: GraphLoadingCallback, metadata?: JournalMetadata): void; | ||
function loadFBP(fbpData: string, callback?: GraphLoadingCallback, metadata: JournalMetadata = {}, caseSensitive = false): void|Promise<Graph> { | ||
const promise = new Promise<GraphJson>((resolve) => { | ||
// eslint-disable-next-line global-require | ||
definition = require('fbp').parse(fbpData, { caseSensitive }); | ||
} catch (e) { | ||
return callback(e); | ||
resolve(require('fbp').parse(fbpData, { caseSensitive })); | ||
}) | ||
.then((def) => loadJSON(def)); | ||
if (callback) { | ||
promise.then((graph) => { | ||
callback(null, graph); | ||
}, callback); | ||
} | ||
return loadJSON(definition, callback, metadata); | ||
return promise; | ||
} | ||
function loadHTTP(url: string, callback: StringLoadingCallback) { | ||
const req = new XMLHttpRequest(); | ||
req.onreadystatechange = () => { | ||
if (req.readyState !== 4) { return; } | ||
if (req.status !== 200) { | ||
callback(new Error(`Failed to load ${url}: HTTP ${req.status}`)); | ||
} | ||
callback(null, req.responseText); | ||
}; | ||
req.open('GET', url, true); | ||
req.send(); | ||
function loadHTTP(url: string): Promise<string> | ||
function loadHTTP(url: string, callback: StringLoadingCallback): void; | ||
function loadHTTP(url: string, callback?: StringLoadingCallback): void|Promise<string> { | ||
const promise = new Promise<string>((resolve, reject) => { | ||
const req = new XMLHttpRequest(); | ||
req.onreadystatechange = () => { | ||
if (req.readyState !== 4) { return; } | ||
if (req.status !== 200) { | ||
reject(new Error(`Failed to load ${url}: HTTP ${req.status}`)); | ||
return; | ||
} | ||
resolve(req.responseText); | ||
}; | ||
req.open('GET', url, true); | ||
req.send(); | ||
}); | ||
if (callback) { | ||
promise.then((content) => { | ||
callback(null, content); | ||
}, callback); | ||
} | ||
return promise; | ||
} | ||
function loadFile(file: string, callback: GraphLoadingCallback, metadata: JournalMetadata = {}, caseSensitive = false) { | ||
function loadFile(file: string): Promise<Graph>; | ||
function loadFile(file: string, callback: GraphLoadingCallback, metadata?: JournalMetadata, caseSensitive?: boolean): void; | ||
function loadFile(file: string, callback?: GraphLoadingCallback, metadata: JournalMetadata = {}, caseSensitive = false) { | ||
let ioPromise: Promise<string>; | ||
if (isBrowser()) { | ||
// On browser we can try getting the file via AJAX | ||
loadHTTP(file, (err: Error | null, data?: string) => { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
if (!data) { | ||
callback(new Error('No data received')); | ||
return; | ||
} | ||
if (file.split('.').pop() === 'fbp') { | ||
loadFBP(data, callback, metadata); | ||
return; | ||
} | ||
loadJSON(data, callback, metadata); | ||
ioPromise = loadHTTP(file); | ||
} else { | ||
ioPromise = new Promise((resolve, reject) => { | ||
// Node.js graph file | ||
readFile(file, 'utf-8', (err: null|Error, data: string) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
resolve(data); | ||
}); | ||
}); | ||
return; | ||
} | ||
// Node.js graph file | ||
// eslint-disable-next-line global-require | ||
require('fs').readFile(file, 'utf-8', (err: Error, data: string) => { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
const promise = ioPromise.then((content) => { | ||
if (file.split('.').pop() === 'fbp') { | ||
loadFBP(data, callback, {}, caseSensitive); | ||
return; | ||
return loadFBP(content); | ||
} | ||
loadJSON(data, callback, {}); | ||
return loadJSON(content); | ||
}); | ||
if (callback) { | ||
promise.then((content) => { | ||
callback(null, content); | ||
}, callback); | ||
} | ||
return promise; | ||
} | ||
@@ -1216,0 +1300,0 @@ |
@@ -529,7 +529,23 @@ // FBP Graph Journal | ||
save(file: string, callback: (err: NodeJS.ErrnoException | null) => void) { | ||
const json = JSON.stringify(this.toJSON(), null, 4); | ||
const { writeFile } = require('fs'); | ||
// eslint-disable-next-line global-require | ||
writeFile(`${file}.json`, json, 'utf-8', callback); | ||
save(file: string): Promise<void>; | ||
save(file: string, callback: (err: NodeJS.ErrnoException | null) => void): void; | ||
save(file: string, callback?: (err: NodeJS.ErrnoException | null) => void): void | Promise<void> { | ||
const promise = new Promise<void>((resolve, reject) => { | ||
const json = JSON.stringify(this.toJSON(), null, 4); | ||
const { writeFile } = require('fs'); | ||
writeFile(`${file}.json`, json, 'utf-8', (err: NodeJS.ErrnoException) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
resolve(); | ||
}); | ||
}); | ||
if (callback) { | ||
promise.then(() => { | ||
callback(null); | ||
}, callback); | ||
return; | ||
} | ||
return promise; | ||
} | ||
@@ -536,0 +552,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
321009
6111
45
4