<|start_filename|>packages/jollof/lib/forms/fields/Field.js<|end_filename|>
'use strict'
const stringUtil = require('../../util/stringUtil');
/**
 * Created by iyobo on 2016-04-25.
 */

/**
 * Default render is a Text Field.
 */
class Field {

	constructor( form, params ) {
		this.name = params.name;
		this.label = params.label || stringUtil.labelize(params.name);
		this.placeholder = params.placeholder || ""
		this.group = params.group

		this.value = (params.context.request.fields && params.context.request.fields[ this.name ]) ? params.context.request.fields[ this.name ] : ""

		this.error = null;
		this.type = params.type || "text";

		this.form = form;
	}

	isRequired(){
		return this.form.isRequired(this.name);
	}

}

module.exports = Field

<|start_filename|>packages/jollof/lib/forms/form.js<|end_filename|>
'use strict'
/**
 * Created by iyobo on 2016-04-25.
 */
// var customValidators = require('./validators')
const _ = require('lodash');

class Form{

	constructor(ctx){
		this.fields={}
		this.isValid=true
		this.error=null
		this.validators=[{},{},{}]
	}

	/**
	 * validate all fields
	 * @param ctx
	 */
	* validate(ctx){
		var valid = true;

		yield ctx.validateBody(
			this.validators[0],
			this.validators[1],
			this.validators[2]
		);

		if (ctx.validationErrors) {
			valid=false;
			ctx.status = 422;
			ctx.body = ctx.validationErrors;

			//Attach errors to fields
			console.log(ctx.validationErrors);

		} else {
			ctx.status = 200;
		}

		return valid;
	}

	/**
	 * Checks if a field is required
	 * @param fieldName
	 */
	isRequired(fieldName){
		let result = false;
		const rule = this.validators[0][fieldName];

		if(rule){
			if(rule.indexOf('required')> -1){
				result = true;
			}
		}

		return result;
	}

	/**
	 * Returns a packaged object of the form, with values placed in their groups.
	 */
	groupedValues(){
		var obj={}
		for (var name in this.fields){
			var f = this.fields[name];
			if(f.group)
			{
				if (!obj[f.group]) obj[f.group] = {};
				obj[f.group][name] = f.value;
			}
			else{
				obj[name] = f.value;
			}
		}
		return obj;
	}

}

module.exports = Form

<|start_filename|>packages/jollof/lib/data/types/any.js<|end_filename|>
const registry = require('../registry');

/**
 * This is an in-built Schema Type.
 * Schema Types are schemas themselves, and can be used inside other schemas.
 * Just be careful not to create an infinite loop by using a custom Schema Type in itself or child.
 *
 *
 * This type is used to store stuff of any field type. Not advised.
 */
const schema = {
    name: 'Any',
    structure: {
        meta: {},
        custom: function (data, schema) {
            return data;
        }
    }

};

module.exports = registry.registerType(schema);




<|start_filename|>packages/jollof/lib/util/JollofCommand.js<|end_filename|>
'use strict'
/**
 * Created by iyobo on 2016-04-24.
 */
class BaseCommand{

	* help(){
		log.info("This is a base script. It is empty and does nothing. Extend from this to create your own")
	}
}

module.exports = BaseCommand

<|start_filename|>packages/jollof/lib/log.js<|end_filename|>
'use strict'
const Tracer = require('tracer');

let log;

log = Tracer.colorConsole({
    format: [
        "{{timestamp}} [{{file}}:{{line}}] {{message}} ",
        { error: "{{timestamp}} [{{file}}:{{line}} - ERROR] {{message}} " }
    ],
});
//if (process.env.NODE_ENV !== 'development')
//    log = Tracer.dailyfile({ root: './logs/', maxLogFiles: 10 });


log.err = log.error;

module.exports = log;



<|start_filename|>packages/jollof/lib/util/stringUtil.js<|end_filename|>
const figlet = require('figlet');

exports.labelize = (str) => {
    return str.replace(/(^[a-z]+)|[0-9]+|[A-Z][a-z]+|[A-Z]+(?=[A-Z][a-z]|[0-9])/g
        , function (match, first) {
            if (first) match = match[0].toUpperCase() + match.substr(1);
            return match + ' ';
        }
    )
}

exports.capitalizeFirstLetter = (string) => {
    return string.charAt(0).toUpperCase() + string.slice(1);
}



<|start_filename|>packages/jollof/lib/admin/rClient/util/dataUtil.js<|end_filename|>
/**
 * Created by iyobo on 2017-04-18.
 */


export const deriveEntityName = (data) => {
    return data.get ? (data.get('name') || data.get('title') || data.get('id') || '' ) : '';
}

<|start_filename|>packages/jollof/lib/admin/rClient/data/store.js<|end_filename|>
/**
 * Created by iyobo on 2017-02-12.
 */
import {observable, action} from 'mobx';
import models from './stores/ModelStore';



/**
 * Created by iyobo on 2017-02-12.
 */
class AppStore{

    @observable loadingCount = 0;
    @observable pageName = "";

    reportError = (error) => {
        //this.loadingCount--;
        //loadingCount = loadingCount < 0? 0: loadingCount;

        console.error(error);
        const data = error.response.data;

        if (data && data.keyPath) {

            const message = data.keyPath.join('.').toUpperCase() + ' ' + data.message;

            //notificationSpice.error(message);
            console.error(message);
            return data;
        } else {
            //notificationSpice.error(error.message);
            console.error(error.message);

        }
    }

    setPageName = (name) => {
        this.pageName = name;
    }

    changeModelPage(name) {
        models.resetQuery();
        models.setActiveModel(name);
        this.setPageName(name);
    }

}



export default {app: new AppStore(), models};

<|start_filename|>packages/jollof/lib/forms/fields/TextAreaField.js<|end_filename|>
'use strict'
/**
 * Created by iyobo on 2016-04-25.
 */


class TextAreaField extends require('./Field'){

	constructor(params){
		super(form, params)
		this.options = params.options|| []; //must be an array

		this.type="textarea"
	}

}

module.exports = TextAreaField

<|start_filename|>packages/isvalid-jollof/index.js<|end_filename|>
'use strict';

exports = module.exports = require('./lib/validate.js');
exports.formalize = require('./lib/schema.js').formalize;
exports.validate = require('./lib/middleware.js');


<|start_filename|>packages/jollof-data-memory/jollofDataMemory.js<|end_filename|>
/**
 * Created by iyobo on 2016-10-30.
 */
const co = require('co');
const _ = require('lodash');
const path = require('path');
const promise = require('bluebird');

const Datastore = require('nedb-jollof')
var convertToJollof = require('./util/conversionUtil.js').convertToJollof;
var convertConditionsFromJollof = require('./util/conversionUtil.js').convertConditionsFromJollof;

/**
 * A Jollof Data Adapter for Memory
 */
class JollofDataMemory {

    /**
     * Keep constructor free of async calls.
     * Only use to set initial values like connection strings and similar sync commands.
     * @param options
     */
    constructor(options) {
        this.connectionOptions = options || {};
        this.db = {};

        this._Datastore = Datastore;
        this._convertConditionsFromJollof = convertConditionsFromJollof;
        this._convertToJollof = convertToJollof;
    }


    /**
     * Jollof will run this on boot. Place all async or 'talking to database per collection' logic here.
     *
     * @param schema
     */
    async addSchema(schema) {

        //options are different each time because same adapter managers multiple stores/collections
        const options = _.cloneDeep(this.connectionOptions)
        options.filename = path.join(this.connectionOptions.filename, schema.name + '.db')
        this.db[schema.name] = promise.promisifyAll(new Datastore(options));
        return true;
    }




    /**
     * This is the primary Id field name to be used in all models under this adapter.
     * @returns {string}
     */
    get idField() {
        return '_id';
    }

    /**
     * These are fields that this adapter's datasource needs, but that might not relate to Jollof.
     * Jollof might use this to know and preserve these fields.
     *
     * @returns {string}
     */
    get keepFields() {
        return [];
    }

    /**
     *
     * @param collectionName
     * @param criteria
     * @param opts
     * @returns {*}
     */
    async find(collectionName, criteria, opts = {}) {
        //If we're paging
        let res;

        if (opts) {

            const options = {};

            if (opts.paging) {
                const page = opts.paging.page || 1;
                const limit = opts.paging.limit || 10;
                const skip = ((page - 1) * limit);

                options.paging = { skip, limit };
            }

            if (opts.sort) {
                options.sort = opts.sort;
            }

            res = await this.db[collectionName].findOptsAsync(convertConditionsFromJollof(criteria), options);
        } else {
            res = await this.db[collectionName].findAsync(convertConditionsFromJollof(criteria));
        }
        return convertToJollof(res);

    }

    /**
     *
     * @param collectionName
     * @param criteria
     * @param opts
     * @returns {*}
     */
    async count(collectionName, criteria, opts) {
        return await this.db[collectionName].countAsync(convertConditionsFromJollof(criteria));
    }


    /**
     * Return how many items were updated
     * @param collectionName
     * @param criteria
     * @param newValues
     * @param params
     * @returns {*}
     */
    async update(collectionName, criteria, newValues, opts) {
        //opts = convertConditionsFromJollof(opts);
        const res = await this.db[collectionName].updateAsync(convertConditionsFromJollof(criteria), newValues);

        return res;
    }


    /**
     * Return the created item
     * @param collectionName
     * @param criteria
     * @param newValues
     * @param params
     * @returns {*}
     */
    async create(collectionName, data) {

        const res = await this.db[collectionName].insertAsync(data);
        return convertToJollof(res);
    }


    /**
     * Deletes all rows that match the criteria.
     * Return number of items deleted
     *
     * @param collectionName
     * @param criteria
     * @param opts
     * @returns {*}
     */
    async remove(collectionName, criteria, opts={}) {

        const options=_.merge(opts, { multi: true });
        const q = convertConditionsFromJollof(criteria);
        return await this.db[collectionName].removeAsync(q, options);

    }

}

module.exports = JollofDataMemory;

<|start_filename|>packages/jollof/lib/data/types/location.js<|end_filename|>
const registry = require('../registry');
/**
 * This is an in-built Schema Type.
 * Schema Types are schemas themselves, and can be used inside other schemas.
 * Just be careful not to create an infinite loop by using a custom Schema Type in itself or child.
 *
 * @type {{type: string, schema: {address1: String, address2: String, city: {type: String, required: boolean}, state: {type: String, required: boolean}, postalCode: String, country: {type: String, required: boolean}, longitude: {type: Number, required: boolean}, latitude: {type: Number, required: boolean}}}}
 */
const locationSchema = {
	name: 'Location',
	structure: {
		type: Object,
		required: false,
		schema: {
			address: { type: String, required: true },
			address2: String,
			city: { type: String, required: true },
			state: { type: String, required: true },
			postalCode: String,
			country: { type: String, required: true },
			longitude: { type: Number, required: true },
			latitude: { type: Number, required: true },
			full: String
		},
		meta: {
			widget: 'map',
		}
	}

}

module.exports = registry.registerType(locationSchema);




<|start_filename|>packages/jollof/lib/util/httpUtil.js<|end_filename|>
/**
 * Created by iyobo on 2016-12-05.
 */
const log = require('../log');
const _ = require('lodash');
const storage = require('../filestorage')


class HTTPUtil {

    /**
     * If request is multi-part, Make multidimensional objects/arrays from one dimensional form data, and process any files.
     * @param ctx
     */
    * objectify(ctx) {
        let fields = {}
        try {
            const contentType = ctx.request.accept.headers['content-type'];
            if (contentType && contentType.indexOf('multipart') > -1) {

                //multi-dimensionalize ctx.fields
                for (let compoundKey in ctx.request.fields) {
                    let v = ctx.request.fields[compoundKey];

                    //If value is a file, then store it
                    if (Array.isArray(v) && v.length > 0 && v[0]._writeStream) {

                        /**
                         * Determine whether single or multi file field upload
                         */
                        if (v.length == 1) {
                            v = yield storage.store(v[0])
                        } else {
                            v = v.map(function*(file) {
                                const val = yield storage.store(file);
                                return val;
                            });
                        }
                    }

                    //split by dot
                    const keypath = compoundKey.split('.');

                    //Thank God for lodash...
                    _.set(fields, keypath, v)
                }

                //Update
                // ctx.request.jollofFields = fields;
                ctx.request.fields = fields;
            }
        }
        catch (ex) {
            log.error('Error while objectifying form data', ex.stack)
        }
    }

    handleError(ctx, err) {
        if (err.isOperational) {
            ctx.status = err.status || 500;
            ctx.body = err;
            //ctx.app.emit('error', err, ctx);
        }
        else if (err.hash) {
            //jql error
            ctx.status = 405;
            ctx.body = 'Bad JQL query.';
            //ctx.app.emit('error', err.message, ctx);
        }
        else if (err.isBoom) {
            ctx.status = err.output.statusCode;
            ctx.body = err.message;
        }
        else {
            log.err(err);
            ctx.throw(err);
        }
    }
}


module.exports = new HTTPUtil();

<|start_filename|>packages/jollof/lib/data/modelizer.js<|end_filename|>
/**
 * Created by iyobo on 2016-10-27.
 */
const _ = require('lodash');
const Promise = require('bluebird');
const stringUtil = require('../util/stringUtil');
const config = require('../configurator').settings;
const log = require('../log');
const jfs = require('../filestorage');
const isValid = require('isvalid-jollof');

const Boom = require('boom');

var populateMetaTypes = require('../util/dataUtils.js').populateMetaTypes;

const isValidValidate = Promise.promisify(isValid);

const adapterMap = {};

function objToCriteria(matchObj) {
    const q = [];

    _.each(matchObj, (v, k) => {
        q.push([k, '=', v]);
    });

    return q;
}

/**
 * Takes a Jollof schema. Dynamically Returns a Model Class.
 * Runs per defined model.
 * @param schema
 * @returns {*}
 */
exports.modelize = function (schema) {
    const collectionName = schema.name;

    const dataSourceName = schema.dataSource || 'default';
    const dataSource = config.data.dataSources[dataSourceName];

    /**
     * Only the native functions matching this connector's nativeName can be run for this model
     * @type {string}
     */
    const activeNativeType = dataSource.nativeType;

    let adapter;
    const AdapterClass = dataSource.adapter;

    //if adapter has already been instantiated
    if (adapterMap[dataSourceName]) {
        adapter = adapterMap[dataSourceName];
    } else {
        adapter = new AdapterClass(dataSource.options);
        //TODO: Move this to something external that keeps "Active adapters mapped by connectorName". ---Done???
        adapterMap[dataSourceName] = adapter;
    }

    /**
     * The native id type this model cares about.
     */
    const IdType = AdapterClass.IdType;

    /**
     * Generate validation rules for the model
     * @param structure
     * @returns {*}
     */
    const generateRules = (structure) => {
        //First clone the structure
        let tempStructure = _.merge({}, structure);

        /**
         * Some adapters use special meta-fields.
         * Add these fields to our rules so we do not wipe those metas out during validation!
         */
        _.each(adapter.keepFields, (fieldName) => {
            tempStructure[fieldName] = {
                custom: function (data) {
                    return data
                }
            }; //The isValid representation for Any
        });

        //determine typeCast fields


        /**
         * These are Jollof's own meta fields:
         * id: Every adapter must understand that this is Jollof's main id field
         * TimestampDates for time logging.
         */
        tempStructure['id'] = {
            meta: { disableEdit: true },
            custom: function (data, schema) {
                return data;
            }
        };
        tempStructure['dateCreated'] = { type: Date, meta: { type: 'Date', disableEdit: true } };
        tempStructure['lastUpdated'] = { type: Date, meta: { type: 'Date', disableEdit: true } };


        /**
         * Finally, formalize the structure.
         * Converts all shortcut field defs to full {type:...} field defs
         */
        const finalStructure = isValid.formalize(tempStructure);

        identifyRefsInSchemaFields(finalStructure);

        return finalStructure;
    };

    /**
     * Stores string notation of ref fields so to know to typecast in queries.
     * Populated in generate rules.
     * @type {{}}
     */
    const refFields = {};

    /**
     * Identify all refs in the structure and store path names of ref fields
     * @param criteria
     */
    function identifyRefsInSchemaFields(structure, prefix) {

        _.each(structure.schema, (fieldStructure, fieldName) => {

            const name = prefix ? prefix + '.' + fieldName : fieldName;

            //If field has a schema, then it is a collection
            if (fieldStructure.schema) {

                // is this an object or an array?
                if (fieldStructure.meta && fieldStructure.meta.type === 'Array') {
                    //array
                    identifyRefsInSchemaFields(fieldStructure, name + '.*')
                } else {
                    //object. loop through schema.fields
                    identifyRefsInSchemaFields(fieldStructure, name)
                }

            } else if (fieldStructure.meta && fieldStructure.meta.ref) {
                //ref field found!
                refFields[name] = 1;
            }
        });


    };


    //Generate the rules for this Model. This is used purely by the validator
    const modelRules = generateRules(schema.structure);


    //const ruleClone = _.cloneDeep(modelRules);
    populateMetaTypes(modelRules);
    //The schema for this model is what various frontends use to render it.
    const modelSchema = modelRules; //JSON.parse(JSON.stringify(ruleClone));
    modelSchema.name = schema.name;
    modelSchema.unknownKeys = 'remove';


    /**
     * This is a dynamically crafted class with some accessor/proxy magic. E.g userInstance.firstName will return userInstance.data.firstName
     * @type {{updateAll: ((id)), update: ((id)), list: ((id)), get: ((id)), delete: ((id)), new(data)=>{validate: (()), save: (()), type}}}
     */
    const Model = class {

        constructor(data) {
            this._loadData(data);
            this._connectionName = dataSourceName;

            /**
             * Only the native functions matching active nativeTypes can be run for this model.
             * To be able to run nativefunctions of other nativeTypes, use a different connector that supports that type.
             * @type {string}
             */
            this._activeNativeType = activeNativeType;
            this._adapter = adapter;

            this._collectionName = schema.name;
            this._collectioonDisplayName = stringUtil.labelize(schema.name);
            this._originalSchema = schema;

            //Proxy magic
            let dataKeys = Object.keys(schema.structure);
            dataKeys.push('dateCreated', 'lastUpdated');

            // const model = new Model(data);
            var modelAccessor = {
                set(target, key, value) {
                    if (target._isDeleted)
                        throw new Error('Jollof Data: Cannot set data for a deleted item');

                    if (dataKeys.indexOf(key) > -1)
                        target._data[key] = value;
                    else
                        target[key] = value;

                    return true
                },
                get(target, key) {
                    if (target._isDeleted)
                        throw new Error('Jollof Data: Cannot get data from a deleted item');

                    //if key exists in data's keys, get from there. otherwise get from the model
                    if (dataKeys.indexOf(key) > -1)
                        return target._data[key];
                    else
                        return target[key];
                }
            };

            const thisProxy = new Proxy(this, modelAccessor);

            //attach custom/"underscore" functions
            this._ = {};
            if (schema.extend) {
                for (let funcName in schema.extend) {
                    this._[funcName] = schema.extend[funcName].bind(thisProxy);
                }
            }

            return thisProxy;
        }

        /**
         * Must be called once, during boot
         */
        static async setup() {
            const g = await adapter.addSchema(schema);

            //Now let the model configure it's own self against its native adapter (e.g indexes etc)
            if (Model.native && Model.native.init) {
                try {
                    await Model.native.init();
                } catch (e) {
                    log.err(`Jollof Model Init Error: Please check init function of ${Model.collectionName}`)
                    throw e;
                }
            }
        }

        static get collectionName() {
            return schema.name;
        }

        static get adapterName() {
            return dataSourceName;
        }

        static get adapter() {
            return adapter;
        }

        static get IdType() {
            return IdType;
        }

        /**
         * Wraps an inputID into the form of an ID that is respected by this model's adapter.
         * Ensure you use this when querying by ref fields.
         *
         * @param inputId
         * @returns {*}
         */
        static wrapId(inputId) {
            let id = inputId;
            if (typeof id !== IdType.name) {
                id = new IdType(inputId)
            }
            return id;
        }

        static get schema() {
            return modelSchema;
        }

        static get rules() {
            return modelRules;
        }

        /**
         * Digests an array or object and returns either an array or singular model instance of the model.
         * @param data - an object or array of objects
         * @returns {*}
         */
        static instantiate(data) {

            if (!data)
                return null;

            if (Array.isArray(data)) {
                //This is an array of items
                return data.map((item) => {
                    return new Model(item);
                });
            }
            else {
                //a singular item. wrap it
                return new Model(data);
            }
        }

        /**
         * Instantiate and create an object
         * @param data
         */
        static async create(data) {
            if (!data)
                return null;

            if (Array.isArray(data)) {
                //This is an array of items
                return await Promise.all(data.map((item) => {
                    return new Model(item).save();
                }));
            }
            else {
                //a singular item. wrap it
                return await new Model(data).save();

            }
        }

        /**
         * For when you don't want to instantiate, but just want the fully formated version
         * FIXME: This can be optimized!!!!
         * @param data
         */
        static format(data) {
            if (!data)
                return null;

            if (Array.isArray(data)) {
                //This is an array of items
                return data.map((item) => {

                    return new Model(item).display();
                });
            }
            else {
                //a singular item. wrap it
                return new Model(data).display();
            }
        }


        static async findById(id, opts = {}) {

            //If id = null, then it obviously doesn't exist

            if (!id) {
                return null;
            }

            let res = await Model.findOneBy({ id }, opts);

            return res;
        }

        /**
         * Finds one by json match
         * @param match json match
         * @param opts
         * @returns {Promise.<*>}
         */
        static async findOneBy(match, opts = {}) {

            let res = await Model.findOne(objToCriteria(match), opts);

            return res;
        }


        /**
         * Finds one by json match or creates with those values if not exist
         *
         * @param match json match
         * @param opts
         * @returns - found or created object
         */
        static async findOrCreate(match, opts = {}) {

            let res = await Model.findOneBy(match);

            if (res) {
                res = new Model(match);
                await res.save();
            }

            return res;
        }


        /**
         * Searches for all matching items, and returns according to options.
         *
         * @param {Array} queryArray - A safe JQ array.
         * @param {object} options
         * @param {boolean} options.raw - if true, result will not be modelized. (But it will still respect the Jollof Id rule)
         * @param {object} options.paging -
         * @param {number} options.paging.page
         * @param {number} options.paging.limit
         * @param {object} options.sort - e.g {fieldA: 1, fieldB: -1}. 1 is asc, -1 is desc
         * @returns {*}
         */
        static async find(criteria, opts = {}) {

            let res = await adapter.find(collectionName, criteria, opts);

            if (opts.raw) {
                return res;
            } else {
                res = Model.instantiate(res);
                return res;
            }

        }

        /**
         * Counts all items matching query criteria.
         *
         * @param {array} criteria - query array.
         * @returns {*}
         */
        static async count(criteria) {

            const count = await adapter.count(collectionName, criteria);

            return count;
        }

        /**
         * Searches for and Returns only ONE item.
         *
         * @param {Array} criteria
         * @param {object} options
         * @param {boolean} options.raw - if true, result will not be modelized. (But it will still respect the Jollof Id rule)
         * @param {object} options.paging
         * @param {number} options.paging.page
         * @param {number} options.paging.limit
         * @param {object} options.sort - e.g {fieldA: 1, fieldB: -1}. 1 is asc, -1 is desc
         * @returns {*}
         */
        static async findOne(criteria, options = {}) {

            const opts = _.clone(options);
            opts.paging = opts.paging || {};
            opts.paging.limit = 1;

            let res = await Model.find(criteria, opts);

            if (Array.isArray(res)) {
                res = res.length > 0 ? res[0] : null;
            }

            return res;
        }

        /**
         * Finds by json match
         * @param match json match
         * @param opts
         * @returns {Promise.<*>}
         */
        static async findBy(match, opts = {}) {

            let res = await Model.find(objToCriteria(match), opts);

            return res;
        }

        /**
         * Finds by json match
         * @param match json match
         * @param opts
         * @returns {Promise.<*>}
         */
        static async countBy(match, opts = {}) {

            let res = await Model.count(objToCriteria(match), opts);

            return res;
        }

        /**
         * updates all matching condition with data.
         *
         * @param {array} criteria -
         * @param data
         *
         * @returns {*} A number signifying number of items updated.
         */
        static async update(criteria, data) {

            const updateCount = await adapter.update(collectionName, criteria, data);
            return updateCount;
        }

        static async updateById(id, data) {

            const updateCount = await adapter.update(collectionName, [['id', '=', id]], data);
            return updateCount;
        }

        static async updateBy(match, data) {

            const updateCount = await adapter.update(collectionName, objToCriteria(match), data);
            return updateCount;
        }

        /**
         * Create or update something
         * @param data
         * @param params
         * @returns {*}
         */
        static async persist(data, params = {}) {

            const newItem = new Model(data);
            await newItem.save();

            const res = params.raw ? newItem.display() : newItem;
            return res;
        }

        /**
         * Delete all matching the criteria.
         * Will throw error if no conditions are given.
         * JollofJS will never wipe all your data unless env= test.
         *
         * @param criteria
         * @returns {*}
         */
        static async remove(criteria) {

            if (!criteria && criteria.length === 0) {
                throw new Boom.badRequest('Cannot remove with zero conditions. Please specify at least one condition to remove.')
            }

            return await adapter.remove(collectionName, criteria);
        }

        static async removeById(id) {

            return await adapter.remove(collectionName, [['id', '=', id]]);
        }

        static async removeBy(match, opts) {

            return await adapter.remove(collectionName, objToCriteria(match));
        }


        /**
         * Loads data into the model.
         *
         * @param data
         * @private
         */
        _loadData(data) {
            this._originalData = _.clone(data);
            this._data = _.clone(data);
        }

        isPersisted() {
            if (this._data.id)
                return true;
            else
                return false;
        }

        _setDateCreated() {
            if (!this._data.dateCreated)
                this._data.dateCreated = new Date();
        }

        _setDateUpdated() {
            this._data.lastUpdated = new Date();
        }

        _setTimestamps() {
            this._setDateCreated();
            this._setDateUpdated();
        }

        async validate() {
            try {

                this._data = await isValidValidate(this._data, modelRules.schema, undefined, undefined);
            } catch (err) {
                throw err;
            }
        }

        //HOOKS

        async _preSave() {
            if (schema.hooks && schema.hooks.preSave) {
                await schema.hooks.preSave.bind(this)();
            }
        }

        async _preCreate() {
            if (schema.hooks && schema.hooks.preCreate) {
                await schema.hooks.preCreate.bind(this)();
            }
        }

        async _postSave() {
            if (schema.hooks && schema.hooks.postSave) {
                await schema.hooks.postSave.bind(this)();
            }
        }

        async _postCreate() {
            if (schema.hooks && schema.hooks.postCreate) {
                await schema.hooks.postCreate.bind(this)();
            }
        }

        async _preRemove() {
            if (schema.hooks && schema.hooks.preRemove) {
                await schema.hooks.preRemove.bind(this)();
            }
        }

        async _postRemove() {
            if (schema.hooks && schema.hooks.post && schema.hooks.postRemove) {
                await schema.hooks.postRemove.bind(this)();
            }
        }

        /**
         * Used internally and within this model instance's hooks to save itself.
         * Does not trigger hooks or validation
         * @private
         */
        async _save() {
            //const res = await adapter.saveModel(this);
            //return res;


            if (this.isPersisted()) {

                await adapter.update(collectionName, [['id', '=', this.id]], this._data)
            }
            else {
                let res = await adapter.create(collectionName, this._data);
                this._loadData(res);
            }

            this._originalData = _.clone(this._data);


            return this;
        }

        get id() {
            return this._data.id;
        }

        /**
         * Persists the model object in the DB
         * @returns {boolean}
         */
        async save() {
            try {

                if (this._isDeleted) throw new Error('Cannot save: Already deleted')

                // log.debug('Saving ' + schema.name);
                const creating = !this.isPersisted();

                this._preValidationData = _.cloneDeep(this._data);

                //pre
                if (creating) {
                    await this._preCreate();
                }
                await this._preSave();

                this._setTimestamps();
                //validate
                //try {
                await this.validate();
                //}
                //catch(validationError){
                //	log.debug(validationError);
                //	return {error: validationError};
                //}


                //Do actual save
                const res = await this._save();

                if (creating) {
                    await this._postCreate();
                }
                await this._postSave();

                //by now we should be done with preValidationData
                delete this._preValidationData;


                //log.debug('ids', 'data', this._data);

                return res;
            } catch (err) {
                if (!err.isOperational) {
                    log.error(err.stack);
                }

                throw err;
            }
        }

        /**
         * Marks this item as deleted.
         * Severs ties with stuff and prepare this object as much as possible for GC.
         * @private
         */
        _markDeleted() {

            this._data = null;
            this._isDeleted = true;
        }

        /**
         * Delete this item from the database
         * @returns {boolean}
         */
        async remove() {

            await this._preRemove();

            await adapter.remove(model._collectionName, [['id', '=', this.id]]);

            await this._postRemove();

            this._markDeleted();
            return true;
        }

        /**
         * The json representation of this model's combined data.
         */
        display() {
            let disp = _.cloneDeep(this._data);

            return disp;
        }

        toString() {
            return JSON.stringify(this._data);

        }

        toJSON() {
            return this.display();
        }

        static async runNativeQuery(queryName, params) {
            if (schema.nativeQueries && schema.nativeQueries[queryName] && schema.nativeQueries[queryName][dataSourceName]) {
                let queryFunc = schema.nativeQueries[queryName][dataSourceName];
                return await queryFunc(params, adapter._db);
            }
            else {
                log.error(`${schema.name}: No such native query '${queryName}' exists for the ${dataSourceName} engine. Did you forget to create it?`)
            }
        }

    };


    /**
     * setup instance methods
     */

    _.each(schema.methods, (func, funcName) => {
        Model.prototype[funcName] = func;
    });

    /**
     * setup static methods
     */
    _.each(schema.statics, (v, k) => {
        Model[k] = v;
    });


    /**
     * Setup native functions
     */
    if (schema.native) {
        //First grab the set of native functions by this Collection's active native type.
        // A collection can only have 1 active native type per run.
        const activeNativeFunctions = schema.native[activeNativeType];

        //Put native funcs in addapter's context by Binding
        for (let funcName in activeNativeFunctions) {
            activeNativeFunctions[funcName] = activeNativeFunctions[funcName].bind(adapter.getBindable ? adapter.getBindable() : adapter)
        }

        const nativeProxyAcessor = {
            set(target, key, value) {

                throw new Error('You cannot set a Jollof Native function')

                return false;
            },
            get(target, key) {

                //if key exists in data's keys, get from there. otherwise get from the model
                if (activeNativeFunctions && activeNativeFunctions[key])
                    return activeNativeFunctions[key];
                else
                    throw new Error(`The native function '${key}' has not been defined for nativeType '${activeNativeType}' in model '${collectionName}'.
                    You are likely seeing this because another nativeType has the '${key}' function defined 
                    `)
            }
        };

        const nativeProxy = new Proxy({}, nativeProxyAcessor);
        Model.native = nativeProxy;
    }
    else {
        //in-case a model has static native overriden via methods
        Model.native = Model.native || {};
    }


    // This way we can new jollof.models.user({firstName:'Joe'})
    return Model;
};

<|start_filename|>packages/jollof/lib/admin/rClient/util/popup.js<|end_filename|>
/**
 * Created by iyobo on 2017-01-17.
 */

const nprogress = require('nprogress');

class Popup {

	constructor() {

	}

	error(msg) {
		$.notify(msg, {
			type: 'danger'
		});
	}

	info(msg) {
		$.notify(msg, "info");
	}

}

export default new Popup();

<|start_filename|>packages/jollof/test/testRest.js<|end_filename|>
/**
 * Created by iyobo on 2017-01-01.
 */
const chai = require('chai');
const should = chai.should();
const expect = chai.expect;
const assert = chai.assert;
const _ = require('lodash');

const co = require('co');
const jollof = require('../');
const Foo = jollof.models.Foo;
var request = require('supertest');

const apiRoot = '/api/v1/resource';
const fooApiRoot = apiRoot+'/Foo';

beforeEach('Clear DB', async () => {
    const deletionThunks = [];

    _.each(jollof.models, (model, name) => {
        deletionThunks.push(async () => {
            return await model.remove({}, { multi: true })
        });
    });

    return Promise.all(deletionThunks)
});

describe('Jollof REST', function () {

	describe('Resource API', function () {

		it('lists all Resources', function () {

			return co(function*() {
				try {
					expect(jollof.serverApp).to.exist;


					const response = yield request(jollof.serverApp.listen()).get(apiRoot).expect(200).expect('Content-Type', /json/)

					expect(response.body).to.exist;
					assert(Array.isArray(response.body), true, 'Response is not an array of Models');
					assert.isAtLeast(response.body.length, 1);

					return response;
				} catch (err) {
					throw err;
				}

			});

		});

		//it('find with filter', function () {
		//
		//	return co(function*() {
		//		try {
		//
		//			expect(jollof.serverApp).to.exist;
		//
		//			//yield request(jollof.serverApp.listen()).post(fooApiRoot)
		//			//	.field('name', '<NAME>')
		//			//	.field('age', 25)
		//			//	.expect(200).expect('Content-Type', /json/);
		//			//
		//			//yield request(jollof.serverApp.listen()).post(fooApiRoot)
		//			//	.field('name', '<NAME>')
		//			//	.field('age', 999)
		//			//	.expect(200).expect('Content-Type', /json/);
		//
		//			const john = new Foo({ name: '<NAME>', age: 25 });
		//			yield* john.save();
		//			//console.log(john)
		//
		//			const cain = new Foo({ name: '<NAME>', age: 999 });
		//			yield* cain.save();
		//
		//			const response = yield request(jollof.serverApp.listen()).get(fooApiRoot)
		//				//.field('filters[0][0]', 'age')
		//				//.field('filters[0][1]', '==')
		//				//.field('filters[0][2]', 25)
		//				.expect(200).expect('Content-Type', /json/);
		//
		//			expect(response.body).to.exist;
		//			assert(Array.isArray(response.body), true, 'Response is not an array of Models');
		//			assert(response.body.length,2,'List should be of length 2');
		//
		//			return response;
		//		} catch (err) {
		//			throw err;
		//		}
		//
		//	});
		//
		//});


	});
});


<|start_filename|>packages/jollof/lib/algorithms/sorting/qsort.js<|end_filename|>
/**
 * Created by iyobo on 2016-05-25.
 */

function swap(arr,a,b){
	var temp = arr[a]
	arr[a] = arr[b]
	arr[b] = temp
}
function sort(arr){

	var n = arr.length
	var walli=0
	var pivoti = n-1

	for(var i=0; i< n; i++){
		if(arr[i] < arr[pivoti]){

		}
	}

	return arr;
}

console.log(sort([23,42,4,16,8,15,42]));

<|start_filename|>packages/jollof/lib/loadModels.js<|end_filename|>
/**
 * Created by iyobo on 2016-08-08.
 */
var keystone = require('keystone')
//Models, Specifically Mongoose
var mongoose = require('mongoose');

const config = require('./configurator');
const appPaths = require('../appPaths');

var dbURI = config.settings.db.mongodb.url;
const path = require('path');


// Create the database connection
mongoose.connect(dbURI);

// CONNECTION EVENTS
// When successfully connected
mongoose.connection.on('connected', function () {
	console.log('Mongoose default connection open to ' + dbURI);
});

// If the connection throws an error
mongoose.connection.on('error', function ( err ) {
	console.log('Mongoose default connection error: ' + err);
});

// When the connection is disconnected
mongoose.connection.on('disconnected', function () {
	console.log('Mongoose default connection disconnected');
});

// If the Node process ends, close the Mongoose connection
process.on('SIGINT', function () {
	mongoose.connection.close(function () {
		console.log('Mongoose default connection disconnected through app termination');
		process.exit(0);
	});
});

keystone.init({
	'name': 'Keystone',
	'brand': 'LivingHired',
	// 'less': 'public',
	'static': 'static',
	// 'favicon': '../static/images/logo.png',
	// 'views': 'templates/views',
	// 'view engine': 'jade',
	// 'emails': 'templates/emails',
	'auto update': false,
	'session': true,
	'auth': true,
	'user model': 'User',
	'cookie secret': config.settings.crypto.secrets[ 0 ],
	'google api key': config.settings.google.map.key,
	port: config.settings.server.keystonePort,
	mongoose: mongoose
});

// BRING IN SCHEMAS & MODELS
var schemaLoc = '../../../app/mongomodels';
var schemaPath = path.join(__dirname, schemaLoc);
var models = {}
require("fs").readdirSync(schemaPath).forEach(function ( file ) {
	var out = require(path.join(schemaLoc, file));
	models[ out.name ] = out.model
});

module.exports = {
	models: models,
	mongoose: mongoose
}

<|start_filename|>packages/jollof/lib/bootstrapper/bootstrap.js<|end_filename|>
/**
 * Created by iyobo on 4/22/16.
 */
const appPaths = require("../../appPaths");
const co = require('co');
const log = require('../log');
const path = require('path');

const data = require('../data');
const IO = require('koa-socket')
const io = new IO()
//const kerror = require('koa-error');
const koa = require('koa');
const config = require('../configurator');

const kc = require('koa-controller-jollof');
const koaNunjucks = require('koa-nunjucks-2');
const session = require('koa-generic-session');
const redisStore = require('koa-redis');
const serve = require('koa-static-server');
const jollof = require('../../index');
const subdomain = jollof.router.subdomain;
let serverApp;
var kBetterBody = require('koa-better-body')
//var kBody = require('koa-body')
const kValidate = require('koa-better-validation');

const httpUtil = require('../util/httpUtil');
const Boom = require('boom');

const CSRF = require('koa-csrf');

var helmet = require('koa-helmet')
const Router = require('koa-jollof-router');

const convert = require('koa-convert') // necessary until all have been updated to support koa@2
const fs = require('fs')

let modelsLoaded = false;

function* loadModels() {
    if (modelsLoaded) return;
    modelsLoaded = true;

    for (let m in data.models) {
        const modelClass = data.models[m];
        try {
            yield modelClass.setup();
            log.debug('Setup ' + modelClass.collectionName)
        } catch (err) {
            // log.error(err);
            log.error('Error in LoadModels', err.stack)
        }
    }
}


/**
 * Default bootstrap behavior
 * @param fn
 * @returns {*}
 */
module.exports.boot = function (fn) {
    return co(function* () {

        //Load models
        yield loadModels();

        //Initialize Bootstrap globals. Access these anywhere in the app e.g env.settings.appname
        log.info("[Jollof] Starting Jollof Server app... \n APPROOT: " + appPaths.appRoot);

        //Run the application
        if (fn)
            return yield fn(process.argv.slice(2));


    }).catch(function (err) {
        log.error("[bootstrapper] Gracefully handled exception...")
        log.error(err.stack)
        process.exit(1)
    })
}

module.exports.bootStandAlone = function (fn) {

    return module.exports.boot(fn);
}

/**
 *
 * @param overWriteFn
 * @param donNotAutoStart - if true, bootstraper will NOT automatically run app.listen(...)
 *                          because the jollof app is using app.Callback(...) somewhere in the overWriteFn.
 * @returns {*}
 */
module.exports.bootServer = function (overWriteFn, donNotAutoStart) {

    return co(function* () {

        //try {

        //Load models
        yield loadModels();

        //Initialize Bootstrap globals. Access these anywhere in the app e.g env.settings.appname
        log.info("[Jollof] Starting Jollof Server app... \n APPROOT: " + appPaths.appRoot);

        //Run the application

        //Set'em up------------------------------------------------------
        serverApp = new koa({
            // qs: true
        });

        // trust proxy
        serverApp.proxy = true;


        //wear helmet for security
        serverApp.use(helmet());

        // serverApp.use(kBody());
        serverApp.use(convert(kBetterBody({
            'multipart': true,
            jsonLimit: '20mb',
            textLimit: '20mb',
            formLimit: '20mb',
            urlencodedLimit: '20mb',
            querystring: require('qs')
        })));

        //koa-better-body annoyingly doesn't put fields in body. Fixes that
        serverApp.use(convert(function* (next) {
            if (this.request.fields) {
                this.request.body = this.request.fields;
            }
            return yield next;
        }));

        //Sessions
        serverApp.keys = jollof.config.crypto.secrets;
        //serverApp.use(convert(session(jollof.config, serverApp)));
        serverApp.use(convert(session({
            store: redisStore(jollof.config.sessions.redis),
            // store: require("koa-generic-session/lib/memory_store")
        })));

        // add the CSRF middleware
        //serverApp.use(new CSRF({
        //    invalidSessionSecretMessage: 'Invalid session secret',
        //    invalidSessionSecretStatusCode: 403,
        //    invalidTokenMessage: 'Invalid CSRF token',
        //    invalidTokenStatusCode: 403,
        //    excludedMethods: [ 'GET', 'HEAD', 'OPTIONS','DELETE' ],
        //    disableQuery: false
        //}));


        //Make session variables accesible to View renderers
        serverApp.use(convert(function* (next) {
            this.state.session = this.session;
            this.state.sessionId = this.sessionId;
            this.state.env = jollof.config.env;
            this.state.config = jollof.config;
            return yield next;
        }));

        /**
         * TODO: Create signature jollof error page
         */
        //if (!JOLLOF_STANDALONE) {
        //	serverApp.use(kerror({
        //		engine: 'nunjucks',
        //		template: appPaths.views + path.sep + 'error.nunj'
        //	}));
        //}

        //server-side form validator
        serverApp.use(convert(kValidate()));


        //app statics
        serverApp.use(convert(serve({ rootDir: 'static', rootPath: '/static' })));


        //Internal jollof statics
        serverApp.use(convert(serve({
            rootDir: path.join('node_modules', 'jollof', 'jollofstatic'),
            rootPath: '/jollofstatic'
        })));

        //Spice statics
        jollof.spices.forEach((it) => {
            if (it.statics) {
                serverApp.use(convert(serve({
                    rootDir: it.statics.rootDir,
                    rootPath: it.statics.rootPath
                })));
            }
        })

        //public file upload statics
        serverApp.use(convert(serve({
            rootDir: jollof.config.fileStorage.engines.local.publicRoot,
            rootPath: jollof.config.fileStorage.engines.local.basePublicUrl
        })));

        //Jollof formdata parsing
        serverApp.use(convert(function* (next) {
            yield httpUtil.objectify(this);
            return yield next;
        }));

        //csrf
        serverApp.use(async (ctx, next) => {

            if (ctx.method === 'GET') {
                ctx.state.csrf = ctx.csrf;
            }

            return next();
        });

        //nunjucks
        let nunjConfig = jollof.config.nunjucks;

        //add spice nunjucks view paths
        jollof.spices.forEach((it) => {
            if (it.views) {
                nunjConfig.path.push(it.views.path)
            }
        })

        jollof.config.nunjucks.configureEnvironment = (env) => {
            jollof.view.setupFilters(env)
        };
        serverApp.use(koaNunjucks(jollof.config.nunjucks));

        //Router
        let router = new Router();

        ////APP ROUTES
        const newRoutePath = process.cwd() + '/app/routes.js';
        const oldRoutePath = process.cwd() + '/app/routes/default.js';
        if (fs.existsSync(newRoutePath))
            router.addRoutes(require(newRoutePath));
        else
            router.addRoutes(require(oldRoutePath));


        //}


        //If Admin is enabled
        if (jollof.config.admin.enabled) {

            router.nestRoutes(jollof.config.admin.routePrefix, jollof.config.admin.auth, require('../admin/adminRoutes'))
        }

        //spice routes
        jollof.spices.forEach((it) => {
            if (it.routes) {
                const spiceConfig = jollof.config.spices[it.name];
                router.nestRoutes(spiceConfig.mountPath, spiceConfig.auth, it.routes)
            }
        })

        //Give Framework user a chance to set things up or mount stuff
        if (overWriteFn)
            yield overWriteFn(serverApp);


        serverApp.use(router.router.routes());
        serverApp.use(router.router.allowedMethods({
            throw: true,
            notImplemented: () => new Boom.notImplemented(),
            methodNotAllowed: () => new Boom.methodNotAllowed()
        }));


        //WEB SERVER-----------------------------------------------------
        if (!donNotAutoStart) {
            console.log('Using default app listen')
            serverApp.listen(jollof.config.server.port);
        }
        else {
            console.log('Deferring server start to a callback process')
        }

        jollof.log.info("Server started on port " + jollof.config.server.port)


        jollof.serverApp = serverApp;

        return serverApp;
        //} catch (err) {
        //    log.error(err.stack)
        //}

    }).catch(function (err) {
        log.error("[bootstrapper] Gracefully handled exception...")
        log.error(err.stack)
        process.exit(1)
    })
}

module.exports.bootStandAloneServer = function (fn) {

    return module.exports.bootServer(fn);
}

<|start_filename|>packages/jollof/lib/util/appUtil.js<|end_filename|>
exports.hijackConsole = () => {
    var origlog = console.log;

    console.log = function (obj, ...placeholders) {
        if (typeof obj === 'string')
            placeholders.unshift(new Date() + " " + obj);
        else {
            // This handles console.log( object )
            placeholders.unshift(obj);
            placeholders.unshift(Date.now() + " %j");
        }

        origlog.apply(this, placeholders);
    };

    var origerr = console.err;

    console.err = function (obj, ...placeholders) {
        if (typeof obj === 'string')
            placeholders.unshift(new Date() + " " + obj);
        else {
            // This handles console.log( object )
            placeholders.unshift(obj);
            placeholders.unshift(Date.now() + " %j");
        }

        origerr.apply(this, placeholders);
    };
}

<|start_filename|>packages/jollof/lib/crypto.js<|end_filename|>
'use strict'
var bcrypt = require('bcryptjs');
const settings = require('./configurator').settings;

class Crypto{
	async hash(password) {
		if (!password || password==="") return null; //don't waste time hashing nothing

        var salt = await bcrypt.genSaltSync(settings.crypto.saltrounds);
        return await bcrypt.hash(password, salt);
	}

	async compare(password,hash){
        return await bcrypt.compare(password,hash)
	}
}

module.exports = new Crypto()

<|start_filename|>packages/jollof/lib/data/types/url.js<|end_filename|>
const registry = require('../registry');

/**
 * This is an in-built Schema Type.
 * Schema Types are schemas themselves, and can be used inside other schemas.
 *
 * This type is a singular Email field def.
 */
const typeDef = {
	name: 'Url',
    required: false,
	structure: {
        type: String,
		trim: true,
		match: /(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&amp;:\/~+#-]*[\w@?^=%&amp;\/~+#-])?/
    }
}

module.exports = registry.registerType(typeDef);

<|start_filename|>packages/jollof/lib/algorithms/heaps.js<|end_filename|>
/**
 * Created by iyobo on 2016-04-24.
 */
/*
 Heap's algorithm generates all possible permutations of N objects
 */

function swap(array,i1, i2){
	if(!Array.isArray(array))
	{
		console.error("Not an array"+ array)
		return;
	}
	// console.log("swapping from "+array)
	var buffer = array[i1]
	array[i1] = array[i2]
	array[i2] = buffer
	// console.log("to "+array)
}

function generateAllCombinations(n,a){
	if (n===1)
		console.log(a)
	else{
		for (var i=0; i<n-1; i++){
			generateAllCombinations(n-1,a)
			if(n%2==0){
				swap(a,1,n-1)
			}
			else
			{
				swap(a,0,n-1)
			}
		}
		generateAllCombinations(n-1,a)
	}
}

var A = ["red","yellow","green","meat"]
var N = A.length

generateAllCombinations(N,A)

module.exports = generateAllCombinations()

// function fibonacci(n){
// 	var cur= [];
// 	for(var i=0; i<n; i++){
//
// 		var sum=0;
// 		if(i==1)
// 			sum = 1;
//
// 		if(cur.length>1){
// 			var first = cur.length -1;
// 			var second = 0;
//
// 			if(cur.length >= 2)
// 				second = cur.length-2;
//
// 			sum += cur[first]+cur[second];
// 		}
// 		cur.push(sum);
// 	}
//
// 	console.log(cur);
// }

<|start_filename|>packages/jollof/bin/commandLauncher.js<|end_filename|>
'use strict'

const bootstrap = require('./bootstrap');
const appPaths = require('../../appPaths');
const path = require('path');

bootstrap.boot(function*(params) {

    /*
     This runs the specified content of the scripts folder
     expecting params to be script, action, optional param
     */
    //find script
    var scriptName = params[0]
    var actionName = params[1]
    var paramName = params[2]
    if (!scriptName || !actionName) {
        log.error(`
		Incomplete Entry. Jollof commands needs to be executed as such:
		. jollof <script> <action> [optional_parameter]
		
		e.g. ./jollof data migrate
		`)
        process.exit(1)
    }


    try {
        var script = require('../../../../commands' + path.sep + scriptName)
    } catch (err) {
        var script = require('../commands' + path.sep + scriptName)
    }
    yield script[actionName](paramName)

})


<|start_filename|>packages/jollof/lib/algorithms/gnerators.js<|end_filename|>
/**
 * Created by iyobo on 2016-07-21.
 */
function * fen (){
	var val = yield Promise.resolve('Me')
	console.log("fenning",val)
}

yield fen();

<|start_filename|>packages/jollof/lib/filestorage/engines/s3.js<|end_filename|>
/**
 * Created by iyobo on 2016-11-27.
 */


<|start_filename|>packages/jollofjs/lib/actions.js<|end_filename|>
/**
 * Created by iyobo on 2017-05-21.
 */
var chalk = require('chalk');
var path = require('path');
const editJsonFile = require("edit-json-file");
const childProc = require('child_process');
var ghdownload = require('github-download')
var os = require('os');
var ncp = require('ncp').ncp;
ncp.limit = 16;

/**
 * Run a command in a project
 * @param args
 * @param args._ - array of unkeyed values
 * @returns {Promise.<void>}
 */
exports.run = async function (args) {

    if (args._.length <= 0) {
        throw new Error('No run command specified i.e jollof run <command>')
    }

    const currentLocation = process.cwd();
    const command = args._[0];

    console.log(`Preparing to run command "${command}" at "${currentLocation}"...\n`);
    args._ = args._.slice(1);

    require(path.join(currentLocation, 'commands', command))(args);
}

/**
 * Create/Scaffold new project
 * @param args
 * @param args._[0] - projectName
 * @returns {Promise.<void>}
 */
exports.new = async function (args) {

    if (args._.length <= 0) {
        throw new Error('No projectName specified i.e jollof new <projectName>')
    }

    const currentLocation = process.cwd();
    const projectName = args._[0];
    const mvpUrlScaffold = 'https://github.com/iyobo/jollof-app-mvp';
    const projectLocation = path.join(currentLocation, projectName);

    console.log('Downloading recipe: ' + mvpUrlScaffold + '...');

    // First download the github
    await new Promise((resolve, reject) => {
        ghdownload(mvpUrlScaffold, projectLocation)
            .on('error', function (err) {
                reject(err);
            })
            .on('end', function (dir) {
                console.log(chalk.yellow('Cooking...'));
                resolve(dir)
            })
    });


    console.log(chalk.green("Bon apetit!\n"));
    console.log(chalk.yellow("To run:"));
    console.log(chalk.green(`- cd '${projectLocation}'`));
    console.log(chalk.green(`- npm i `));
    console.log(chalk.green(`- npm run`));

}


/**
 * Show help
 * @param args
 * @returns {Promise.<void>}
 */
exports.help = async function (args) {

    console.log(`
Command Line Interface for running Jollof actions.    
`);

    console.log(chalk.green('jollof run <commandName> [...params]'));
    console.log('  Runs a command in a JollofJS app. \n ');

    console.log(chalk.green('jollof new <projectName> [...params]'));
    console.log('  Creates a new JollofJS app.\n ');

    console.log(chalk.green('jollof help'));
    console.log('  This Display.\n ');

    console.log('\n(c) 2017 - <NAME>\n')
}

<|start_filename|>packages/jollof/testme.js<|end_filename|>
/**
 * Created by iyobo on 2017-01-10.
 */
const jollof = require('./index');
const co = require('co');
var jql = require('./lib/data/index.js').jql;

co(function*() {
    yield jollof.bootstrap.boot()

    const Foo = jollof.models.Foo;

    const james = new Foo({ name: '<NAME>', age: 31 });
    const tom = new Foo({ name: '<NAME>', age: 35 });

    yield james.save();
    yield tom.save();

    const person = yield Foo.findOne(jql`age=${35}`);

    yield Foo.native.pityFoo('Alexander');

    yield Foo.findNear(2, 5, 4, 6)

    console.log('RESULT', person.display());
}).catch((err) => {
    console.error(err);
});


<|start_filename|>packages/jollof/lib/data/index.js<|end_filename|>
/**
 * Created by iyobo on 2016-10-27.
 */
const registry = require('./registry');

/**
 * These module loaders should be dumb.
 * All they should do is run files, who should registers themselves.
 *
 * @type {{registerModel, registerType, types: *, models: *}}
 */
const jollofQL = require('./jollofql');

module.exports ={
	registerModel: registry.registerModel,
	registerType: registry.registerType,
	registerService: registry.registerService,
	types: registry.types,
	models: registry.models,
	services: registry.services,
	init: registry.init,
    jollofql: jollofQL,
    parser: jollofQL.parser,
    jql: jollofQL.jql
}

<|start_filename|>packages/jollof/lib/configurator/index.js<|end_filename|>
'use strict'

/**
 * Builds the Eluvian config object.
 * Copyright(c) 2015-2016 <NAME>
 * MIT Licensed
 */

const appPaths = require('../../appPaths');
const log = require('../log');
const path = require('path');
const _ = require('lodash');

class Env {

    constructor(Helpers) {

        this.currentEnv = process.env.NODE_ENV || 'development';
        this.settings = {};

        //load base
        try {
            this.settings = require('./base');

            if (this.currentEnv !== 'test') {

                const appBase = require(appPaths.baseConfig);

                _.merge(this.settings, appBase)

                //load environment config file and merge if exists
                try {
                    var envSettings = require(path.join(appPaths.config, this.currentEnv));
                    _.merge(this.settings, envSettings);
                } catch (e) {
                    console.warn(`config/${this.currentEnv}.js not found. Using base alone`)
                }
            }

            console.log('[Environment]', this.currentEnv, 'settings loaded.');

        } catch (err) {
            log.error('Jollof config not loaded:', err);
        }
    }


    /**
     * @description get value of an existing key from
     * env file
     * @method get
     * @param  {String} key
     * @param  {Mixed} defaultValue
     * @return {Mixed}
     */
    get(key, defaultValue) {
        return this.settings[key] || defaultValue;
    }

    /**
     * @description set value of an existing .env variable
     * @method set
     * @param  {String} key
     * @param  {Mixed} value
     * @public
     */
    set(key, value) {
        this.settings[key] = value;
    }


}

module.exports = new Env();


<|start_filename|>packages/jollof/lib/admin/rClient/data/stores/ModelStore.js<|end_filename|>
import axios from 'axios';
import {getState, storeState} from 'react-spoon';
/**
 * Created by iyobo on 2017-02-12.
 */
import {action, extendObservable, observable, toJS} from 'mobx';
import convertToFormData from '../../util/convertToFormData';

const defaultQuery = {
    paging: {
        page: 1,
        limit: 10
    },
    sort: { dateCreated: -1 },
    conditions: ''
};

class ModelStore {

    @observable models = [{ name: 'Loading' }];
    @observable modelHash = {};
    @observable active = {
        single: observable.map({}),
        totalItems: 0,
        totalPages: 0,
        table: [],
        model: {}
    };
    //@observable modelTable = [];
    //@observable modelSingle = {};

    @observable modelQuery = {
        paging: {
            page: 1,
            limit: 10
        },
        sort: { dateCreated: -1 },
        conditions: ''
    };

    //Store for the active Ref dialog
    @observable refActive = {
        totalItems: 0,
        totalPages: 0,
        table: [],
        model: {}
    };
    @observable refModelQuery = {
        paging: {
            page: 1,
            limit: 10
        },
        sort: { dateCreated: -1 },
        conditions: ''
    };

    @observable loadingCount = 0;


    reportError = (error) => {

        let msg = error.message;

        if (error.statusCode === 401) location.reload();


        if (error.response && error.response.data && error.response.data.keyPath) {
            const data = error.response.data;
            msg = 'Invalid Input at <b>' + data.keyPath.join('.') + '</b>: ' + data.message;
            //console.error(msg);
        }else {
            msg = error.response.data;
            //console.error(error.response.data);
        }

        $.notify({
            icon: "pe-7s-attention",
            message: msg

        }, {
            type: 'danger',
            delay: 2000,
            placement: {
                from: 'top',
                align: 'right'
            }
        });
    }

    ////====== Table QUERY FILTER actions
    changeQueryConditions(str) {
        this.modelQuery.conditions = str;
        storeState(this.getStateKey(), this.modelQuery);
        //console.log({real: str})
    }
    changeRefQueryConditions(str) {
        this.refModelQuery.conditions = str;
        //console.log({ref: str})
    }

    reportSuccess = (msg) => {
        console.log(msg);

        $.notify({
            icon: "pe-7s-check",
            message: msg

        }, {
            type: 'success',
            delay: 2000,
            placement: {
                from: 'top',
                align: 'right'
            }
        });
    }

    fetchModels = () => {
        this.loadingCount++;
        return axios.get(apiPrefix+ '/api/v1/resource').catch((error) => {
            this.loadingCount--;
            this.reportError(error);
        }).then((res) => {
            this.loadingCount--;
            if (res) {
                const data = res.data;
                //run in action?
                this.models = data;

                //create a hash of resources mapped to their name
                this.modelHash = {};
                _.each(data, (it) => {
                    this.modelHash[it.name] = it;
                })

                return data;
            }
        });
    }

    fetchTable = () => {
        this.loadingCount++;

        //NOTE: If we are fetching tables, clean up old model data
        this.active.single.replace({});

        return axios.get(apiPrefix+ '/api/v1/resource/' + this.active.model.name, { params: this.modelQuery }).catch((error) => {
            this.loadingCount--;
            this.reportError(error);
        }).then((result) => {
            this.loadingCount--;
            if (result) {

                //console.log(result);

                this.active.totalItems = result.headers['jollof-total-count'];
                this.active.totalPages = Math.ceil(this.active.totalItems / this.modelQuery.paging.limit);

                this.active.table = result.data;

                //store the query of this load in the url for URL reproducability
                storeState(this.getStateKey(), this.modelQuery);

                //Now also store it in session Storage for navigational reproducability
                sessionStorage.setItem(this.active.model.name + '.list', window.location.href);

                return result.data;
            }


        });
    }

    fetchRefTable = () => {
        this.loadingCount++;

        //NOTE: If we are fetching tables, clean up old model data
        //this.activeRef.single.replace({});

        return axios.get(apiPrefix+ '/api/v1/resource/' + this.refActive.model.name, { params: this.refModelQuery }).catch((error) => {
            this.loadingCount--;
            this.reportError(error);
        }).then((result) => {
            this.loadingCount--;
            if (result) {

                //console.log(result);

                this.refActive.totalItems = result.headers['jollof-total-count'];
                this.refActive.totalPages = Math.ceil(this.refActive.totalItems / this.refModelQuery.paging.limit);

                this.refActive.table = result.data;

                return result.data;
            }


        });
    }

    fetchItem = (id, noSave) => {
        this.loadingCount++;

        return axios.get(apiPrefix+ '/api/v1/resource/' + this.active.model.name + '/' + id).catch((error) => {
            this.loadingCount--;
            this.reportError(error);
        }).then((result) => {
            this.loadingCount--;
            //run in action?
            //data.crono = null;
            if (result) {
                if(!noSave)
                    this.active.single.replace(result.data);

                //this.active.single.set('crono' ,new Date());

                return result.data;
            }
        });
    };

    fetchSpecificItem = (collectionName, id) => {
        this.loadingCount++;

        return axios.get(apiPrefix+ '/api/v1/resource/' + collectionName + '/' + id).catch((error) => {
            this.loadingCount--;
            this.reportError(error);
        }).then((result) => {
            this.loadingCount--;
            //run in action?
            //data.crono = null;
            if (result) {

                return result.data;
            }
        });
    };

    //----Update actions------

    saveActiveItem = (id) => {
        const activeData = toJS(this.active.single);
        console.log('Updating...', activeData);
        this.loadingCount++;

        //First convert to formdata
        const formData = convertToFormData(activeData);

        return axios.patch(apiPrefix+ '/api/v1/resource/' + this.active.model.name + '/' + id, formData, {}).catch((error) => {
            this.loadingCount--;
            this.reportError(error);
        }).then((res) => {
            this.loadingCount--;
            if (res) {
                this.reportSuccess(`${this.active.model.name}:<b>${id}</b> updated successfully!`);
                //success

                return res.data;
            }
        });
    }

    createActiveItem = () => {
        const activeData = toJS(this.active.single);
        console.log('Creating...', activeData);
        this.loadingCount++;

        //First convert to formdata
        const formData = convertToFormData(activeData);

        return axios.post(apiPrefix+ '/api/v1/resource/' + this.active.model.name, formData, {}).catch((error) => {
            this.loadingCount--;
            this.reportError(error);
        }).then((res) => {
            this.loadingCount--;

            if (res) {
                this.reportSuccess(`${this.active.model.name} created successfully!`);
                //success

                return res.data;
            }
        });
    }

    deleteItem = (id) => {

        return new Promise((resolve, reject) => {

            $.confirm({
                title: 'Are you Sure?',
                content: `Are you sure you want to <b>permanently delete </b> ${this.active.model.name}: <b>${id}</b>?`,
                buttons: {
                    yes: () => {
                        this.loadingCount++;

                        return axios.delete(apiPrefix+ '/api/v1/resource/' + this.active.model.name + '/' + id).catch((error) => {
                            this.loadingCount--;
                            this.reportError(error);

                            return reject();

                        }).then((res) => {
                            this.loadingCount--;
                            if (res) {
                                this.reportSuccess(`${this.active.model.name}:<b>${id}</b> Deleted successfully!`);
                                return resolve(res.data);
                            }
                            //run in action?
                            //return data;
                        });
                    },
                    no: () => {

                    },
                }
            });
        });
    }

    setActiveModel = (name) => {
        //console.log(this.modelHash)
        this.active.table = [];
        this.active.model = this.modelHash[name] || {};
    }

    setActiveRefModel = (name) => {
        //console.log(this.modelHash)
        this.refActive.table = [];
        this.refActive.model = this.modelHash[name] || {};
    }


    getStateKey() {
        //return 'models.'+this.active.model.name;
        return 'models';
    }

    /**
     * FIXME: this should not care about urlstate. use for now to build concept of storedState
     */
    resetQuery = () => {
        const queryKey = this.getStateKey();
        const urlstate = getState(queryKey) || defaultQuery;

        this.modelQuery = Object.assign({}, urlstate);
    }

    //=======PAGING
    incrementPage = () => {
        if (this.modelQuery.paging.page < this.active.totalPages)
            this.modelQuery.paging.page++;

        this.fetchTable()
    }

    decrementPage = () => {
        if (this.modelQuery.paging.page > 1)
            this.modelQuery.paging.page--;

        this.fetchTable();
    }
    incrementRefPage = () => {
        if (this.refModelQuery.paging.page < this.refActive.totalPages)
            this.refModelQuery.paging.page++;

        this.fetchRefTable()
    }

    decrementRefPage = () => {
        if (this.refModelQuery.paging.page > 1)
            this.refModelQuery.paging.page--;

        this.fetchRefTable();
    }


    //SORT
    sortTable(fieldName) {
        if (!this.modelQuery.sort[fieldName]) {
            this.modelQuery.sort = {};
            this.modelQuery.sort[fieldName] = 1;
        }

        this.modelQuery.sort[fieldName] = this.modelQuery.sort[fieldName] * -1;

        this.fetchTable();
    }
    sortRefTable(fieldName) {
        if (!this.refModelQuery.sort[fieldName]) {
            this.refModelQuery.sort = {};
            this.refModelQuery.sort[fieldName] = 1;
        }

        this.refModelQuery.sort[fieldName] = this.refModelQuery.sort[fieldName] * -1;

        this.fetchRefTable();
    }

    onFieldChange(observed, newValue) {
        observed = newValue;
    }

    clearFilters(){
        this.modelQuery.conditions = '';
        this.fetchTable();
    }
    clearRefFilters(){
        this.refModelQuery.conditions = '';
        this.fetchRefTable();
    }

}

export default new ModelStore();


<|start_filename|>packages/jollof/lib/algorithms/test2.js<|end_filename|>
function sum(x){

	return function(y){
		return x+y;
	};
}

console.log(sum(2)(3))

<|start_filename|>packages/jollof/lib/admin/adminRoutes.js<|end_filename|>
/**
 * Created by iyobo on 2016-08-24.
 */
const auth = require('./constraints/adminSecurity');
const admin = require('./controllers/adminController');

module.exports = {
    '/': { flow: admin.index },
    'get /api/v1/resource': { flow: admin.models },
    'get /api/v1/resource/:modelName': { flow: admin.list },
    'get /api/v1/resource/:modelName/:id': { flow: admin.get },
    'post /api/v1/resource/:modelName': { flow: admin.create },
    'put /api/v1/resource/:modelName/:id': { flow: admin.update },
    'patch /api/v1/resource/:modelName/:id': { flow: admin.update },
    'delete /api/v1/resource/:modelName/:id': { flow: admin.delete},

}

<|start_filename|>packages/jollof/lib/util/dataUtils.js<|end_filename|>
/**
 * Created by iyobo on 2017-04-30.
 */
const _ = require('lodash');

/**
 * populates schema with meta.type field.
 * The meta object contains all information needed to render this schema in Jollof-compatible frontends.
 * contains field type (as string), required, etc.
 */
exports.populateMetaTypes = (schema, isChild) => {

    //This should be present after formalization
    schema.meta = schema.meta || {};
    if (schema.type) {
        schema.meta.type = schema.type.name;
    }
    else if (schema.custom) {
        //An ANY field
        schema.meta.type = 'any';
    }
    else {
        throw new Error('Jollof Model Initialization Error: Cannot populateMeta for unFormalized schema. This is likely an internal Jollof bug. Please upgrade to the latest version of JollofJS')
    }

    //recurse for children
    if (schema.schema) {
        let structure = schema.schema;

        //Prepare fieldOrder array
        let fieldOrder = isChild ? [] : ['id'];

        //if it's an array...skip to child schema
        _.each(structure, (childSchema, k) => {

            if (!childSchema.type) {
                exports.populateMetaTypes(childSchema, true);
            }
            else if (childSchema.type.name === 'Array') {
                exports.populateMetaTypes(childSchema.schema, true);
            }
            else {
                exports.populateMetaTypes(childSchema, true);
            }

            if (k !== 'id')
                fieldOrder.push(k);
        });

        schema.fieldOrder = fieldOrder;

    }

}

<|start_filename|>packages/koa-jollof-router/lib/util.js<|end_filename|>
/**
 * Created by iyobo on 2017-05-14.
 */

const KRouter = require('koa-router');

function isPath(it) {

    return it.startsWith('/')
}

exports.makeRoute = (router, method, path, flow, children) => {

    if (flow && !Array.isArray(flow))
        flow = [flow];

    //error handling try/catch wrap
    if (flow) {
        flow = flow.map((it) => {
            return async function (ctx, next) {
                try {
                    return await it(ctx, next)
                } catch (err) {
                    console.error(err.stack);

                    if (err.isBoom && !err.isServer) {
                        ctx.status = err.output.statusCode;
                        ctx.body = err.output.payload;
                    } else {
                        ctx.throw(err);
                    }

                }
            }
        });
    }


    if (!children) {
        if (flow)
            router = router[method](path, ...flow)
        //else
        //    router = router[method](path, ...flow)
        //console.log('route:',method,path, flow.length||1)
    } else {
        //has children
        let subRouter = KRouter();
        subRouter = exports.digestRouteMap(subRouter, children)

        if (flow)
            router.use(path, ...flow, subRouter.routes(), subRouter.allowedMethods());
        else
            router.use(path, subRouter.routes(), subRouter.allowedMethods());

    }


    return router;
}

exports.digestRouteMap = (router, routes) => {

    const keys = Object.keys(routes);

    for (let i in keys) {

        let key = keys[i];
        const value = routes[key];

        //flow and children are the currently supported route fields
        const flow = value.flow;
        const children = value.children;

        //key contains method and path
        key = key.trim();
        let methPath = key.split(' ');
        let method = 'get';
        let path;

        methPath.forEach((it) => {
            it = it.trim();
            if (isPath(it)) {
                path = it;
            } else {
                method = it.toLowerCase();
            }
        });

        //At this point we should have a path!
        if (!path) {
            throw new Error('No path defined for route:', key)
        }

        router = exports.makeRoute(router, method, path, flow, children);

    }

    return router;
}

<|start_filename|>packages/isvalid-jollof/lib/schema.js<|end_filename|>
'use strict';

var merge = require('merge'),
    SchemaError = require('./errors/SchemaError.js');

var supportedTypes = ['Object', 'Array', 'String', 'Number', 'Boolean', 'Date'];

var finalize = function (formalizedSchema, nonFormalizedSchema, fn, sync) {

    // Add the old non-formalized schema - for preventing
    // redundant formalization and for usage by the
    // validator when sending validation errors.
    //
    // Make the property unenumerable.
    Object.defineProperty(formalizedSchema, '_nonFormalizedSchema', {
        value: nonFormalizedSchema,
        enumerable: false,
    });

    // We seal the schema so no futher editing can take place.
    //Object.seal(formalizedSchema); //Remove seal as we need to make changes to this.

    // Allow for I/O if running async.
    if (fn) {
        if (sync) return fn(formalizedSchema, true);
        else setImmediate(fn, formalizedSchema, true);
    }

};

var formalizeObject = function (formalizedSchema, nonFormalizedSchema, fn, sync) {

    // For backwards compatibility be before version 1.0.3
    // we change the allowUnknownKeys to unknownKeys
    if (typeof formalizedSchema.allowUnknownKeys === 'boolean') {
        if (!process.env.ISVALID_SILENCE) {
            console.error('isvalid: DEPRECATED: Validator allowUnknownKeys has been deprecated in favour of unknownKeys as of version 1.0.4. See README for more info.');
        }
        formalizedSchema.unknownKeys = (formalizedSchema.allowUnknownKeys ? 'allow' : 'remove'); //Don't throw an error. either allow, or remove.
        formalizedSchema.wasAllowUnknownKeys = true;
        delete formalizedSchema.allowUnknownKeys;
    }

    // If no sub-schema is provided we consider the schema final.
    if (typeof formalizedSchema.schema === 'undefined') return finalize(formalizedSchema, nonFormalizedSchema, fn, sync);
    if (formalizedSchema.schema.constructor.name !== 'Object') throw new SchemaError(formalizedSchema.schema, 'Object schemas must be an object.');

    // Build new formalized schema into this.
    var formalizedSubschema = {};

    var keys = Object.keys(formalizedSchema.schema);

    (function formalizeNextKey(idx) {

        // If idx equals keys length - we are done.
        if (idx == keys.length) {
            formalizedSchema.schema = formalizedSubschema;
            formalizedSchema = finalize(formalizedSchema, nonFormalizedSchema, fn, sync);
            return;
        }

        var key = keys[idx];

        formalizeAny(formalizedSchema.schema[key], function (formalizedKey) {

            // Apply implicit required if sub-schema is required.
            if (formalizedSchema.required === undefined || formalizedSchema.required == 'implicit') {
                if (formalizedKey.required === true) formalizedSchema.required = true;
            }

            formalizedSubschema[key] = formalizedKey;

            if (sync) return formalizeNextKey(idx + 1);
            else setImmediate(formalizeNextKey, idx + 1);

        }, sync);

    })(0);

    return formalizedSchema;

};

var formalizeArray = function (formalizedSchema, nonFormalizedSchema, fn, sync) {

    // formalizedSchema has been pre-processed by formalizeAny, so
    // we only need to formalize the sub-schema.

    // If no sub-schema is provided we consider the schema final.
    if (typeof formalizedSchema.schema === 'undefined') return finalize(formalizedSchema, nonFormalizedSchema, fn, sync);

    return formalizeAny(formalizedSchema.schema, function (formalizedSubschema) {

        formalizedSchema.schema = formalizedSubschema;

        // Apply implicit required if sub-schema has required data.
        if (typeof(formalizedSchema.required) === 'undefined' || formalizedSchema.required === 'implicit') {
            if (formalizedSchema.schema.required === true) formalizedSchema.required = true;
        }

        return finalize(formalizedSchema, nonFormalizedSchema, fn, sync);

    }, sync);

};

/**
 * Converts shortcuts to full form.
 * Also populates 'meta' field with field type.
 *
 * @param schema
 * @param fn
 * @param sync
 * @returns {*}
 */
var formalizeAny = function (schema, fn, sync) {

    // If no fn we operate sync.
    if (fn === undefined) {
        fn = function (s) {
            return s;
        };
        sync = true;
    }

    // If schema is already formalized we just call back.
    if (typeof schema._nonFormalizedSchema !== 'undefined')
    	return fn(schema, false);

    if (!schema.type && !schema.custom) {
        if ('Object' == schema.constructor.name) {
            return formalizeObject({
                type: Object,
                schema: schema,
                meta: { type: schema.constructor.name },
				unknownKeys: 'remove'
            }, schema, fn, sync);
        }
        if ('Array' == schema.constructor.name) {
            if (schema.length === 0) throw new SchemaError(schema, 'Array must have exactly one schema.');
            return formalizeArray({
                type: Array,
                schema: schema[0],
                meta: { type: schema.constructor.name }
            }, schema, fn, sync);
        }
        if (typeof schema === 'function' && supportedTypes.indexOf(schema.name) > -1) {
            return formalizeAny({ type: schema, meta: { type: schema.name } }, fn, sync);
        }
        throw new SchemaError(schema, 'Schemas must have validators `type` and/or `custom`.');
    }


    // Copy schema.
    var formalizedSchema = merge(true, schema);

    // Validators common to all types.
    var validators = {
        'type': ['Function'],
        'required': ['Boolean', 'String'],
        'default': 'Any',
        'allowNull': ['Boolean'],
        'errors': ['Object'],
        'custom': ['Function', 'Array'],
        'meta': 'Any'
    };

    // Validators specific to type.
    if (formalizedSchema.type !== undefined) {
        if ('Object' == formalizedSchema.type.name) merge(validators, {
            'schema': 'Any',
            'allowUnknownKeys': ['Boolean'], // Deprecated as of version 1.0.4
            'unknownKeys': ['String']
        });
        if ('Array' == formalizedSchema.type.name) merge(validators, {
            'schema': 'Any',
            'len': ['String', 'Number'],
            'unique': ['Boolean'],
            'autowrap': ['Boolean', 'String']
        });
        if ('String' == formalizedSchema.type.name) merge(validators, {
            'match': ['RegExp'],
            'trim': ['Boolean'],
            'enum': ['Array'],
            'range': ['String', 'Number']
        });
        if ('Number' == formalizedSchema.type.name) merge(validators, {
            'range': ['String', 'Number']
        });
    }

    // If custom validator is provided allow for options.
    if (formalizedSchema.custom !== undefined) {
        merge(validators, { 'options': 'Any' });
    }

    // Copy validators to formalizedSchema - checking
    // for non-supported validators at the same time.
    for (var key in formalizedSchema) {

        var validator = validators[key];

        if (validator === undefined) throw new SchemaError(
            schema,
            'Validator \'' + key + '\' is unknown in this context.'
        );

        var test = formalizedSchema[key];

        // Test for - and transform - errors in validator.
        if (test.constructor.name === 'Array' &&
            test.length === 2 &&
            (validator === 'Any' || validator.indexOf(test[0].constructor.name) > -1) &&
            test[1].constructor.name === 'String') {

            formalizedSchema.errors = formalizedSchema.errors || {};
            formalizedSchema.errors[key] = test[1];
            formalizedSchema[key] = test = test[0];

        }

        // Ensure validator is of correct type.
        if (validator !== 'Any' && validator.indexOf(test.constructor.name) == -1) {
            throw new SchemaError(
                schema,
                'Validator \'' + key + '\' must be of type(s) ' + validators[key].join(', ') + '.'
            );
        }

    }

    // Check for supported type.
    if (typeof formalizedSchema.type !== 'undefined' && supportedTypes.indexOf(formalizedSchema.type.name) == -1) {
        throw new SchemaError(formalizedSchema, 'Cannot validate schema of type ' + formalizedSchema.type.name + '.');
    }

    // Convert custom function to array
    if (typeof formalizedSchema.custom === 'function') {
        formalizedSchema.custom = [formalizedSchema.custom];
    }

    // Throw error if required is invalid value
    if (typeof formalizedSchema.required === 'string' && formalizedSchema.required != 'implicit') {
        throw new SchemaError(
            schema,
            'Validator \'required\' must be a Boolean or String of value \'implicit\'.'
        );
    }

    // Check object unknownKeys
    if (typeof formalizedSchema.unknownKeys === 'string' &&
        ['allow', 'deny', 'remove'].indexOf(formalizedSchema.unknownKeys) == -1) {
        throw new SchemaError(
            schema,
            'Validator \'unknownKeys\' must have value \'allow\', \'deny\' or \'remove\'.'
        );
    }

    // Check string enums
    if (typeof formalizedSchema.enum !== 'undefined') {
        if (formalizedSchema.enum.length < 1) {
            throw new SchemaError(
                schema,
                'Validator \'enum\' must have at least one item.'
            );
        }
        for (var idx in formalizedSchema.enum) {
            if (typeof formalizedSchema.enum[idx] !== 'string') {
                throw new SchemaError(
                    schema,
                    'Validator \'enum\' must be an array of strings.'
                );
            }
        }
    }

    // Check autowrap.
    if (typeof formalizedSchema.autowrap === 'string' && formalizedSchema.autowrap !== 'transparent') {
        throw new SchemaError(
            schema,
            'Validator \'autowrap\' must have value `true`, `false` or `transparent`.'
        );
    }

    // Finalize objects and arrays if necessary.
    if (formalizedSchema.type) {
        if ('Object' == formalizedSchema.type.name) return formalizeObject(formalizedSchema, schema, fn, sync);
        if ('Array' == formalizedSchema.type.name) return formalizeArray(formalizedSchema, schema, fn, sync);
    }

    return finalize(formalizedSchema, schema, fn, sync);

};

exports.formalize = module.exports.formalize = formalizeAny;


<|start_filename|>packages/jollofjs/index.js<|end_filename|>
const figlet = require('figlet')
console.log(figlet.textSync('JollofJS'));

const jollof = require('jollof');


jollof.bootstrap.bootServer(function*(app) {
    jollof.log.info('Server started');
});

<|start_filename|>packages/jollof/lib/errors/auth/DuplicateUserError.js<|end_filename|>
'use strict'
module.exports= class extends Error{
	constructor(email){
		//TODO: i18n
		super("User with email "+email+" already exists")
	}
}

<|start_filename|>packages/jollof/lib/algorithms/quicksort.js<|end_filename|>
var input = [5,1,7,4,9,2,3,4,4,9,2]

function quicksort(a) {
	var stack = [[0, a.length]];
	while (1) {
		var stackLength = stack.length;
		if (! stackLength) {
			break;
		}
		var l = stack[stackLength - 1][0],
			r = stack[stackLength - 1][1];
		if (l >= r - 1) {
			stack.pop();
			continue;
		}
		var p = r - 1;
		var y = l;
		var tmp;
		for (var i = l; i < r - 1; i++)
			if (a[i] < a[p])
			{
				tmp = a[i];
				a[i] = a[y];
				a[y] = tmp;
				y++;
			}
		tmp = a[y];
		a[y] = a[r - 1];
		a[r - 1] = tmp;
		stack.pop();
		stack.push([y + 1, r]);
		stack.push([l, y]);
	}
	return a;
}


console.log(quicksort(input, 0, input.length-1))

<|start_filename|>packages/jollof/lib/commands/data.js<|end_filename|>
'use strict'
const JollofCommand = require('../util/JollofCommand');
const jollof = require('jollof');
const log = jollof.log;
const appPaths = require('../../appPaths')


class DataCommand extends JollofCommand {

	/**
	 * @description this is the description of your script
	 * @method description
	 * @return {String}
	 * @public
	 */
	* help() {
		log.info(`
		
		Contains actions for handling batch databases actions.
		Actions:
		help: Shows this message
		migrate: runs all pending data migrations.
		`)
		console.log(m)
		process.exit()
	}

	/**
	 * @description Migrate
	 * @method handle
	 * @param  {Object} options
	 * @param  {Object} flags
	 * @return {void}
	 * @public
	 */
	* migrate() {
		log.info("Running DATA migrations (order : name) ...");
		try {
			var migrations = require(appPaths.appRoot+'/migrations');
			var embassy = jollof.services.migration;
			var ignored = 0
			var ran = 0

			var nothingnew = true;
			for (var i in migrations) {
				var m = migrations[i];


				if (yield embassy.exists(m.path) ) {
					//already ran
				} else {
					//set the insertion order regardless of exclusions
					m.order = i;

					//Should we exclude this from current environment?
					var exclude = false
					if (m.excludeFrom) {
						if (Array.isArray(m.excludeFrom)) {
							if (m.excludeFrom.indexOf(jollof.config.env) > -1)
								exclude = true
						} else {
							if (m.excludeFrom === jollof.config.env)
								exclude = true
						}
					}

					//if not exluding, run this migration
					if (!exclude) {
						log.debug("Running "+m.path+"...")
						var content = require(appPaths.appRoot+'/migrations/' + m.path)
						m.reason = content.reason;
						yield content.migrate_up();
						yield embassy.create(m);
						ran++
					}
					else {
						log.trace("[Migration] " + m.path + ": ignored. Excluded from " + jollof.config.env + " environment.");
						ignored++
					}
				}


			}

			if (ran == 0)
				log.info(`No migrations ran. Database already up-to-date. Ignored ${ignored}`);
			else
				log.info(`Databases updated. Ran: ${ran} , Ignored:${ignored}`)
		} catch (e) {
			log.error(e.stack)
		}
		process.exit()
	}

}

module.exports = new DataCommand()


<|start_filename|>packages/jollof/lib/router/helper.js<|end_filename|>
/**
 * Created by iyobo on 2016-05-15.
 */


module.exports = {

	generateAPI: function (routes, version, name, constraints) {
		if (!constraints)
			constraints = {};

		routes[`get /api/${version}/${name}`] = {
			to: `api/${version}/${name}Controller list`,
			constraint: constraints.read
		}; //list all
		routes[`get /api/${version}/${name}/:id`] = {
			to: `api/${version}/${name}Controller get`,
			constraint: constraints.read
		}; //get one
		routes[`post /api/${version}/${name}`] = {
			to: `api/${version}/${name}Controller post`,
			constraint: constraints.create
		}; // create
		routes[`patch /api/${version}/${name}/:id`] = {
			to: `api/${version}/${name}Controller patch`,
			constraint: constraints.update
		}; // update
		routes[`delete /api/${version}/${name}/:id`] = {
			to: `api/${version}/${name}Controller delete`,
			constraint: constraints.delete
		}; //delete or disable

	},

	generateAdminAPI: function (routes) {

		routes[`get /api/admin/:modelName`] = {
			to: `adminController list`,
			constraint: 'adminSecurity loggedIn'
		}; //list all
		routes[`get /api/admin/:modelName/:id`] = {
			to: `adminController get`,
			constraint: 'adminSecurity loggedIn'
		}; //get one
		routes[`post /api/admin/:modelName`] = {
			to: `adminController post`,
			constraint: 'adminSecurity loggedIn'
		}; // create
		routes[`patch /api/admin/:modelName/:id`] = {
			to: `adminController patch`,
			constraint: 'adminSecurity loggedIn'
		}; // update
		routes[`delete /api/admin/:modelName/:id`] = {
			to: `adminController delete`,
			constraint: 'adminSecurity loggedIn'
		}; //delete or disable

	},

	generateGenericAPI: function (routes) {

		routes[`get /api/generic/:modelName`] = {
			to: `api/generic/genericController list`,
			constraint: 'adminSecurity loggedIn'
		}; //list all
		routes[`get /api/generic/:modelName/:id`] = {
			to: `api/generic/genericController get`,
			constraint: 'adminSecurity loggedIn'
		}; //get one
		routes[`post /api/generic/:modelName`] = {
			to: `api/generic/genericController post`,
			constraint: 'adminSecurity loggedIn'
		}; // create
		routes[`patch /api/generic/:modelName/:id`] = {
			to: `api/generic/genericController patch`,
			constraint: 'adminSecurity loggedIn'
		}; // update
		routes[`delete /api/generic/:modelName/:id`] = {
			to: `api/generic/genericController delete`,
			constraint: 'adminSecurity loggedIn'
		}; //delete or disable

	},

}

<|start_filename|>packages/jollof/webpack.config.js<|end_filename|>
// var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
var webpack = require('webpack');
 var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
var ExtractTextPlugin = require("extract-text-webpack-plugin")
const _ = require('lodash');

let isProd = process.env.NODE_ENV === 'production';
_.each(process.argv, (arg) => {
	if (arg === '--prod') {
		console.log('Setting to Production Mode');
		isProd = true;
	}
})

const plugins = [];

//uglify in prod
if (isProd) {
	plugins.push(new webpack.optimize.UglifyJsPlugin());
}
plugins.push(new CommonsChunkPlugin("commons.chunk.js"));

plugins.push(function() {
    this.plugin('watch-run', function(watching, callback) {
        console.log('>>' + new Date());
        callback();
    })
});


module.exports = {
	entry: {
		//Old react app using admin-on-rest.
		//admin: "./lib/admin/oldRarClient/adminApp.jsx",

		//Brand new Admin app. Custom-built from start to finish with the beautiful Vue.js
		//admin: "./lib/admin/vueClient/adminApp.js",

		/**
		 * But you know what? Better start getting very comfortable with react because
		 * of react-native and future employability reasons.
		 */
		admin: "./lib/admin/rClient/adminApp.jsx"
	},
    //context: path.join(__dirname, 'lib/admin'),
    output: {
		path: "./jollofstatic/bundles/",
		filename: "[name].bundle.js"
	},
	devtool: "source-map",
    plugins,
	module: {
		loaders: [
			{ test: /\.html/, loader: "html-loader" },
			//{ test: /\.vue/, loader: "html-loader" },

			{ test: /\.css$/, loader: "style-loader!css-loader" },
			{
				test: /\.js|jsx?$/,
				loader: 'babel',
				exclude: /node_modules/,
				query: {
					presets: [['es2015'], ['stage-2'], ['react']],
					plugins: ['transform-decorators-legacy', 'react-html-attrs', 'transform-class-properties']
				}
			},
			//{
			//	test: /\.vue$/,
			//	loader: 'vue-loader',
			//	options: {
			//		loaders: {
			//			// extract all <docs> content as raw text
			//			'docs': ExtractTextPlugin.extract('raw-loader')
			//		}
			//	}
			//},
		]
	},
	resolve: {
		extensions: ['', '.js', '.jsx', '.vue'],
		alias: {
			vue: 'vue/dist/vue.js',
            react: path.join(__dirname, 'node_modules', 'react')
		}
	}
};

<|start_filename|>packages/jollof/lib/data/models/JollofMigration.js<|end_filename|>
/**
 * Created by iyobo on 2016-09-18.
 */
const data = require('../../../index').data;


//Models use names
const schema = {
	name: 'JollofMigration',
	structure: {
		order: { type: Number, required: true },
		path: { type: String, required: true },
		reason: String,
	}
}

module.exports = data.registerModel(schema);

<|start_filename|>packages/jollof/lib/algorithms/finddupes.js<|end_filename|>
'use strict'
var list = [1, 0, 99, 9, 2, 1];

function swap(arr, x, y) {
	var buf = arr[x];
	arr[x] = arr[y];
	arr[y] = buf;

}

/**
 * Sorts
 * - Bubble
 * - Insertion
 * - Quick
 */

function insertionsort(arr){


}
var cmp = (n1, n2) => n1 - n2;

function bubbleSort(arr){
	var len = arr.length;
	for (var i = len-1; i>=0; i--){
		for(var j = 1; j<=i; j++){
			if(arr[j-1]>arr[j]){
				var temp = arr[j-1];
				arr[j-1] = arr[j];
				arr[j] = temp;
			}
		}
	}
	return arr;
}

function selectionSort(arr){
	var minIdx, temp,
		len = arr.length;
	for(var i = 0; i < len; i++){
		minIdx = i;
		for(var  j = i+1; j<len; j++){
			if(arr[j]<arr[minIdx]){
				minIdx = j;
			}
		}
		temp = arr[i];
		arr[i] = arr[minIdx];
		arr[minIdx] = temp;
	}
	return arr;
}

function insertionSort(arr){
	var i, len = arr.length, el, j;

	for(i = 1; i<len; i++){
		el = arr[i];
		j = i;

		while(j>0 && arr[j-1]>toInsert){
			arr[j] = arr[j-1];
			j--;
		}

		arr[j] = el;
	}

	return arr;
}

function merge(left, right){
	var result = [],
		lLen = left.length,
		rLen = right.length,
		l = 0,
		r = 0;
	while(l < lLen && r < rLen){
		if(left[l] < right[r]){
			result.push(left[l++]);
		}
		else{
			result.push(right[r++]);
		}
	}
	//remaining part needs to be addred to the result
	return result.concat(left.slice(l)).concat(right.slice(r));
}
function mergeSort(arr){
	var len = arr.length;
	if(len <2)
		return arr;
	var mid = Math.floor(len/2),
		left = arr.slice(0,mid),
		right =arr.slice(mid);
	//send left and right to the mergeSort to broke it down into pieces
	//then merge those
	return merge(mergeSort(left),mergeSort(right));
}


console.log(bubbleSort(list))

<|start_filename|>packages/jollof-data-arangodb/util/conversionUtil.js<|end_filename|>
/**
 * Created by iyobo on 2017-05-02.
 */
const idField = '_key';
const assert = require('assert');
const aql = require('arangojs').aql;
const util = require('util')
const moment = require('moment')

function convertComp(comp) {

    let symbol = '';

    switch (comp) {

        case '=':
            symbol = '==';
            break;
        case '!=':
            symbol = '!=';
            break;
        case '>':
            symbol = '>';
            break;
        case '>=':
            symbol = '>=';
            break;
        case '<':
            symbol = '<';
            break;
        case '<=':
            symbol = '<=';
            break;
        case 'in':
            symbol = 'IN';
            break;
        case 'nin':
            symbol = 'NOT IN';
            break;
        case 'like':
            throw new Error('Cannot handle "like" operator here. Do so in native functions')
            break;
        case 'nlike':
            throw new Error('Cannot handle "nlike" operator here. Do so in native functions')
            break;

    }

    return symbol;
}

/**
 * Translates a single jollof condition
 * @param {array | object} cond
 * @param queryObj
 * @returns {*}
 */
function translate(cond, queryObj) {

    let result = '1 == 1';

    // If condition is not an array...
    if (!Array.isArray(cond)) {
        //...then it is a condition cluster
        if (cond.and) {
            result = translateAndList(cond.and, queryObj)
        }
        else if (cond.or) {
            result = translateOrList(cond.or, queryObj)
        }
        else {
            //This is an illegal subject.
            throw new Error('Invalid query Object keys. Objects must be either "and" or "or"');
        }
    }
    else {
        //...then it's a singular condition
        assert(cond.length === 3, 'Invalid condition item. Condition items must be an array of 3 items representing field, comparator, and value')

        //field
        let fieldName = cond[0];
        if (fieldName === 'id') {
            fieldName = '_key';
        }

        let isNestedQuery = false;
        if (fieldName.indexOf('*') > -1) {
            fieldName = fieldName.replace('.*', '[*]');
            isNestedQuery = true;
        }

        //comp/value
        let comp = cond[1];
        let value = cond[2];

        if (value) {

            // construct mini block
            if (comp === 'like' || comp === 'nlike') {

                value = '%' + value + '%';

            }
        }

        const paramIndex = Math.floor(Object.keys(queryObj.bindVars).length / 2);

        const paramName = 'p' + paramIndex;
        const valueName = 'v' + paramIndex;

        const aqlComp = convertComp(comp);

        //appendants

        //NOTE: unfortunatlely, dynamic queries cannot handle nested fields as at Arango 3.1
        if (isNestedQuery) {
            // FIXME: secure the fieldName variable before using point blank!!! VERY prone to AQL/SQL injection right now.
            const safeFieldName = fieldName
            result = ` c.${safeFieldName} ANY ${aqlComp} @${valueName} `;
        } else {
            result = ` c.@${paramName} ${aqlComp} @${valueName} `;
            queryObj.bindVars[paramName] = fieldName;
        }

        queryObj.bindVars[valueName] = value;

    }

    return result;

}

/**
 *
 * @param conds
 * @param queryObj
 * @returns {string}
 */
function translateAndList(conds, queryObj, op) {
    assert(Array.isArray(conds), 'Expected an array to AND. Instead got ' + typeof conds);

    let ff = ' ( '
    const andList = [];
    conds.forEach((cond) => {
        andList.push(translate(cond, queryObj))
    });
    ff += andList.join(op || ' AND ') + ' ) '
    //console.log('and', ff)
    return ff;
}

/**
 *
 * @param conds
 * @param queryObj
 * @returns {string}
 */
function translateOrList(conds, queryObj, op) {
    assert(Array.isArray(conds), 'Expected an array to OR. Instead got ' + typeof conds);

    let ff = ' ( '
    const orList = [];
    conds.forEach((cond) => {
        orList.push(translate(cond, queryObj))
    });
    ff += orList.join(op || ' OR ') + ' ) '
    //console.log('or', ff)
    return ff;
}


/**
 * Converts an array of jollof conditions to native query language
 * @param {Array} jollofArray
 * @param queryObj
 * @returns ArangoDB string query
 */
exports.convertConditionsFromJollof = (jollofArray, queryObj) => {
    try {
        const q = translateAndList(jollofArray, queryObj, ' && ');

        queryObj.query += q;
        //console.log({ q })
        //console.log('The query:', util.inspect(resp))

    } catch (e) {
        console.error('jollof-data-arangodb: Trouble processing ', jollofArray);
        throw e;
    }

}


/**
 * Prepare data for delivery back to JollofJS.
 * For one, jollof respects data.id as the universal id key, so change the datasource's id key to that.
 * @param res
 * @returns {*}
 */
exports.convertToJollof = (res) => {
    if (Array.isArray(res)) {
        res = res.map((row) => {
            row.id = row[idField];
            delete row[idField];

            return row;
        });
    } else if (res) {
        if (res[idField]) {
            res.id = res[idField];
            delete res[idField];
        }
    }

    return res;
}

<|start_filename|>packages/jollof/lib/rendering/general.js<|end_filename|>
/**
 * Created by iyobo on 2016-04-24.
 *
 * For Common Nunjuck filters
 * @param nunjucksEnv
 */
module.exports = function(nunjucksEnv){
	
	nunjucksEnv.addFilter('shorten', function(str, count) {
		return str.slice(0, count || 5);
	});

	nunjucksEnv.addFilter('showError', function(str) {
		if(str){
			return '<span class="error">'+str+'</span>'
		}
		else
			return ""
	});

	nunjucksEnv.addFilter('stringify', function(obj) {
		return JSON.stringify(obj);
	});
	
}

<|start_filename|>packages/jollof/appPaths.js<|end_filename|>
/**
 * Created by iyobo on 2016-10-17.
 */
const path = require('path');
const root = process.cwd();

module.exports={
	appRoot: root,
	models: path.join(root,'app','models'),
	controllers: path.join(root,'app','controllers'),
	views: path.join(root,'app','views'),
	schemaTypes: path.join(root,'app','data','types'),
	services: path.join(root,'app','services'),
	viewFilters: path.join(root,'app','views','filters'),
	config: path.join(root,'config'),
	baseConfig: path.join(root,'config','base'),
	commands: path.join(root,'app','commands'),


}

<|start_filename|>packages/jollof/lib/admin/rClient/util/convertToFormData.js<|end_filename|>
/**
 * Created by iyobo on 2017-04-25.
 */
function appendRecursively(formData, collection, parentkey, parentIsArray) {
    //console.log(...arguments)

    for (let k in collection) {
        const val = collection[k];

        if (val === undefined) continue;

        if (val instanceof File) {
            let mkey = (parentkey ? parentkey + '.' : '') + k;

            // if(parentIsArray)
            // 	mkey = parentkey
            // else
            // 	mkey = k


            val.foo = 'bar'

            formData.append(mkey, val)
        }
        else if (Array.isArray(val)) {

            let mkey = '';
            if (parentIsArray) {
                mkey = parentkey; //parentKey can/should never be empty if parentISarray
            } else {
                mkey = (parentkey ? parentkey + '.' + k : k);
            }

            appendRecursively(formData, val, mkey, true);
        }
        else if (typeof val === 'object') {
            let mkey = (parentkey ? parentkey + '.' : '') + k;
            appendRecursively(formData, val, mkey, false);
        }
        else {
            let mkey = (parentkey ? parentkey + '.' : '') + k;
            formData.append(mkey, val)
        }

    }
}




export default function convertToFormData (data) {
    var formData = new FormData();
    appendRecursively(formData, data);
    return formData;
}

<|start_filename|>packages/jollof/lib/algorithms/test.js<|end_filename|>
'use strict'

function Car(){

	this.honk = function(){
		console.log("Pam Pam")
	};

	this._ignite = function(){
		console.log("We on fire!")
	}

	this.fuel = function(){
		console.log("Give me fuel")
	}
}

function Tesla(){
	Car.call(this)

	this.fuel = function(){
		console.log("Give me Electricity")
	}
}

var Subaru = new Car.prototype

new Tesla()._ignite()
new Subaru.fuel();

<|start_filename|>packages/jollof/lib/rendering/index.js<|end_filename|>
/**
 * Created by iyobo on 2016-04-24.
 *
 * For commonly accessed Nunjucks extensions
 * @param nunjucksEnv
 */
module.exports = function(nunjucksEnv){

	//General helpers
	require('./general')(nunjucksEnv)

	//Form Fields
	require('./fields')(nunjucksEnv)

}

<|start_filename|>packages/jollof/lib/data/types/ref.js<|end_filename|>
const registry = require('../registry');
const config = require('../../configurator');
const dataSourceSettings = config.settings.data.dataSources;
/**
 * This is an in-built Schema Type.
 * Schema Types are schemas themselves, and can be used inside other schemas.
 * Just be careful not to create an infinite loop by using a custom Schema Type in itself or child.
 *
 *
 * This type is used to store relationships to other entities/models by id.
 * Before storage, the type of the id will be cast to the adapter.IdType of dataSourceName ('default' by default)
 */
const refSchema = {
    name: 'Ref',
    structure: {
        meta: {
            widget: 'ref',
            ref: '',
            dataSourceName: 'default'
        },
        custom: function (data, schema) {
            let validatedData = data;
            const IdType = dataSourceSettings[schema.meta.dataSourceName].adapter.IdType;

            if (validatedData && IdType && typeof validatedData !== IdType.name) {
                validatedData = new IdType(validatedData);
            }

            return validatedData;
        }
    }

};

module.exports = registry.registerType(refSchema);




<|start_filename|>packages/jollof/lib/configurator/base.js<|end_filename|>
/**
 * Created by iyobo on 2016-11-07.
 */
const os = require('os');
const path = require('path');
const appPaths = require('../../appPaths');
var firstExistingPath = require('../util/fileUtil.js').firstExistingPath;
/**
 * These are your base app configs.
 * Every one of your environment config files inherit the values here.
 * Created by iyobo on 2016-03-22.
 */
module.exports = {
    appName: "Jollof App",

    server: {
        port: 3000, //Port main app runs on
        host: '0.0.0.0',
        hostname: 'localhost'
    },

    admin: {
        enabled: true,
        routePrefix: '/admin',
        auth: async (ctx, next) => {
            //No auth by default
            await next()
        }

    },

    domains: {
        default: 'http://localhost:3000',
    },

    env: 'development',

    data: {
        dataSources: {
            default: {
                adapter: {},//require('jollof-data-memory'),
                nativeType: 'memory',
                options: {
                    filename: '/tmp/jollofdb',
                    autoload: true
                }
            }
        }
    },

    sessions: {
        key: 'jollof:sess', /** (string) cookie key (default is koa:sess) */
        maxAge: 86400000, /** (number) maxAge in ms (default is 1 days) */
        overwrite: true, /** (boolean) can overwrite or not (default true) */
        httpOnly: true, /** (boolean) httpOnly or not (default true) */
        signed: true, /** (boolean) signed or not (default true) */

        /**
         * see https://www.npmjs.com/package/redis#options-is-an-object-with-the-following-possible-properties
         * Also, only using redis directly for sessions, not as a model engine, so no model adapter needed.
         */
        redis: {
            host: "127.0.0.1",
            port: 6379,
            db: 0
        },
    },

    /**
     * Be sure to generate a fresh secret for each app.
     * You can have multiple rotating keys
     */
    crypto: {
        secrets: ['jollofIsSweet'],
        saltrounds: 8,
    },

    cookies: {
        expiry: 120,
        names: {
            app: 'APP',
        },
    },

    nunjucks: {
        ext: 'nunj',
        path: ['app/views', 'node_modules/jollof/lib/admin/views'], //from project root
        nunjucksConfig: {
            //see https://mozilla.github.io/nunjucks/api.html#configure
            noCache: true,
            autoescape: false
        },
    },

    fileStorage: {
        defaultEngine: 'local',
        engines: {
            local: {
                privateRoot: path.join(appPaths.appRoot, 'uploads', 'private'), //Where to store private files
                publicRoot: path.join(appPaths.appRoot, 'uploads', 'public'), //Where to store public files that can be accessed by the internet (e.g. like in a CMS)
                basePublicUrl: '/jollofuploads'
            },
            s3: {}
        }
    },

    thirdParty: {
        google: {
            maps: {
                apiKey: ''
            }
        }
    },

    spices:{

    }

}



<|start_filename|>packages/koa-jollof-router/test/routeTest.js<|end_filename|>
/**
 * Created by iyobo on 2017-05-13.
 */
module.exports = {
    '/': {},
    'get /profile': {},
}

<|start_filename|>packages/jollof/lib/errors/auth/InvalidCredentialsError.js<|end_filename|>
'use strict'
module.exports= class extends Error{
	constructor(){
		this.message = "Invalid Credentials"
	}
}

<|start_filename|>packages/jollof-data-memory/util/conversionUtil.js<|end_filename|>
/**
 * Created by iyobo on 2017-05-02.
 */
const idField = '_id';
const Boom = require('boom');

function convertComp(comp) {

    let symbol = '';

    switch (comp) {
        case '>':
            symbol = '$gt';
            break;
        case '!=':
            symbol = '$ne';
            break;
        case '>=':
            symbol = '$gte';
            break;
        case '<':
            symbol = '$lt';
            break;
        case '<=':
            symbol = '$lte';
            break;
        case 'in':
            symbol = '$in';
            break;
        case 'not in':
            symbol = '$nin';
            break;

    }

    return symbol;
}


function translate(cond, query, parentConnector) {

    let fieldName = cond.field === 'id' ? '_id' : cond.field;

    //We don't support elemMatch fields
    if (fieldName.indexOf('*') > -1) {
        throw new Boom.methodNotAllowed('Sub-array matches not supported in jollof-data-memory adapter');
    }


    if (!cond.items) { // if not a nest starter

        const condBlock = {};

        // construct mini block
        if (cond.comp === '=') {
            let value = cond.value;

            //NEDB is too dumb to deal with foo:null
            if (cond.value === null || cond.value === undefined) {
                value = {};
                value['$exists'] = false;
            }

            condBlock[fieldName] = value;
        } else if (cond.comp === '!=' && (cond.value === null || cond.value === undefined)) {
            let value = {};
            value['$exists'] = true;

            condBlock[fieldName] = value;
        }
        else {
            const subCondBlock = {};
            subCondBlock[convertComp(cond.comp)] = cond.value;

            //construct block
            condBlock[fieldName] = subCondBlock;
        }


        //how is this block connected to the previous?
        const logical = '$' + (cond.connector || parentConnector || 'and');
        query[logical] = query[logical] || [];
        query[logical].push(condBlock);

    }
    else if (cond.items) {
        throw new Boom.methodNotAllowed('Nested conditions currently unsupported in Jollof Memory Adapter')
    }

    return cond.connector;

}

function translateList(conds, map) {
    let activeConnector;
    conds.reverse().forEach((cond, index) => {
        activeConnector = translate(cond, map, activeConnector);
    });
}

exports.convertConditionsFromJollof = (conditions) => {
    const query = {};

    translateList(conditions, query);

    //console.log('conditions:', conditions, 'query:', query);

    return query;
}


exports.convertOptionsFromJollof = (opts) => {

    if (opts.sort && opts.sort.id) {
        opts.sort._id = opts.sort.id;
        delete opts.sort.id;
    }

    return opts;
}

exports.convertToJollof = (res) => {
    if (Array.isArray(res)) {
        res = res.map((row) => {
            row.id = row[idField];
            delete row[idField];

            return row;
        });
    } else {
        if (res[idField]) {
            res.id = res[idField];
            delete res[idField];
        }
    }

    return res;
}

<|start_filename|>packages/jollof/test/setup.js<|end_filename|>
/**
 * Created by iyobo on 2017-01-11.
 */
const co = require('co');
const jollof = require('../');
const del = require('del')



before(function(){
	if(process.env.NODE_ENV !== 'test') {
		throw new Error('NODE_ENV should be "test". It is "'+process.env.NODE_ENV+'". Aborting!')
	}
});

before('bootstrapping Jollof for testing', function () {

	//Before each test, wipe
	return co(function*() {

		//Start Jollof
		yield jollof.bootstrap.bootStandAloneServer();

	});

});

<|start_filename|>packages/jollof/lib/filestorage/index.js<|end_filename|>
/**
 * Created by iyobo on 2016-08-24.
 */
const config = require('../configurator').settings;
const log = require('../log')
const path = require('path');
/**
 * Handles uploaded file storage and persistence within eluvianJS
 */
class FileStorage {

	* store( file, opts = {} ) {
		try {
			//Only process file upload if it has no 'key' field i.e. File hasn't been uploaded before
			if(file['key'])
				return file;

			let engineName = opts.engineOverride || config.fileStorage.defaultEngine;

			let engine = require(path.join(__dirname, 'engines', engineName));
			if (!engine) {
				log.warn(`No such engine name: ${engineName}. Defaulting to local`)
				engine = require('./engines/local')
			}

			const res= yield engine.store(file, opts);

			return res;
		} catch (err) {
			log.error(`There was an error storing the file`, err.stack);
			throw err;
		}
	}

	* retrieve( path, opts = {} ) {

	}

	* delete( path, opts = {} ) {

	}

}

module.exports = new FileStorage();

<|start_filename|>packages/jollofjs/bin/jollof-cli.js<|end_filename|>
#!/usr/bin/env node
var chalk = require('chalk');
var figlet = require('figlet');

var actions = require('../lib/actions.js');

var action = process.argv[2];
var argv = require('minimist')(process.argv.slice(3));


(async () => {
    console.log(figlet.textSync('JollofJS'));

    if (!actions[action]) {
        action = 'help';
    }
    console.log(chalk.blue.bold(action.toUpperCase()),'\n');

    //Run action from a map of available actions
    await actions[action](argv);

    console.log('')
})().catch(err => {
    console.log(chalk.red(err.message),'\n');
    console.error(err.stack);
});


<|start_filename|>packages/jollof/standalone-server.js<|end_filename|>
/**
 * Created by iyobo on 2016-12-27.
 */
const jollof = require('./index');

jollof.bootstrap.bootStandAloneServer(function(){
	jollof.log.info('Jollof started in standalone-mode')
});


<|start_filename|>packages/jollof/lib/algorithms/parser.js<|end_filename|>
/**
 * Created by iyobo on 2016-05-12.
 */

//Spreading it out to ease human-readability of input
var input = `
{
   "foo1": "bar1",
   "foo2": "bar2",
   "foo3": {
      "foo31": "bar31",
      "foo32": "bar32",
      "foo33": {
         "foo331": "bar331"
      }
   },
   "foo4":"bar4"
}
`

//minifying input
input = input.replace(/ |\n/g,'')
console.log("Input:", input)

function parseMyJson(input){
	var neststack=[]
	var output={}
	var activekey=""
	var activevalue=""

	//States...because state machines are dirty and smelly without them
	var states = new function(){
		this.IDLE=0
		this.OBJECT_BEGIN=1 //{
		this.OBJECT_END=2 //}
		this.COLLECTING_KEY=3 //" + !assigning
		this.COLLECTING_VALUE=4  //" + assigning
		this.ASSIGNING=6 // :
		this.NEXT_KVP=7 // ,
		this.NEST_NEW_OBJECT=8
	}()

	var recursiveParse =function (str,index,state,out){
		switch(state){
			case states.IDLE:
				if(str[index]==='{'){
					recursiveParse(str, index+1, states.OBJECT_BEGIN,out)
				}
				break;
			case states.OBJECT_BEGIN:
				if(str[index]==='"'){
					recursiveParse(str, index+1, states.COLLECTING_KEY,out)
				}
				break;
			case states.OBJECT_END:
				out = output
				neststack.pop()

				for(nest in neststack){
					out= output[neststack[nest]]
				}

				recursiveParse(str, index+1, states.NEXT_KVP,out)
				break;
			case states.COLLECTING_KEY:
				activekey=""
				while(str[index]!='"'){
					activekey += str[index]
					index++
				}
				out[activekey]={}
				recursiveParse(str, index+1, states.ASSIGNING,out)

				break;
			case states.ASSIGNING:
				if(str[index]==':'){
					index++

					if(str[index]==='{'){
						//new nested object
						recursiveParse(str,index, states.NEST_NEW_OBJECT,out)
					}
					else if(str[index]=='"'){
						recursiveParse(str,index+1, states.COLLECTING_VALUE,out)
					}
				}

				break;
			case states.NEST_NEW_OBJECT:
				neststack.push(activekey)
				recursiveParse(str,index+1, states.OBJECT_BEGIN,out[activekey])

				break;
			case states.COLLECTING_VALUE:
				activevalue=""

				while(str[index]!='"'){
					activevalue += str[index]
					index++
				}
				out[activekey]=activevalue

				recursiveParse(str,index+1, states.NEXT_KVP,out)

				break;
			case states.NEXT_KVP:
				if(str[index]==','){
					recursiveParse(str,index+2, states.COLLECTING_KEY,out)
				}
				else if (str[index]=='}'){
					recursiveParse(str,index, states.OBJECT_END,out)
				}

				break;
		}
	}
	recursiveParse(input,0,states.IDLE,output)

	return output;
}

console.log("\nOutput:\n",parseMyJson(input))


<|start_filename|>packages/jollof/lib/util/adminUtil.js<|end_filename|>
module.exports = {

	labelize: function (str) {
		return str.replace
		(/(^[a-z]+)|[0-9]+|[A-Z][a-z]+|[A-Z]+(?=[A-Z][a-z]|[0-9])/g
			, function (match, first) {
				if (first) match = match[0].toUpperCase() + match.substr(1);
				return match + ' ';
			}
		)
	}
}

<|start_filename|>packages/jollof-data-pg/util/conversionUtil.js<|end_filename|>
/**
 * Created by iyobo on 2017-05-02.
 */
const idField = '_key';
const assert = require('assert');
const aql = require('arangojs').aql;
const util = require('util')

function convertComp(comp) {

    let symbol = '';

    switch (comp) {

        case '=':
            symbol = '==';
            break;
        case '!=':
            symbol = '!=';
            break;
        case '>':
            symbol = '>';
            break;
        case '>=':
            symbol = '>=';
            break;
        case '<':
            symbol = '<';
            break;
        case '<=':
            symbol = '<=';
            break;
        case 'in':
            symbol = 'IN';
            break;
        case 'nin':
            symbol = 'NOT IN';
            break;
        case 'like':
            throw new Error('Cannot handle "like" operator here. Do so in translate function')
            break;
        case 'nlike':
            throw new Error('Cannot handle "nlike" operator here. Do so in translate function')
            break;

    }

    return symbol;
}

/**
 * Translates a single jollof condition
 * @param {array | object} cond
 * @param queryObj
 * @returns {*}
 */
function translate(cond, queryObj) {

    let result = {};

    // If condition is not an array...
    if (!Array.isArray(cond)) {
        //...then it is a condition cluster
        if (cond.and) {
            result = translateAndList(cond.and, queryObj)
        }
        else if (cond.or) {
            result = translateOrList(cond.or, queryObj)
        }
        else {
            //This is an illegal subject.
            throw new Error('Invalid query Object keys. Objects must be either "and" or "or"');
        }
    }
    else {
        //...then it's a singular condition
        assert(cond.length === 3, 'Invalid condition item. Condition items must be an array of 3 items representing field, comparator, and value')

        let fieldName = cond[0];
        if (fieldName === 'id') {
            fieldName = '_key';
        }
        fieldName = fieldName.replace('.*', '[*]');

        let comp = cond[1];
        let value = cond[2];

        // construct mini block
        if (comp === 'like' || comp === 'nlike') {

            value = '%' + value + '%';

        }

        const paramName = 'p' + Object.keys(queryObj.bindVars).length;
        const valueName = 'v' + Object.keys(queryObj.bindVars).length;

        const aqlComp = convertComp(comp);

        result = `c.@${paramName} ${aqlComp} @${valueName}`;

        queryObj.bindVars[paramName] = fieldName;
        queryObj.bindVars[valueName] = value;

    }

    return result;

}

/**
 *
 * @param conds
 * @param queryObj
 * @returns {string}
 */
function translateAndList(conds, queryObj) {
    assert(Array.isArray(conds), 'Expected an array to AND. Instead got ' + typeof conds);

    queryObj.query += ' ( '
    const andList = [];
    conds.forEach((cond) => {
        andList.push(translate(cond, queryObj))
    });
    queryObj.query += andList.join(' AND ')
    queryObj.query += ' ) '
}

/**
 *
 * @param conds
 * @param queryObj
 * @returns {string}
 */
function translateOrList(conds, queryObj) {
    assert(Array.isArray(conds), 'Expected an array to OR. Instead got ' + typeof conds);

    queryObj.query += ' ( '
    const orList = [];
    conds.forEach((cond) => {
        orList.push(translate(cond, queryObj))
    });
    queryObj.query += orList.join(' OR ')
    queryObj.query += ' ) '

}


/**
 * Converts an array of jollof conditions to native query language
 * @param {Array} jollofArray
 * @param queryObj
 * @returns ArangoDB string query
 */
exports.convertConditionsFromJollof = (jollofArray, queryObj) => {
    try {
        translateAndList(jollofArray, queryObj);

        //console.log('The query:', util.inspect(resp))

    } catch (e) {
        console.error('jollof-data-arangodb: Trouble processing ', jollofArray);
        throw e;
    }

}


/**
 * Prepare data for delivery back to JollofJS.
 * For one, jollof respects data.id as the universal id key, so change the datasource's id key to that.
 * @param res
 * @returns {*}
 */
exports.convertToJollof = (res) => {
    if (Array.isArray(res)) {
        res = res.map((row) => {
            row.id = row[idField];
            delete row[idField];

            return row;
        });
    } else if (res) {
        if (res[idField]) {
            res.id = res[idField];
            delete res[idField];
        }
    }

    return res;
}

<|start_filename|>packages/jollof-data-mongodb/util/conversionUtil.js<|end_filename|>
/**
 * Created by iyobo on 2017-05-02.
 */
const idField = '_id';
const assert = require('assert');
const ObjectID = require('mongodb').ObjectID;
const util = require('util')

function convertComp(comp) {

    let symbol = '';

    switch (comp) {
        case '>':
            symbol = '$gt';
            break;
        case '!=':
            symbol = '$ne';
            break;
        case '>=':
            symbol = '$gte';
            break;
        case '<':
            symbol = '$lt';
            break;
        case '<=':
            symbol = '$lte';
            break;
        case 'in':
            symbol = '$in';
            break;
        case 'nin':
            symbol = '$nin';
            break;
        case 'like':
            throw new Error('Cannot handle "like" operator here. Do so in translate function')
            break;
        case 'nlike':
            throw new Error('Cannot handle "nlike" operator here. Do so in translate function')
            break;
    }

    return symbol;
}

/**
 * Translates a single jollof condition
 * @param {array | object} cond
 * @returns {*}
 */
function translate(cond) {

    let result = {};

    // If condition is not an array...
    if (!Array.isArray(cond)) {
        //...then it is a condition cluster
        if (cond.and) {
            result = translateAndList(cond.and)
        }
        else if (cond.or) {
            result = translateOrList(cond.or)
        }
        else {
            //This is an illegal subject.
            throw new Error('Invalid query Object keys. Objects must be either "and" or "or"');
        }
    }
    else {
        //...then it's a singular condition
        assert(cond.length === 3, 'Invalid condition item. Condition items must be an array of 3 items representing field, comparator, and value')

        let fieldName = cond[0];
        fieldName = fieldName.replace('.*', '');
        let comp = cond[1];
        let value = cond[2];

        if (fieldName === 'id') {
            fieldName = '_id';
            value = Array.isArray(value) ? value : new ObjectID(value)
        }

        // construct mini block
        if (comp === '=') {

            if (value === null || value === undefined) {
                value = {};
                value['$exists'] = false;
            }

            result[fieldName] = value;
        } else if (comp === '!=' && (value === null || value === undefined)) {
            let value = {};
            value['$exists'] = true;

            result[fieldName] = value;
        } else if (comp === 'like') {

            result[fieldName] = new RegExp(value);

        } else if (comp === 'nlike') {

            result[fieldName] = {
                $not: new RegExp(value)
            };
        }
        else {
            const subCondBlock = {};

            subCondBlock[convertComp(comp)] = value;

            result[fieldName] = subCondBlock;
        }
    }

    return result;

}

function translateAndList(conds) {
    assert(Array.isArray(conds), 'Expected an array to AND. Instead got ' + typeof conds);

    const andList = [];
    conds.forEach((cond) => {
        andList.push(translate(cond));
    })
    return andList.length > 0 ? { $and: andList } : {};
}

function translateOrList(conds) {
    assert(Array.isArray(conds), 'Expected an array to OR. Instead got ' + typeof conds);

    const orList = [];
    conds.forEach((cond) => {
        orList.push(translate(cond));
    })

    return orList.length > 0 ? { $or: orList } : {};
}


/**
 * Converts an array of jollof conditions to MongoDB
 * @param {Array} jollofArray
 * @returns MongoDB query
 */
exports.convertConditionsFromJollof = (jollofArray) => {
    try {
        const resp = translateAndList(jollofArray);

        //console.log('The query:', util.inspect(resp))
        return resp;
    } catch (e) {
        console.error('jollof-data-mongodb: Trouble processing ', jollofArray);
        throw e;
    }

}

/**
 * Converts paging and sorting options
 * @param opts
 * @returns {*}
 */
exports.convertOptionsFromJollof = (opts) => {

    if (opts.sort && opts.sort.id) {
        opts.sort._id = opts.sort.id;
        delete opts.sort.id;
    }

    return opts;
}

exports.convertToJollof = (res) => {
    if (Array.isArray(res)) {
        res = res.map((row) => {
            row.id = row[idField];
            delete row[idField];

            return row;
        });
    } else {
        if (res[idField]) {
            res.id = res[idField];
            delete res[idField];
        }
    }

    return res;
}

<|start_filename|>packages/jollof/lib/admin/controllers/adminController.js<|end_filename|>
'use strict'

const data = require('../../data');
const models = data.models;
const log = require('../../log');
const Boom = require('boom');
const _ = require('lodash');
const httpUtil = require('../../util/httpUtil');
var jql = require('../../data/index.js').jql;
const { VM } = require('vm2');

module.exports = {
    index: async function (ctx) {
        // try {
        await ctx.render('jollofadmin/admin', { models: data.models });

        // } catch (err){
        // 	log.error(err);
        // }
    },

    models: async function (ctx) {
        //generate a list of model schemas and send it down
        //try {
        let schemas = [];
        _.each(models, (model) => {
            schemas.push(model.schema);
        });

        ctx.body = schemas;
        //} catch (err) {
        //    httpUtil.handleError(ctx, err);
        //}
    },

    //List all
    /**
     *
     * @param modelName
     *
     * HTTP Params
     * query.page - default 1
     * query.limit
     * query.sort e.g -id, id, -age
     * query.sort e.g -id, id, -age
     */
    list: async function (ctx) {
        //try {

        const modelName = ctx.params.modelName;

        if (!modelName || !models[modelName]) {
            throw new Boom.badRequest('Model: ' + modelName + ' does not exist');
            //return [];
        }

        const opts = {};

        if (ctx.query.sort) {
            opts.sort = JSON.parse(ctx.query.sort);
        }
        if (ctx.query.paging) {
            opts.paging = JSON.parse(ctx.query.paging);
        }

        //turn conditions to JQA, in separate context, with a 500ms timeout
        let vmctx = Object.create(null); //create a context for the vm that does NOT have a prototype to prevent vuln.
        vmctx.a = 1;
        const jqa = new VM({
            timeout: 5
        }).run(ctx.query['conditions']);

        const items = await models[modelName].find(jqa || [], opts) || [];
        const count = await models[modelName].count(jqa || [], opts) || 0;

        ctx.body = items.map((it) => {
            return it.display();
        });

        //Set headers
        ctx.set('jollof-total-count', count);

        //} catch (err) {
        //    httpUtil.handleError(ctx, err);
        //}

    },

    //Get singular
    get: async function (ctx) {
        //try {
        const id = ctx.params.id;
        const modelName = ctx.params.modelName;
        const res = await models[modelName].findById(id);

        if (res)
            ctx.body = res.display();
        else {
            throw new Boom.notFound(`${modelName}:${id} not found`)
        }
        //} catch (err) {
        //    httpUtil.handleError(ctx, err);
        //}
    },

    /**
     * Create
     * The whole point of create is to Persist new data, regardless of whether or not it already has an Id.
     * @param modelName
     */
    create: async function (ctx) {
        //get schema
        //try {
        // console.log('body',ctx.request.body)    // if buffer or text
        // console.log('files',ctx.request.files)   // if multipart or urlencoded
        // console.log('fields',ctx.request.fields)
        const modelName = ctx.params.modelName;
        const payload = ctx.request.fields;


        delete payload['id'];

        const res = await models[modelName].persist(payload);

        ctx.body = res.display();
        //} catch (err) {
        //    httpUtil.handleError(ctx, err);
        //}
    },

    //Update
    update: async function (ctx) {
        //try {
        // console.log('body',ctx.request.body)    // if buffer or text
        // console.log('files',ctx.request.files)   // if multipart or urlencoded
        // console.log('fields',ctx.request.fields)
        const modelName = ctx.params.modelName;
        const payload = ctx.request.fields;

        const res = await models[modelName].persist(payload);
        ctx.body = res.display();
        //console.log(ctx.body);
        //} catch (err) {
        //    httpUtil.handleError(ctx, err);
        //}
    },

    //Delete or disable
    delete: async function (ctx) {
        //try {
        const id = ctx.params.id;
        const modelName = ctx.params.modelName;
        ctx.body = await models[modelName].removeById(id)
        //} catch (err) {
        //    httpUtil.handleError(ctx, err);
        //}
    },
}

<|start_filename|>packages/jollof/lib/admin/rClient/util/mediaUtil.js<|end_filename|>
exports.isVideo = (urlParam, mime) => {

    if (mime && mime.indexOf('video') > -1) return true;
    if (!urlParam) return false;

    var url = urlParam.toLowerCase();

    return url.endsWith('.mp4') ||
        url.endsWith('.3gp') ||
        url.endsWith('.mov') ||
        url.endsWith('.flv') ||
        url.endsWith('.wmv') ||
        url.indexOf('youtube.com') > -1

}

exports.isImage = (urlParam, mime) => {

    if (mime && mime.indexOf('image') > -1) return true;
    if (!urlParam) return false;

    var url = urlParam.toLowerCase();

    return url.endsWith('.jpg') ||
        url.endsWith('.jpeg') ||
        url.endsWith('.png') ||
        url.endsWith('.gif') ||
        url.endsWith('.bmp') ||
        url.endsWith('.webp') ||
        url.endsWith('.svg') ||
        url.endsWith('.tiff')

}




<|start_filename|>packages/jollof/lib/data/registry.js<|end_filename|>
/**
 * Created by iyobo on 2016-10-27.
 */
const Modelizer = require('./modelizer');

const _ = require('lodash');
const models = {};
const types = {};
const services = {};
const appPaths = require('../../appPaths');
const path = require('path');
const requireDir = require('require-dir');
const log = require('../log');

var requireDirOptional = require('../util/fileUtil.js').requireDirOptional;

/**
 * What gets stored in models{} needs to be a fully functioning
 * mongoose-like model (e.g. findAll(), findOne, etc)...only that it's ours.
 * @param schema
 */
module.exports.registerModel = function (schema) {
    //Wrap whatever this is in a true Model
    models[schema.name] = Modelizer.modelize(schema);
    return models[schema.name];
};

module.exports.registerType = function (schema) {
    types[schema.name] = (opts) => {
        return _.merge({},schema.structure, opts);
    };
    return types[schema.name];
};
module.exports.registerService = function (name, Class) {
    services[name] = new Class();
    return services[name];
};

/**
 * Populates registry with all models and types.
 * @param schema
 */
module.exports.init = function (schema) {
    // try {
    //Init in-built Schema Types and models
    requireDir(path.join(__dirname, 'types'), { recurse: true });
    requireDir(path.join(__dirname, 'models'), { recurse: true });

    //Init app Schema Types
    requireDirOptional(appPaths.schemaTypes, { recurse: true });

    //Init app models
    requireDirOptional(appPaths.models, { recurse: true });

    //Init app services
    requireDirOptional(appPaths.services, { recurse: true });


    // }catch(err){
    // 	log.error('Jollof init error:',err.message);
    // }

};

module.exports.models = models;
module.exports.types = types;
module.exports.services = services;

<|start_filename|>packages/jollof/jollofstatic/css/home.css<|end_filename|>
@keyframes slideInLeft {
    0% {
        transform: translateX(-50px);
        opacity: 0;
    }
    100% {
        transform: translateX(0);
        opacity: 1;
    }
}

body#background {
    background: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.8)), url('../jollof.png') no-repeat 50% 50% fixed;
    background-size: cover;
    -moz-background-size: cover;
    -o-background-size: cover;
    -webkit-background-size: cover;
    font-family: 'Open Sans Light', sans-serif;
}
a{
    font-size: 25px;
    text-decoration: none;
}

.maincover{
    margin: auto;
    top: 30%;
    right: 0;
    bottom: 0;
    left: 0;
    position: absolute;
    width: 800px;
    color: white;

    text-align: center;

    animation-name: slideInLeft;
    animation-iteration-count: 1;
    animation-timing-function: ease-out;
    animation-duration: 0.6s;
}

.title{
    font-size: 70px;

}

.subtitle{
    padding: 10px;

    text-align: center;

}

.subtitle .col{
    width: 800px;
}

.gobox{
    margin: 0 auto;
    background-color: white;
    width: 250px;
    padding: 10px;
    animation-delay: 0.4s;

    border-radius: 20px;
}

.adminCog{
    animation-delay: 0.5s;
    vertical-align:middle;
}

.adminLabel{
    animation-delay: 0.2s;
    color: #343434;
    font-weight: 800;

    display: inline-block;

}


<|start_filename|>packages/jollof/lib/data/types/richtext.js<|end_filename|>
const registry = require('../registry');

/**
 * This is an in-built Schema Type.
 * Schema Types are schemas themselves, and can be used inside other schemas.
 *
 * This type is a singular richtext field def.
 */
const richtextSchema = {
	name: 'RichText',
    required: false,
	structure: {
        type: String,
		meta:{
			widget: 'richtext',
		}
    }
}


module.exports = registry.registerType(richtextSchema);

<|start_filename|>packages/jollof-data-mongodb/jollofDataMongoDB.js<|end_filename|>
/**
 * Created by iyobo on 2016-10-30.
 */
const _ = require('lodash');

const mongodb = require('mongodb');
const MongoClient = mongodb.MongoClient;
const ObjectId = mongodb.ObjectID;

const convertToJollof = require('./util/conversionUtil.js').convertToJollof;
const convertConditionsFromJollof = require('./util/conversionUtil.js').convertConditionsFromJollof;

const connPool = {};

async function getConnection(url, opts) {

    if (connPool[url]) {
        return connPool[url];
    } else {
        const conn = await MongoClient.connect(url, opts || {
            poolSize: 3
        });
        connPool[url] = conn;
        return conn;
    }

}

/**
 * A Jollof Data Adapter for MongoDB using mongoose.
 */
class JollofDataMongoDB {

    /**
     * Keep constructor free of async calls.
     * Only use to set initial values like connection strings and similar sync commands.
     * @param options
     */
    constructor(options) {

        this.connectionOptions = options || {};

        this._MongoClient = MongoClient;
        this._convertConditionsFromJollof = convertConditionsFromJollof;
        this._convertToJollof = convertToJollof;
        this.ObjectId = ObjectId;
    }

    async ensureConnection() {
        const url = this.connectionOptions.mongoUrl || 'mongodb://localhost/nodb';
        const opts = this.connectionOptions.opts || { poolSize: 10 };

        this.db = await getConnection(url, opts);
    }

    /**
     * Jollof will run this on boot. Place all async or 'talking to database per collection' logic here.
     *
     * @param schema
     */
    async addSchema(schema) {

        //Because this is mongo, there is no need to do a whole lot here.
        //Just ensure the necessary connection is cached.
        await this.ensureConnection();
        return true;
    }


    /**
     * This is the primary Id field name to be used in all models under this adapter.
     * @returns {string}
     */
    get idField() {
        return '_id';
    }

    /**
     * This is the primary Id type
     * @returns {*}
     */
    static get IdType() {
        return ObjectId;
    }

    /**
     * These are fields that this adapter's datasource needs, but that might not relate to Jollof.
     * Jollof might use this to know and preserve these fields.
     *
     * @returns {string}
     */
    get keepFields() {
        return [];
    }

    /**
     * Return the created item
     * @param collectionName
     * @param criteria
     * @param newValues
     * @param params
     * @returns {*}
     */
    async create(collectionName, data) {

        //await this.ensureConnection();
        const res = await this.db.collection(collectionName).insertOne(data);
        return convertToJollof(res.ops[0]);
    }


    /**
     *
     * @param collectionName
     * @param criteria
     * @param opts
     * @returns {*}
     */
    async find(collectionName, criteria, opts = {}) {
        //await this.ensureConnection();

        //If we're paging
        let res;

        const params = convertConditionsFromJollof(criteria);

        //console.log(params)

        let cursor = this.db.collection(collectionName).find(params);

        if (opts) {

            if (opts.paging) {
                const page = opts.paging.page || 1;
                const limit = opts.paging.limit || 10;
                const skip = ((page - 1) * limit);

                cursor = cursor.skip(skip);
                cursor = cursor.limit(limit);
            }

            if (opts.sort) {
                cursor = cursor.sort(opts.sort)
            }

        }

        res = await cursor.toArray();
        return convertToJollof(res);

    }

    /**
     *
     * @param collectionName
     * @param criteria
     * @param opts
     * @returns {*}
     */
    async count(collectionName, criteria, opts) {
        //await this.ensureConnection();
        return await this.db.collection(collectionName).count(convertConditionsFromJollof(criteria));
    }


    /**
     * Return how many items were updated
     * @param collectionName
     * @param criteria
     * @param newValues
     * @param params
     * @returns {number} - How many were updated
     */
    async update(collectionName, criteria, newValues, opts) {
        //await this.ensureConnection();
        //opts = convertFromJollof(opts);
        const q = convertConditionsFromJollof(criteria);
        const res = await this.db.collection(collectionName).updateMany(q, { $set: newValues });
        //console.log('item update result', res);
        return res.modifiedCount;
    }


    /**
     * Deletes all rows that match the criteria.
     * Return number of items deleted
     *
     * @param collectionName
     * @param criteria
     * @param params
     * @returns {number}
     */
    async remove(collectionName, criteria, opts) {
        //await this.ensureConnection();
        _.merge(opts, { multi: true })
        const res = await this.db.collection(collectionName).deleteMany(convertConditionsFromJollof(criteria), opts);

        return res.deletedCount;

    }

}

module.exports = JollofDataMongoDB;

<|start_filename|>packages/koa-jollof-router/index.js<|end_filename|>
/**
 * Created by iyobo on 2017-05-13.
 */
/**
 * Convenience wrapper around koa-router
 */

/**
 * @type {Array}
 */

const Router = require('koa-router');
const makeRoute = require('./lib/util.js').makeRoute;
const digestRouteMap = require('./lib/util.js').digestRouteMap;

class KoaJollofRouter {

    constructor(routes) {
        this.router = Router();

        if (routes) {
            this.addRoutes(routes);
        }
    }

    /**
     * Takes in a map of routes.
     * Should support nesting
     *
     * @param routes - a map with keys like '/', 'get /' or 'post /foo/:bar'
     */
    addRoutes(routes) {

        this.router = digestRouteMap(this.router, routes);
    }

    nestRoutes(path, flow, routes) {
        this.router = makeRoute(this.router, '', path, flow, routes);
    }

    /**
     * use with koa.use as you would koa-router
     * i.e  app.use(jRouter.apply());
     */
    get routes() {
        return this.router.routes.bind(this.router);
    }

    /**
     * use with koa.use as you would koa-router
     * i.e  app.use(jRouter.apply());
     */
    get allowedMethods() {
        return this.router.allowedMethods.bind(this.router);
    }

}


module.exports = KoaJollofRouter;

<|start_filename|>packages/jollof/lib/router/subdomain.js<|end_filename|>
/**
 * Copyright: <NAME>
 * License: MIT
 *
 * Koa Sub-Domain Constructor.
 * @param {Object} sub Subdomain can be also a FQDN or a wildcard like *.example.com
 * œparam {Object} r Can be a generator function or a Koa-router
 * @api public
 */
function Subdomain(sub, r){
	if (!(this instanceof Subdomain)){
		var instance = new Subdomain(sub, r);
		return instance;
	}

	if(!sub || !r || typeof sub !== 'string' || (typeof r !== "object" && typeof r !== "function")){
		throw new Error('Missing or wrong params! Expected first param a STRING (is '+(typeof sub)+') and second a GENERATOR-Object or a function (is '+(typeof r)+')');
	}
	/**
	 * steps to test:
	 * 1) sub === this.hostname
	 * 2) this.subdomain[0] === sub
	 * 3) typeof this.subdomain[1] === 'string' // or wildcard => sub sub domain ?
	 */
	return function *(next){
		// for the case someone enters a complete domain name
		// e.g. 'sub.example.com'
		// instead of 'sub' or '*'


		if(sub === this.hostname){
			if(typeof r.routes === "function") {
				yield r.routes();
			} else
			{
				yield r;
			};

		}

		var subdomain = this.subdomains[0] || '';
		if (subdomain === sub && sub[0] !== '*' && !this.subdomains[1]) {
			if(typeof r.routes === "function") {
				yield r.routes();
			} else
			{
				yield r;
			};
		} else if(typeof this.subdomains[1] === 'string'
			|| sub[0] === "*"){
			var subdomainsstring = "";
			var length = this.subdomains.length;
			for(var i=length; i > 0; i-- ){
				if(subdomainsstring === ""){
					subdomainsstring += this.subdomains[i-1];
				}else {
					subdomainsstring += "."+this.subdomains[i-1];
				}
			}
			// wildcard subdomain at most left in "sub" string
			var splittedSub = sub.split(".");
			if(splittedSub[0] === "*"){
				var subdomains = this.subdomains;
				var hostname = this.hostname.split(".");
				var hostnamelength = hostname.length;
				// reverse lookup arrays against each other
				for(var s=hostnamelength; s > 0; s--){
					var hostnamepop = hostname.pop();
					if(subdomains.length > 0){
						var shift = subdomains.shift();
					}
					var pop = splittedSub.pop();

					if(shift === pop || hostnamepop === pop){
						continue;
					} else {
						if(pop === "*"){
							if(typeof r.routes === "function") {
								yield r.routes();
							} else
							{
								yield r;
							};
							break;
						} else {
							yield next;
							break;
						}
					}
				}
			}

			// NOTE: app.subdomainOffset is the base!
			if(subdomainsstring === sub){
				if(typeof r.routes === "function") {
					yield r.routes();
				} else
				{
					yield r;
				};
			} else {
				yield next;
			}
		}
		else{
			yield next;
		}
	};


}

module.exports = Subdomain;

<|start_filename|>packages/jollof/lib/forms/fields/HiddenField.js<|end_filename|>
'use strict'
/**
 * Created by iyobo on 2016-04-25.
 */


class HiddenField extends require('./Field'){

	constructor(form, params){
		super(form, params)
		this.options = params.options|| []; //must be an array
		this.label="";
		this.type="hidden"
	}

}

module.exports = HiddenField

<|start_filename|>package.json<|end_filename|>
{
  "scripts": {
    "dev": "cd packages/jollof && npm run dev",
    "build": ". build.sh",
    "publish": "npm run build && lerna publish",
    "test": "NODE_ENV=test cd packages/jollof && npm test"
  },
  "devDependencies": {
    "lerna": "^2.0.0"
  },
  "dependencies": {}
}


<|start_filename|>packages/jollof/lib/admin/constraints/adminSecurity.js<|end_filename|>
exports.loggedIn = async function (ctx, next) {

    if (!ctx.isAuthenticated()) {

        //TODO: If I don't have the right privs
        //TODO: if I have the right privs
        await next();

    } else {

        //Try to login
        await ctx.login();

        console.log(ctx.state.user);

        ctx.response.redirect('/login?nextUrl=' + this.originalUrl)
    }


    //if (JOLLOF_STANDALONE || (this.session.user && this.session.user.canViewAdmin)) {
    //    // allow access only if logged in and has canViewAdmin flag
    //    yield next;
    //} else if (this.session.user) {
    //    //authenticated, but not authorized
    //    this.response.redirect('/')
    //} else {
    //    this.response.redirect('/login?nextUrl=' + this.originalUrl)
    //}
}

<|start_filename|>packages/jollof/lib/filestorage/engines/local.js<|end_filename|>
/**
 * Created by iyobo on 2016-11-27.
 */
const config = require('../../configurator').settings;
const path = require('path');
const fs = require('fs-promise');
const uuid = require('uuid');
const log = require('../../log');

module.exports = new class LocalFileStorage {

	/**
	 * move the file and return file metadata
	 * @param file
	 * @param metaData
	 * @param opts
	 */
	* store( file, opts={} ) {

		//Determine indexing prefix
		const hash = uuid().toLowerCase();

		//Determine path
		const rootPath = opts.private? config.fileStorage.engines.local.privateRoot: config.fileStorage.engines.local.publicRoot;
		const fileName = file.name.toLowerCase();

		let newPath = path.join(rootPath, hash[0], hash[1], hash+'__'+fileName);
		log.debug('Saving file '+fileName+' as '+newPath);
		yield fs.move(file.path, newPath);

		const resp= {
			name: fileName,
			size: file.size,
			type: file.type,

			key: hash,

			engine: 'local',
			path: newPath,
		};

		if(!opts.private){
			resp.url = config.fileStorage.engines.local.basePublicUrl+'/'+ [hash[0], hash[1], hash+'__'+fileName].join('/');
		}


		return resp;

	}


}();

<|start_filename|>packages/jollof/lib/algorithms/sorting/insertionsort.js<|end_filename|>
/**
 * Created by iyobo on 2016-05-25.
 */

function swap(arr,a,b){
	var temp = arr[a]
	arr[a] = arr[b]
	arr[b] = temp
}
function sort(arr){

	for(var i=1; i<arr.length; i++){
		if(arr[i]<arr[i-1]){
			for(var j=i; j>=0; j--){
				if(arr[j]<arr[j-1])
					swap(arr,j,j-1)
				else
					break;
			}
		}
	}

	return arr;
}

console.log(sort([23,42,4,16,8,15,1]));

<|start_filename|>packages/jollof/lib/data/types/textarea.js<|end_filename|>
const registry = require('../registry');

/**
 * This is an in-built Schema Type.
 * Schema Types are schemas themselves, and can be used inside other schemas.
 *
 * This type is a singular textarea field def.
 */
const textareaSchema = {
	name: 'Textarea',
    required: false,
	structure: {
        type: String,
		meta:{
			widget: 'textarea',
		}
    }
}


module.exports = registry.registerType(textareaSchema);

<|start_filename|>packages/jollof/lib/algorithms/greedy.js<|end_filename|>
function computeChange(coins, amount) {
	// Create a array that is used to return the final result, instead of the global one.
	var coincount = [];

	// use the given `amount` to set `creminder ` rather than `AMOUNT` which may not be accessible if your code is called otherplace rather than here.
	var i = 0; var creminder = amount; var ccoin;


	while( i < coins.length )
	{
		// Lazily init the used coin for coin type i to 0.
		coincount[i] = 0;
		while ( coins[i] <= creminder )
		{
			creminder = creminder - coins[i];
			ccoin = coincount[i];
			ccoin += 1;
			coincount[i] = ccoin;
		}

		i++;
	}

	return coincount;
}

console.log(computeChange([23,4,5,2],45))

<|start_filename|>packages/jollof/test/testModels.js<|end_filename|>
/**
 * Created by iyobo on 2017-01-01.
 */
const chai = require('chai');
const should = chai.should();
const expect = chai.expect;
const assert = chai.assert;
const _ = require('lodash');

const co = require('co');
const jollof = require('../');
const Foo = jollof.models.Foo;

beforeEach('Clear DB', async () => {
    const deletionThunks = [];

    _.each(jollof.models, (model, name) => {
        deletionThunks.push(async () => {
            return await model.remove({}, { multi: true })
        });
    });

    return Promise.all(deletionThunks)
});

describe('Jollof Data', function () {

    describe('Models', function () {

        it('save() and findOne()', async function () {


            const john = new Foo({ name: '<NAME>', age: 25 });
            await john.save();
            //console.log(john)

            const cain = new Foo({ name: '<NAME>', age: 999 });
            await cain.save();
            //console.log(cain);

            const person = await Foo.findOne({ age: 25 });

            person.age.should.equal(25);

        });

        it('findOne with id as param', function () {

            return co(function* () {

                const john = new Foo({ name: '<NAME>', age: 25 });
                yield* john.save();
                //console.log(john)

                const cain = new Foo({ name: '<NAME>', age: 999 });
                yield* cain.save();
                //console.log(cain);

                const person = yield* Foo.findOne({ id: cain.id });

                expect(person).to.exist;
                expect(person.age).to.equal(999);
                expect(person.id).to.equal(cain.id);

            });

        });

        it('validate() fails as should', function () {

            return co(function* () {

                try {
                    const person = new Foo({ name: 24, age: 26 });
                    yield person.save();

                    assert.fail(0, 0, 'This validation should have failed, but passed anyway.')
                } catch (err) {
                    if (err.isOperational)
                        expect(err.isOperational).to.be.true;
                    else
                        throw err;

                }
            });

        });

        it('findById()', function () {

            return co(function* () {

                const john = new Foo({ name: '<NAME>', age: 25 });
                yield* john.save();
                //console.log(john)

                const cain = new Foo({ name: '<NAME>', age: 999 });
                yield* cain.save();
                //console.log(cain);

                const person = yield* Foo.findById(cain.id);

                expect(person).to.exist
                expect(person.age).to.equal(999);
                expect(person.id).to.equal(cain.id);

            });

        });


        describe('find(criteria,opts)', function () {

            beforeEach('equal', () => {
                return co(function* () {
                    const p2 = new Foo({ name: '<NAME>', age: 25 });
                    yield* p2.save();

                    const p1 = new Foo({ name: '<NAME>', age: 33 });
                    yield* p1.save();

                    const p3 = new Foo({ name: '<NAME>', age: 25 });
                    yield* p3.save();

                    const p4 = new Foo({ name: 'Lox', age: 3 });
                    yield* p4.save();

                });
            });

            it('with id as param', function () {

                return co(function* () {

                    const john = new Foo({ name: '<NAME>', age: 25 });
                    yield* john.save();
                    //console.log(john)

                    const cain = new Foo({ name: '<NAME>', age: 999 });
                    yield* cain.save();
                    //console.log(cain);

                    const person = yield* Foo.find({ id: cain.id });

                    expect(person).to.not.be.empty;
                    expect(person[0].age).to.equal(999);
                    expect(person[0].id).to.equal(cain.id);

                });

            });

            it('equal', () => {

                return co(function* () {

                    // equal
                    let res = yield* Foo.find({ age: 25 });
                    expect(res.length).to.equal(2);
                });
            });
            it('not equal', () => {

                return co(function* () {
                    //not equal
                    res = yield* Foo.find({ age: { $ne: 3 } });
                    expect(res.length).to.equal(3);
                });
            });
            it('lt', () => {

                return co(function* () {
                    //less than
                    res = yield* Foo.find({ age: { $lt: 5 } });
                    expect(res.length).to.equal(1);
                    expect(res[0].name).to.equal('Lox');
                });
            });
            it('lte', () => {

                return co(function* () {
                    //less than or equal
                    res = yield* Foo.find({ age: { $lte: 25 } });
                    expect(res.length).to.equal(3);
                });
            });
            it('gt', () => {

                return co(function* () {
                    //greater than
                    res = yield* Foo.find({ age: { $gt: 25 } });
                    expect(res.length).to.equal(1);
                    expect(res[0].name).to.equal('<NAME>');
                });
            });
            it('gte', () => {

                return co(function* () {
                    //greater than or equal
                    res = yield* Foo.find({ age: { $gte: 25 } });
                    expect(res.length).to.equal(3);
                });
            });
            it('in', () => {

                return co(function* () {
                    //in
                    res = yield* Foo.find({ age: { $in: [25, 45] } });
                    expect(res.length).to.equal(2);
                });
            });
            it('not in', () => {

                return co(function* () {
                    //not in
                    res = yield* Foo.find({ age: { $nin: [25, 45, 3] } });
                    expect(res.length).to.equal(1);
                    expect(res[0].name).to.equal('<NAME>');

                });
            });
            it('regex', () => {

                return co(function* () {
                    //regex
                    res = yield* Foo.find({ name: { $regex: /Masta/ } });
                    expect(res.length).to.equal(1);

                });

            });

            it('paging', () => {

                return co(function* () {
                    res = yield* Foo.find({}, {
                        paging: {
                            page: 2,
                            limit: 3
                        }
                    });
                    expect(res.length).to.equal(1);

                });

            });

            it('sorting ASC', () => {

                return co(function* () {
                    res = yield* Foo.find({}, {
                        sort: {
                            age: 1,
                        }
                    });
                    expect(res[0].age).to.equal(3);

                });

            });
            it('sorting DESC', () => {

                return co(function* () {
                    res = yield* Foo.find({}, {
                        sort: {
                            age: -1,
                        }
                    });
                    expect(res[0].age).to.equal(33);

                });

            });

        });

        it('remove(criteria,opts)', function () {

            return co(function* () {

                const p1 = new Foo({ name: '<NAME>', age: 33 });
                yield* p1.save();

                const p2 = new Foo({ name: '<NAME>', age: 25 });
                yield* p2.save();

                const p3 = new Foo({ name: '<NAME>', age: 15 });
                yield* p3.save();

                yield* Foo.remove({ age: 25 });

                let res = yield* Foo.find({});
                expect(res.length).to.equal(2);

                res = yield* Foo.find({ age: 25 });
                expect(res.length).to.equal(0);

            });

        });

        it('update(criteria,opts)', function () {

            return co(function* () {

                const p1 = new Foo({ name: '<NAME>', age: 33 });
                yield* p1.save();

                const p2 = new Foo({ name: '<NAME>', age: 25 });
                yield* p2.save();

                const p3 = new Foo({ name: '<NAME>', age: 15 });
                yield* p3.save();

                yield* Foo.update({ age: 25 }, { age: 50 });

                let res = yield* Foo.find({ age: 50 });
                expect(res.length).to.equal(1);

                res = yield* Foo.find({ age: 25 });
                expect(res.length).to.equal(0);

            });

        });


    });
});


<|start_filename|>packages/jollof/queryTest.js<|end_filename|>
/**
 * Created by iyobo on 2017-04-25.
 */
const jollofQL = require('./lib/data').jollofql;
const parser = jollofQL.jqlParser;
const jql = jollofQL.jql;

console.log('testing SQ parser')
const util = require('util');

let val;
let val2;
const n = 90;

//const arr = [1, 2, 3]
//const inj = '105 OR id != null';
//val = parser.parse(jql`po = ${23} and home.*.city = ${inj} and xyz in ${arr} and (lastName != 'doe' or statusId in(1, 2, 3)) `);
//const startTime = Date.now();
//for (let i = 0; i < n - 1; i++) {
//    val2 = parser.parse(`home.*.city = ${inj} and xyz in ${arr} and (lastName != 'doe' or statusId in(1, 2, 3)) `);
//}
//
//const totalTime = Date.now() - startTime;
//console.log(`Performance: ${totalTime}ms  Mean: ${totalTime / n}ms`);
//console.log(val);
//console.log(val2);


const badInjection = "bar's OR id != null";
const badArray = [1, 2, 'f ) OR id != null '];
const arr = [1,2, 'f'];

const safeQuery = jql`foo = ${badInjection} AND age > 30 AND ( xyz in ${badArray} OR ijk in (j,k,9) ) AND arr not in ${arr}`;
const conditions = parser.parse(safeQuery)
console.log(conditions);
//const str = parser.compile(conditions);
//console.log(str);


<|start_filename|>packages/jollof/lib/util/fileUtil.js<|end_filename|>
const requireDir = require('require-dir');
const fs = require('fs')
const co = require('co')

module.exports = {
	/**
	 * Stores a file depending on app-specified file storage engine
	 * @param str
	 * @returns {*}
	 */
	labelize: function (str) {
		return str.replace
		(/(^[a-z]+)|[0-9]+|[A-Z][a-z]+|[A-Z]+(?=[A-Z][a-z]|[0-9])/g
			, function (match, first) {
				if (first) match = match[0].toUpperCase() + match.substr(1);
				return match + ' ';
			}
		)
	},

	/**
	 * RequireDir only if path actually exists.
	 * @param path
	 * @param opts
	 */
	requireDirOptional: function (path, opts) {

		if (fs.existsSync(path)) {
			requireDir(path, opts);
		}
	},

	/**
	 * Returns the first existing path from an array of paths.
	 * This is a blocking function.
	 * @param arr
	 */
	firstExistingPath: function (arr) {
		for (let i in arr) {

			if (fs.existsSync(arr[i])) return arr[i];
		}
	}
}

<|start_filename|>packages/jollof/jollofstatic/css/admin.css<|end_filename|>
/* Enter and leave animations can use different */
/* durations and timing functions.              */
.slide-fade-enter-active {
    transition: all .3s ease;
}

.slide-fade-leave-active {
    transition: all 0s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}

.slide-fade-enter, .slide-fade-leave-to
    /* .slide-fade-leave-active for <2.1.8 */
{
    transform: translateX(10px);
    opacity: 0;
}

.pad-5 {
    padding: 5px;
}

.marg-5 {
    margin: 5px;
}

.marg-10 {
    margin: 10px;
}

/*OVerride for sidebar active link*/
.sidebar .nav li > a.active {
    color: #FFFFFF;
    opacity: 1;
    background: rgba(255, 255, 255, 0.23);
}

.filter-control {
    background-color: #FFFFFF;
    border: 1px solid #E3E3E3;
    border-radius: 4px;
    color: #565656;
    padding: 8px 12px;
    height: 30px;
    -webkit-box-shadow: none;
    box-shadow: none;
    max-width: 30%;
    width: 200px;
    display: -webkit-inline-box;
}

.fakeLink {
    color: #1DC7EA;
    /*text-decoration: underline;*/
    cursor: pointer;
}

@keyframes fadeInLeft {
    from {
        opacity: 0;
        -webkit-transform: translate3d(-5%, 0, 0);
        transform: translate3d(-5%, 0, 0);
    }

    to {
        opacity: 1;
        -webkit-transform: none;
        transform: none;
    }
}

.fadeInLeft {
    -webkit-animation-name: fadeInLeft;
    animation-name: fadeInLeft;
}

.animated {
    animation-duration: 0.5s;
}

/*Notifications*/
.alert-success {
    background-color: #02895e;
}

.alert-info {
    background-color: #292929;
}

.redText {
    color: #ff2a48;
}

.jconfirm-box {
    min-width: 350px;
}

/*///FIELDS*/
.react-date-field__input[disabled] {
    background-color: #cdcdcd;
}

.react-date-field [disabled] {
    background-color: #cdcdcd;
}

.chkbox {
    cursor: pointer;
}

textarea {
    width: 500px;
    /*height: 150px;*/
}

/*----Miigrated from old client*/

/*//Filepreview v2*/
.filePreview .fileImage {
    width: 100%;
    max-width: 300px;
}

.wrapText {
    word-wrap: break-word;
}

.pad-5 {
    padding: 5px;
}

.bold {
    font-weight: 400;
}

.list {
    max-width: 100px;
}

.single {
    font-size: 20px;
}

.jollofMap {
    height: 450px;
    width: 100%;
}

.full-width {
    width: 100%;
    /*margin: 5px 0px;*/
}

.textbox {
    font-size: 16px;
    padding: 10px;
}

.tpad-10 {
    margin-top: 10px;
}

label.label {
    display: block;
    color: #999999;
    font-size: 10px;
    line-height: 0.9;
}

.mce-notification {
    display: none !important;
}

.CodeMirror {
    height: 120px !important;
}

.sidebar {
    background: linear-gradient(
            rgba(50, 50, 50, 0.90),
            rgba(50, 50, 50, 0.90)
    ), url(/jollofstatic/img/jollof2.jpg) center;
    -webkit-background-size: cover;
    -moz-background-size: cover;
    -o-background-size: cover;
    background-size: cover;
}

.card label {
    font-size: 14px;
    color: black
}

.form-group > label {
    color: #5e8d9a;
    font-size: 14px;
}

<|start_filename|>packages/jollof/lib/data/types/file.js<|end_filename|>
const registry = require('../registry');

/**
 * This is an in-built Schema Type.
 * Schema Types are schemas themselves, and can be used inside other schemas.
 * Just be careful not to create an infinite loop by using a custom Schema Type in itself or child.
 */
const fileSchema = {
	name: 'File',
	structure: {
        type: Object,
        required: false,
        schema: {
            name: { type: String, required: true },
            size: Number,
            type: { type: String, required: true },

            path: String,
            url: String,
            key: String,

            engine: { type: String, required: true , default:'http'},
        },
		meta:{
			widget: 'file',
		}
    }
}


module.exports = registry.registerType(fileSchema);

<|start_filename|>packages/jollof/lib/data/plugins/validate-plugin-array.js<|end_filename|>
/**
 * Created by iyobo on 2016-10-29.
 */
var _ = require("lodash")
var s = require("util").format

exports.register = function (validateModule) {
	if (!validateModule) validateModule = require("validate.js");

	var val = function (value, options, key, attributes) {
		var validateElement = function (element) {
			return validateModule(element, options)
		}
		var validations = _.chain(value).map(validateElement).filter().value()
		if (_.isEmpty(validations)) return
		return s("element %j does not validate: %j", value, validations)
	}

	var async = function (args) {
		args = arguments
		return new validateModule.Promise(function (resolve, reject) {
			var result = val.apply(this, args)
			resolve(result)
		})
	}

	validateModule.validators.array = val
	validateModule.validators.arrayAsync = async
}

<|start_filename|>packages/jollof/jollofstatic/aoradmin.css<|end_filename|>

.clickable {
    cursor: pointer;
}

.fill {
    width: 100%;
    height: 100%;
}

/*.filePreview .image {*/
/*width: 100px;*/
/*height: 100px;*/
/*overflow:hidden;*/
/*margin: 5px;*/
/*}*/

/*.overlay {*/
/*background: rgba(0, 0, 0, .50);*/
/*text-align: center;*/
/*padding: 45px 0 66px 0;*/
/*opacity: 0;*/
/*-webkit-transition: opacity .25s ease;*/
/*transition: opacity .25s ease;*/
/*}*/

/*.filePreview .image:hover .overlay {*/
/*opacity: 1;*/
/*}*/

/*.x {*/
/*font-family: Helvetica;*/
/*font-weight: 900;*/
/*color: rgba(255, 162, 160, 0.85);*/
/*font-size: 96px;*/
/*}*/

/*//Filepreview v2*/
.filePreview .fileImage {
    width: 100%;
    max-width: 300px;
}

.wrapText {
    word-wrap: break-word;
}

.pad-5 {
    padding: 5px;
}

.bold {
    font-weight: 400;
}

.list {
    max-width: 100px;
}

.single {
    font-size: 20px;
}

.jollofMap {
    height: 450px;
    width: 100%;
}

.full-width {
    width: 100%;
    /*margin: 5px 0px;*/
}

.textbox {
    font-size: 16px;
    padding: 10px;
}

.tpad-10 {
    margin-top: 10px;
}

label.label {
    display: block;
    color: #999999;
    font-size: 10px;
    line-height: 0.9;
}

.myButton {
    -moz-box-shadow:inset 0px 1px 0px 0px #ffffff;
    -webkit-box-shadow:inset 0px 1px 0px 0px #ffffff;
    box-shadow:inset 0px 1px 0px 0px #ffffff;
    background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #ededed), color-stop(1, #dfdfdf));
    background:-moz-linear-gradient(top, #ededed 5%, #dfdfdf 100%);
    background:-webkit-linear-gradient(top, #ededed 5%, #dfdfdf 100%);
    background:-o-linear-gradient(top, #ededed 5%, #dfdfdf 100%);
    background:-ms-linear-gradient(top, #ededed 5%, #dfdfdf 100%);
    background:linear-gradient(to bottom, #ededed 5%, #dfdfdf 100%);
    filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#dfdfdf',GradientType=0);
    background-color:#ededed;
    -moz-border-radius:6px;
    -webkit-border-radius:6px;
    border-radius:6px;
    border:1px solid #dcdcdc;
    display:inline-block;
    cursor:pointer;
    color:#777777;
    font-family:Arial;
    font-size:15px;
    font-weight:bold;
    padding:6px 24px;
    text-decoration:none;
    text-shadow:0px 1px 0px #ffffff;
}
.myButton:hover {
    background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #dfdfdf), color-stop(1, #ededed));
    background:-moz-linear-gradient(top, #dfdfdf 5%, #ededed 100%);
    background:-webkit-linear-gradient(top, #dfdfdf 5%, #ededed 100%);
    background:-o-linear-gradient(top, #dfdfdf 5%, #ededed 100%);
    background:-ms-linear-gradient(top, #dfdfdf 5%, #ededed 100%);
    background:linear-gradient(to bottom, #dfdfdf 5%, #ededed 100%);
    filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#dfdfdf', endColorstr='#ededed',GradientType=0);
    background-color:#dfdfdf;
}
.myButton:active {
    position:relative;
    top:1px;
}

/*Reduce width of long columns*/
/*#root > div > div > div.body > div:nth-child(1) > div > div > div > table > tbody > tr > td{*/
    /*width: 100px;*/
/*}*/


/*Header*/
/*#root > div > div > div.body > div:nth-child(1) > div > div > div > div:nth-child(2){*/
    /*position: absolute !important;*/
/*}*/

/*!*Action Bar on top*!*/
/*#root > div > div > div.body > div:nth-child(1) > div > div > div > div:nth-child(1){*/
    /*float: left !important;*/
    /*margin-top: 40px;*/
/*}*/

.example-enter {
    opacity: 0.01;
}

.example-enter.example-enter-active {
    opacity: 1;
    transition: opacity 500ms ease-in;
}

.example-leave {
    opacity: 1;
}

.example-leave.example-leave-active {
    opacity: 0.01;
    transition: opacity 300ms ease-in;
}

<|start_filename|>packages/jollof/lib/data/jollofql/index.js<|end_filename|>
const jollofql = require('./jollofql');
const parser = jollofql.parser;


function cleanString(value) {
    const v = value.replace(/"/g, '\"');

    return '"' + v + '"';
}

/**
 * A tag to be used with ES6 string literals to prevent JQL injection.
 * Helps to build JQL queries.
 *
 * @param queryChunks
 * @param injects
 * @returns {string}
 */
function jql(queryChunks, ...injects) {
    try {
        let res = '';


        for (let i = 0; i < injects.length; i++) {

            let value = injects[i];
            let q = queryChunks[i];

            if (typeof value === 'string') {
                //make string value safe

                value = cleanString(value);
            }
            else if (Array.isArray(value)) {

                //We do not support nested arrays or objects
                value = value.map((it) => {
                    if (Array.isArray(it))
                        throw new Error('JQL: Nested arrays not supported . Consider using a nativeFunction for that query instead.')

                    if (typeof it === 'string') {
                        //make string value safe

                        it = cleanString(it);
                    }

                    return it;
                })

                value = "(" + value.join(', ') + ")";
            }

            res += q + value;

        }

        res += queryChunks[queryChunks.length - 1];

        //console.log('query:', res);

        return res;
    } catch (err) {
        throw new Boom.badRequest('Bad JQL:', err)
    }
}

function jqlToJSON(queryChunks, ...injects) {
    const safeJql = jql(queryChunks, ...injects);
    return parser.parse(safeJql);

}

module.exports = {
    jqlParser: parser,
    compile: jollofql.compile,
    jql,
    jqlToJSON
}

<|start_filename|>packages/jollof/index.js<|end_filename|>
/**
 * Created by iyobo on 2016-10-17.
 */
const requireDir = require('require-dir');
const appPaths = require('./appPaths');
const co = require('co');
const data = require('./lib/data');
const _ = require('lodash');
const fs = require('fs');

const log = require('./lib/log');
const version = require('./package.json').version;
log.info('JollofJS v' + version);


module.exports = {
    errors: requireDir('./lib/errors', { recurse: true }),
    fileStore: require('./lib/filestorage'),
    forms: requireDir('./lib/forms', { recurse: true }),
    filters: require('./lib/rendering'),
    view: {
        setupFilters: (renderEnv) => {
            require('./lib/rendering')(renderEnv);
            if (fs.existsSync(appPaths.viewFilters)) {
                require(appPaths.viewFilters)(renderEnv);
            }

        }
    },
    router: requireDir('./lib/router', { recurse: true }),
    utils: requireDir('./lib/util', { recurse: true }),
    cryto: require('./lib/crypto'),
    log: log,
    config: require('./lib/configurator').settings,
    currentEnv: require('./lib/configurator').currentEnv,
    appRoot: appPaths.appRoot,
    crypto: require("./lib/crypto"),
    spices: []

}

//Must be seperate from previous initialization of modules.export to avoid undefineds due to cyclic dependencies
module.exports.data = data;
module.exports.models = data.models;
module.exports.services = data.services;

//Initialize jollof data
data.init();

module.exports.bootstrap = require('./lib/bootstrapper/bootstrap');


<|start_filename|>packages/jollof/lib/data/types/email.js<|end_filename|>
const registry = require('../registry');

/**
 * This is an in-built Schema Type.
 * Schema Types are schemas themselves, and can be used inside other schemas.
 *
 * This type is a singular Email field def.
 */
const emailSchema = {
    name: 'Email',
    structure: {
        type: String,
        meta: {
            widget: 'email',
        },
        trim: true,
        match: /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
    }
}


module.exports = registry.registerType(emailSchema);