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

fnflow

Package Overview
Dependencies
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fnflow - npm Package Compare versions

Comparing version 0.1.5 to 0.1.6

167

lib/fnFlow.js

@@ -19,18 +19,38 @@ var async = require('async');

if (data instanceof Array) {
return async.map(data, function (dataItem, cb) {flowGo(dataItem, tasks, cb)}, callback);
} else {
return flowGo(data, tasks, callback);
}
flowExec(data, tasks, null, callback);
}
}
var flowGo = function flowGo(data, tasks, callback) {
var flowExec = function flowExec(data, tasks, contextName, callback) {
if (data instanceof Array) {
return async.map(data, function (dataItem, cb) {
var context;
if(contextName) context = dataItem[contextName]
flowGo(dataItem, tasks, context, function(err, results){
if(contextName) cb(err, us.pick(results, Object.keys(tasks)));
else cb(err, results);
})
}, callback);
} else {
var context;
if(contextName) context = data[contextName];
return flowGo(data, tasks, context, function(err, results){
if(contextName) callback(err, us.pick(results, Object.keys(tasks)));
else callback(err, results);
});
}
}
var flowGo = function flowGo(data, tasks, context, callback) {
var newTasks;
if(data) newTasks = us.object(Object.keys(data), us.map(Object.keys(data), function(key){ return function(cb){ cb(null, data[key]); } }))
if(tasks) us.extend(newTasks, us.object(Object.keys(tasks), us.map(Object.keys(tasks), function(key){ return flowCall(tasks[key], key, tasks, data); } )));
if(context) us.extend(newTasks, us.object(Object.keys(context), us.map(Object.keys(context), function(key){ return function(cb){ cb(null, context[key]); } })));
if(tasks) us.extend(newTasks, us.object(Object.keys(tasks), us.map(Object.keys(tasks), function(key){
var taskPlan = interpretTask(tasks[key], key, tasks, data, context);
return flowCall(taskPlan, key);
} )));
return async.auto(newTasks, callback);
}
var flowCall = function flowCall(taskArgs, taskName, tasks, data) {
var interpretTask = function interpretTask(taskArgs, taskName, tasks, data, context) {
var fn; //function to call within each async.auto task (does the work).

@@ -41,6 +61,12 @@ var fnIndex; //index where fn was found.

if(typeof taskArgs == 'function') {
if(taskArgs instanceof SubFlow) {
var subFlow = taskArgs;
if(!tasks.hasOwnProperty(subFlow.dataName) && (!data || !data.hasOwnProperty(subFlow.dataName)) || subFlow.dataName == taskName) throw new FlowTaskError(taskName, "SubFlow data '" + subFlow.dataName + "' does not exist. Provide the name of a task or data from the parent flow. Possible values include: " + Object.keys(tasks).concat(Object.keys(data)).filter(function(name){ return name != taskName }).join(', '));
fn = { receiverName: subFlow.dataName, tasks: subFlow.tasks };
var prereqs = scanForPrereqs(subFlow.tasks, us.extend({}, data, tasks));
autoTask.push(subFlow.dataName);
autoTask = autoTask.concat(Object.keys(prereqs));
} else if(typeof taskArgs == 'function') {
fn = taskArgs;
} else if(!Array.isArray(taskArgs)) throw new FlowTaskError(taskName, "Invalid flow type. Must be function or array.");
else {
} else if(Array.isArray(taskArgs)) {
for(var i in taskArgs) {

@@ -52,3 +78,13 @@ var taskArg = taskArgs[i];

fnIndex = i;
} else if((tasks.hasOwnProperty(taskArg) || data.hasOwnProperty(taskArg)) && taskArg != taskName) {
} else if(taskArg instanceof SubFlow) {
var subFlow = taskArg;
if(fn) throw new FlowTaskError(taskName, "A task may have a SubFlow (index " + i + ") or a function call (index " + i + "), but not both. " + fnIndex + " and " + i + ").");
if(i < taskArgs.length - 1) throw new FlowTaskError(taskName, "SubFlows must be the at the last index.");
if(!tasks.hasOwnProperty(subFlow.dataName) && (!data || !data.hasOwnProperty(subFlow.dataName)) || subFlow.dataName == taskName) throw new FlowTaskError(taskName, "SubFlow data '" + subFlow.dataName + "' does not exist. Provide the name of a task or data from the parent flow. Possible values include: " + Object.keys(tasks).concat(Object.keys(data)).filter(function(name){ return name != taskName }).join(', '));
fn = { receiverName: subFlow.dataName, tasks: subFlow.tasks };
fnIndex = i;
var prereqs = scanForPrereqs(subFlow.tasks, us.extend({}, data, tasks));
autoTask.push(subFlow.dataName);
autoTask = autoTask.concat(Object.keys(prereqs));
} else if((tasks.hasOwnProperty(taskArg) || data && data.hasOwnProperty(taskArg) || context && context.hasOwnProperty(taskArg) && typeof context[taskArg] != 'function') && taskArg != taskName) {
if(fn) args.push(taskArg);

@@ -64,30 +100,93 @@ autoTask.push(taskArg);

if(!fn) throw new FlowTaskError(taskName, "Function required.");
} else {
throw new FlowTaskError(taskName, "Invalid flow type. Must be function, or array.");
}
autoTask.push(function(cb, results){
var fnArgs = [];
for(var i in args) {
var arg = args[i];
fnArgs[i] = results[arg];
}
fnArgs.push(cb);
return { fn: fn, args: args, autoTask: autoTask };
}
var receiver;
if(typeof fn != 'function') {
var receiverName = fn.receiverName;
receiver = results[receiverName];
var fnName = fn.name;
if(!receiver) throw new FlowTaskError(taskName, "Cannot call function '" + fn.name + "' on " + receiver);
fn = receiver[fnName];
if(!fn) throw new FlowTaskError(taskName, "Unknown symbol '" + fnName + "' must be either the name of a task, the name of data, or the name of a function on '" + receiverName + "'");
}
var flowCall = function flowCall(taskPlan, taskName) {
var fn = taskPlan.fn;
var args = taskPlan.args;
var autoTask = taskPlan.autoTask;
try {
fn.apply(receiver, fnArgs);
} catch(e){
throw new FlowTaskError(taskName, "Error during execution of function.", e);
}
});
if(typeof fn == 'object' && fn.tasks) { //subflows
autoTask.push(function(cb, results){
var rawResultData = results[fn.receiverName];
if(rawResultData === null || rawResultData === undefined) {
throw new FlowTaskError(taskName, "Result of '" + fn.receiverName + "' returned no data. Could not start SubFlow.");
} else if(Array.isArray(rawResultData)) {
var newData = rawResultData.map(function(dataItem){
var newDataItem = us.extend({}, results);
newDataItem[fn.receiverName] = dataItem;
return newDataItem;
});
} else {
var newData = us.extend({}, results);
newData[fn.receiverName] = rawResultData;
}
flowExec(newData, fn.tasks, fn.receiverName, cb);
});
} else {
autoTask.push(function(cb, results){
var fnArgs = [];
for(var i in args) {
var arg = args[i];
fnArgs[i] = results[arg];
}
fnArgs.push(cb);
var receiver;
if(typeof fn != 'function') {
var receiverName = fn.receiverName;
receiver = results[receiverName];
var fnName = fn.name;
if(!receiver) throw new FlowTaskError(taskName, "Cannot call function '" + fn.name + "' on " + receiver);
fn = receiver[fnName];
if(!fn) throw new FlowTaskError(taskName, "Unknown symbol '" + fnName + "' must be either the name of a task, the name of data, or the name of a function on '" + receiverName + "'");
}
try {
fn.apply(receiver, fnArgs);
} catch(e){
throw new FlowTaskError(taskName, "Error during execution of function.", e);
}
});
}
return autoTask;
}
var scanForPrereqs = function scanForPrereqs(tasks, allTasks) {
var prereqs = {};
for(var taskName in tasks) {
var taskArgs = tasks[taskName];
if(taskArgs instanceof SubFlow){
us.extend(prereqs, scanForPrereqs(taskArgs.tasks, us.extend({}, allTasks, tasks)));
} else if(Array.isArray(taskArgs)){
for(var i in taskArgs) {
var taskArg = taskArgs[i];
if(taskArg instanceof SubFlow) {
us.extend(prereqs, scanForPrereqs(taskArg.tasks, us.extend({}, allTasks, tasks)));
} else if(typeof taskArg == 'function') {
} else if(allTasks.hasOwnProperty(taskArg)) {
prereqs[taskArg] = true;
} else {
}
}
}
}
return prereqs;
}
var SubFlow = function(dataName, tasks){
this.dataName = dataName;
this.tasks = tasks;
if(!dataName) throw new Error("SubFlow error: No data given for subFlow. Provide the name of a task or data from the parent flow.");
if(!tasks) throw new Error("SubFlow error: No tasks given for subFlow.");
};
module.exports.flow.subFlow = function(dataName, tasks) {
return new SubFlow(dataName, tasks);
};

@@ -6,3 +6,3 @@ {

"author": "David Fenster <david@dfenster.com>",
"version": "0.1.5",
"version": "0.1.6",
"repository": {

@@ -9,0 +9,0 @@ "type": "git",

@@ -119,2 +119,10 @@ fnFlow

### flow.subFlow(dataName, tasks)
This function pairs with the original _flow_ function to execute a sub flow within a parent. Given the name of a task or data from the parent flow,
and a set of new tasks, it will execute the equivalent of one flow call from within another. It is most handy when you must invoke operations on each
item in an array of results from a previous task in the flow, though, it will work on a single result as well.
## Authors

@@ -121,0 +129,0 @@

@@ -73,3 +73,3 @@ var util = require('util');

};
Book.prototype.getAuthor = function(cb){ return Author.getById(this.author_id, cb); }
Book.prototype.getAuthor = function(cb){ return Author.getById(this.authorId, cb); }
bindFunctions(Book);

@@ -635,3 +635,3 @@

test.equals(e.name, "FlowTaskError", "got FlowTaskError");
test.equals(e.message, "Flow error in 'getAuthor': Invalid flow type. Must be function or array.", "error message match")
test.equals(e.message, "Flow error in 'getAuthor': Invalid flow type. Must be function, or array.", "error message match")
test.done();

@@ -660,1 +660,485 @@ }

}
module.exports["array result data execution"] = function(test){
flow({
genreName: 'Fantasy'
}, {
getGenre: [Genre.getByName, 'genreName'],
getBooksByGenre: ['getGenre', 'getBooks'],
getAuthors: flow.subFlow('getBooksByGenre', {
getBookAuthor: ['getBooksByGenre', 'getAuthor']
})
}, function(err, results){
test.ok(!err, 'no error');
test.deepEqual(results, {
genreName: 'Fantasy',
getGenre: Genre.all[1],
getBooksByGenre: [Book.all[7], Book.all[8], Book.all[9], Book.all[10], Book.all[11]],
getAuthors: [{
getBookAuthor: Author.all[6]
}, {
getBookAuthor: Author.all[6]
}, {
getBookAuthor: Author.all[5]
}, {
getBookAuthor: Author.all[5]
}, {
getBookAuthor: Author.all[5]
}]
});
test.done();
});
}
module.exports["array result data execution with context"] = function(test){
flow({
genreName: 'Fantasy'
}, {
getGenre: [Genre.getByName, 'genreName'],
getBooksByGenre: ['getGenre', 'getBooks'],
getAuthors: flow.subFlow('getBooksByGenre', {
getBookAuthor: [Author.getById, 'authorId']
})
}, function(err, results){
test.ok(!err, 'no error');
test.deepEqual(results, {
genreName: 'Fantasy',
getGenre: Genre.all[1],
getBooksByGenre: [Book.all[7], Book.all[8], Book.all[9], Book.all[10], Book.all[11]],
getAuthors: [{
getBookAuthor: Author.all[6]
}, {
getBookAuthor: Author.all[6]
}, {
getBookAuthor: Author.all[5]
}, {
getBookAuthor: Author.all[5]
}, {
getBookAuthor: Author.all[5]
}]
});
test.done();
});
}
module.exports["array result data execution using subFlow"] = function(test){
flow({
genreName: 'Fantasy'
}, {
getGenre: [Genre.getByName, 'genreName'],
getBooksByGenre: ['getGenre', 'getBooks'],
getAuthors: flow.subFlow('getBooksByGenre', {
getBookAuthor: ['getBooksByGenre', 'getAuthor']
})
}, function(err, results){
test.ok(!err, 'no error');
test.deepEqual(results, {
genreName: 'Fantasy',
getGenre: Genre.all[1],
getBooksByGenre: [Book.all[7], Book.all[8], Book.all[9], Book.all[10], Book.all[11]],
getAuthors: [{
getBookAuthor: Author.all[6]
}, {
getBookAuthor: Author.all[6]
}, {
getBookAuthor: Author.all[5]
}, {
getBookAuthor: Author.all[5]
}, {
getBookAuthor: Author.all[5]
}]
});
test.done();
});
}
module.exports["array result data execution with prereqs using subFlow"] = function(test){
flow({
genreName: 'Fantasy',
authorName: 'Barbara Hambly'
}, {
getGenre: [Genre.getByName, 'genreName'],
getBooksByGenre: ['getGenre', 'getBooks'],
getAuthors: flow.subFlow('getBooksByGenre', {
getHambly2: [Author.getById, 'getHambly']
}),
getHambly: [Author.getByName, 'authorName']
}, function(err, results){
test.ok(!err, 'no error');
test.deepEqual(results, {
genreName: 'Fantasy',
authorName: 'Barbara Hambly',
getGenre: Genre.all[1],
getBooksByGenre: [Book.all[7], Book.all[8], Book.all[9], Book.all[10], Book.all[11]],
getHambly: Author.all[6],
getAuthors: [{
getHambly2: Author.all[6]
}, {
getHambly2: Author.all[6]
}, {
getHambly2: Author.all[6]
}, {
getHambly2: Author.all[6]
}, {
getHambly2: Author.all[6]
}]
});
test.done();
});
}
module.exports["two nested subflows with prereqs"] = function(test){
flow({
genreName: 'Fantasy',
authorName: 'Barbara Hambly'
}, {
getGenre: [Genre.getByName, 'genreName'],
getBooksByGenre: ['getGenre', 'getBooks'],
getAuthors: flow.subFlow('getBooksByGenre', {
getBookAuthor: ['getBooksByGenre', 'getAuthor'],
getBooksByAuthor: [Book.findByAuthorId, 'getBookAuthor'],
getManyHamblies: flow.subFlow('getBooksByAuthor', {
getHambly2: [Author.getById, 'getHambly']
})
}),
getHambly: [Author.getByName, 'authorName']
}, function(err, results){
test.ok(!err, 'no error');
test.deepEqual(results, {
genreName: 'Fantasy',
authorName: 'Barbara Hambly',
getGenre: Genre.all[1],
getBooksByGenre: [Book.all[7], Book.all[8], Book.all[9], Book.all[10], Book.all[11]],
getHambly: Author.all[6],
getAuthors: [{
getBookAuthor: Author.all[6],
getBooksByAuthor: [Book.all[7], Book.all[8]],
getManyHamblies: [{
getHambly2: Author.all[6]
}, {
getHambly2: Author.all[6]
}]
}, {
getBookAuthor: Author.all[6],
getBooksByAuthor: [Book.all[7], Book.all[8]],
getManyHamblies: [{
getHambly2: Author.all[6]
}, {
getHambly2: Author.all[6]
}]
}, {
getBookAuthor: Author.all[5],
getBooksByAuthor: [Book.all[9], Book.all[10], Book.all[11]],
getManyHamblies: [{
getHambly2: Author.all[6]
}, {
getHambly2: Author.all[6]
}, {
getHambly2: Author.all[6]
}]
}, {
getBookAuthor: Author.all[5],
getBooksByAuthor: [Book.all[9], Book.all[10], Book.all[11]],
getManyHamblies: [{
getHambly2: Author.all[6]
}, {
getHambly2: Author.all[6]
}, {
getHambly2: Author.all[6]
}]
}, {
getBookAuthor: Author.all[5],
getBooksByAuthor: [Book.all[9], Book.all[10], Book.all[11]],
getManyHamblies: [{
getHambly2: Author.all[6]
}, {
getHambly2: Author.all[6]
}, {
getHambly2: Author.all[6]
}]
}]
});
test.done();
});
}
module.exports["subflow with empty name"] = function(test){
try {
flow({
genreName: 'Fantasy'
}, {
getGenre: [Genre.getByName, 'genreName'],
getBooksByGenre: ['getGenre', 'getBooks'],
getAuthors: flow.subFlow('', {
getBookAuthor: ['getBooksByGenre', 'getAuthor']
})
}, function(err, results){
test.fail(null, null, "no error received");
test.done();
});
} catch(e) {
test.ok(e, 'got an error');
test.equals(e.name, "Error", "got Error");
test.equals(e.message, "SubFlow error: No data given for subFlow. Provide the name of a task or data from the parent flow.", "error message match")
test.done();
}
}
module.exports["subflow with no tasks"] = function(test){
try {
flow({
genreName: 'Fantasy'
}, {
getGenre: [Genre.getByName, 'genreName'],
getBooksByGenre: ['getGenre', 'getBooks'],
getAuthors: flow.subFlow('getBooksByGenre', null)
}, function(err, results){
test.fail(null, null, "no error received");
test.done();
});
} catch(e) {
test.ok(e, 'got an error');
test.equals(e.name, "Error", "got Error");
test.equals(e.message, "SubFlow error: No tasks given for subFlow.", "error message match")
test.done();
}
}
module.exports["subflow with bad data name"] = function(test){
try {
flow({
genreName: 'Fantasy'
}, {
getGenre: [Genre.getByName, 'genreName'],
getBooksByGenre: ['getGenre', 'getBooks'],
getAuthors: flow.subFlow('asdf', {
getBookAuthor: ['getBooksByGenre', 'getAuthor']
})
}, function(err, results){
test.fail(null, null, "no error received");
test.done();
});
} catch(e) {
test.ok(e, 'got an error');
test.equals(e.name, "FlowTaskError", "got Error");
test.equals(e.message, "Flow error in 'getAuthors': SubFlow data 'asdf' does not exist. Provide the name of a task or data from the parent flow. Possible values include: getGenre, getBooksByGenre, genreName", "error message match")
test.done();
}
}
module.exports["subflow task with own data name"] = function(test){
try {
flow({
genreName: 'Fantasy'
}, {
getGenre: [Genre.getByName, 'genreName'],
getBooksByGenre: ['getGenre', 'getBooks'],
getAuthors: flow.subFlow('getAuthors', {
getBookAuthor: ['getBooksByGenre', 'getAuthor']
})
}, function(err, results){
test.fail(null, null, "no error received");
test.done();
});
} catch(e) {
test.ok(e, 'got an error');
test.equals(e.name, "FlowTaskError", "got Error");
test.equals(e.message, "Flow error in 'getAuthors': SubFlow data 'getAuthors' does not exist. Provide the name of a task or data from the parent flow. Possible values include: getGenre, getBooksByGenre, genreName", "error message match")
test.done();
}
}
module.exports["subflow with explicit prereq"] = function(test){
flow({
genreName: 'Fantasy'
}, {
getGenre: [Genre.getByName, 'genreName'],
assertGenreExistence: [Genre.assertExistence, 'getGenre'],
getBooksByGenre: ['getGenre', 'getBooks'],
getAuthors: ['assertGenreExistence', flow.subFlow('getBooksByGenre', {
getBookAuthor: [Author.getById, 'authorId']
})]
}, function(err, results){
test.ok(!err, 'no error');
test.deepEqual(results, {
genreName: 'Fantasy',
assertGenreExistence: true,
getGenre: Genre.all[1],
getBooksByGenre: [Book.all[7], Book.all[8], Book.all[9], Book.all[10], Book.all[11]],
getAuthors: [{
getBookAuthor: Author.all[6]
}, {
getBookAuthor: Author.all[6]
}, {
getBookAuthor: Author.all[5]
}, {
getBookAuthor: Author.all[5]
}, {
getBookAuthor: Author.all[5]
}]
});
test.done();
});
}
module.exports["subflow out of order"] = function(test){
try {
flow({
genreName: 'Fantasy'
}, {
getGenre: [Genre.getByName, 'genreName'],
assertGenreExistence: [Genre.assertExistence, 'getGenre'],
getBooksByGenre: ['getGenre', 'getBooks'],
getAuthors: [flow.subFlow('getBooksByGenre', {
getBookAuthor: [Author.getById, 'authorId']
}), 'assertGenreExistence']
}, function(err, results){
test.fail(null, null, "no error received");
test.done();
});
} catch(e){
test.ok(e, 'got an error');
test.equals(e.name, "FlowTaskError", "got Error");
test.equals(e.message, "Flow error in 'getAuthors': SubFlows must be the at the last index.", "error message match")
test.done();
}
}
module.exports["subflow in task array with bad data name"] = function(test){
try {
flow({
genreName: 'Fantasy'
}, {
getGenre: [Genre.getByName, 'genreName'],
assertGenreExistence: [Genre.assertExistence, 'getGenre'],
getBooksByGenre: ['getGenre', 'getBooks'],
getAuthors: ['assertGenreExistence', flow.subFlow('asdf', {
getBookAuthor: [Author.getById, 'authorId']
})]
}, function(err, results){
test.fail(null, null, "no error received");
test.done();
});
} catch(e){
test.ok(e, 'got an error');
test.equals(e.name, "FlowTaskError", "got Error");
test.equals(e.message, "Flow error in 'getAuthors': SubFlow data 'asdf' does not exist. Provide the name of a task or data from the parent flow. Possible values include: getGenre, assertGenreExistence, getBooksByGenre, genreName", "error message match")
test.done();
}
}
module.exports["subflow in task array with own data name"] = function(test){
try {
flow({
genreName: 'Fantasy'
}, {
getGenre: [Genre.getByName, 'genreName'],
assertGenreExistence: [Genre.assertExistence, 'getGenre'],
getBooksByGenre: ['getGenre', 'getBooks'],
getAuthors: ['assertGenreExistence', flow.subFlow('getAuthors', {
getBookAuthor: [Author.getById, 'authorId']
})]
}, function(err, results){
test.fail(null, null, "no error received");
test.done();
});
} catch(e){
test.ok(e, 'got an error');
test.equals(e.name, "FlowTaskError", "got Error");
test.equals(e.message, "Flow error in 'getAuthors': SubFlow data 'getAuthors' does not exist. Provide the name of a task or data from the parent flow. Possible values include: getGenre, assertGenreExistence, getBooksByGenre, genreName", "error message match")
test.done();
}
}
module.exports["single data subflow execution"] = function(test){
flow({
genreName: 'Fantasy'
}, {
getGenre: [Genre.getByName, 'genreName'],
getBooksByGenre: flow.subFlow('getGenre', {
getBooksByGenre: [Book.findByGenreId, 'id']
})
}, function(err, results){
test.ok(!err, 'no error');
test.deepEqual(results, {
genreName: 'Fantasy',
getGenre: Genre.all[1],
getBooksByGenre: {
getBooksByGenre: [Book.all[7], Book.all[8], Book.all[9], Book.all[10], Book.all[11]]
}
});
test.done();
});
}
module.exports["single null data subflow execution"] = function(test){
try {
flow({
genreName: 'Chainsaw slashers'
}, {
getGenre: [Genre.getByName, 'genreName'],
getBooksByGenre: flow.subFlow('getGenre', {
getBooksByGenre2: [Book.findByGenreId, 'id']
})
}, function(err, results){
test.fail(null, null, "no error received");
test.done();
});
} catch(e){
test.ok(e, 'got an error');
test.equals(e.name, "FlowTaskError", "got Error");
test.equals(e.message, "Flow error in 'getBooksByGenre': Result of 'getGenre' returned no data. Could not start SubFlow.", "error message match")
test.done();
}
}
// module.exports["array result data execution"] = function(test){
// var authors, fantasyAuthors;
// fantasyAuthors = new FnFlow({
// genreName: 'Fantasy',
// })
// .addTask('getGenre', Genre.getByName, fantasyAuthors.data('genreName'));
// .addTask('getBooksByGenre', fantasyAuthors.task.getGenre, fantasyAuthors.task.getGenre.call('getBooks'));
// .addTask('getAuthors', (authors = new FnFlow(fantasyAuthors.task.getBooksByGenre))
// .addTask('getBookAuthor', authors.data.call('getAuthor'))
// .addTask('getBookAuthor', Author.getById, author.data('authorId'))
// )
// }, function(err, results){
// test.ok(!err, 'no error');
// test.deepEqual(results, {
// genreName: 'Fantasy',
// getGenre: Genre.all[1],
// getBooksByGenre: [Book.all[7], Book.all[8], Book.all[9], Book.all[10], Book.all[11]],
// getAuthors: [{
// getBookAuthor: Author.all[6]
// }, {
// getBookAuthor: Author.all[6]
// }, {
// getBookAuthor: Author.all[5]
// }, {
// getBookAuthor: Author.all[5]
// }, {
// getBookAuthor: Author.all[5]
// }]
// });
// test.done();
// });
// }
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