function factory(list) {
  var expressions = []
  var sensitive = []
  var insensitive = []

  construct()

  return test

  function construct() {
    var length = list.length
    var index = -1
    var value
    var normal

    while (++index < length) {
      value = list[index]
      normal = value === lower(value)

      if (value.charAt(value.length - 1) === '*') {
        // Regexes are insensitive now, once we need them this should check for
        // `normal` as well.
        expressions.push(new RegExp('^' + value.slice(0, -1), 'i'))
      } else if (normal) {
        insensitive.push(value)
      } else {
        sensitive.push(value)
      }
    }
  }

  function test(value) {
    var normal = lower(value)
    var length
    var index

    if (sensitive.indexOf(value) !== -1 || insensitive.indexOf(normal) !== -1) {
      return true
    }

    length = expressions.length
    index = -1

    while (++index < length) {
      if (expressions[index].test(value)) {
        return true
      }
    }

    return false
  }
}

function factory ( list ) { var expressions = [ ] var sensitive = [ ] var insensitive = [ ] construct ( ) return test function construct ( ) { var length = list . length var index = - 1 var value var normal while ( ++ index < length ) { value = list [ index ] normal = value === lower ( value ) if ( value . charAt ( value . length - 1 ) === '*' ) { expressions . push ( new RegExp ( '^' + value . slice ( 0 , - 1 ) , 'i' ) ) } else if ( normal ) { insensitive . push ( value ) } else { sensitive . push ( value ) } } } function test ( value ) { var normal = lower ( value ) var length var index if ( sensitive . indexOf ( value ) !== - 1 || insensitive . indexOf ( normal ) !== - 1 ) { return true } length = expressions . length index = - 1 while ( ++ index < length ) { if ( expressions [ index ] . test ( value ) ) { return true } } return false } }

Create a test based on a list of phrases.

Create a test based on a list of phrases .


function(rcb) {
                    return function() {
                        if (frc || !db[cname]) {
                            bindColCtls(db);
                        }
                        if (rcb) {
                            rcb.apply(this, arguments);
                        }
                    }
                }

function ( rcb ) { return function ( ) { if ( frc || ! db [ cname ] ) { bindColCtls ( db ) ; } if ( rcb ) { rcb . apply ( this , arguments ) ; } } }

creating callback with collection rebuilding


function() {
    Object.defineProperty(this, "_impl", {
        value : new EJDBImpl(),
        configurable : false,
        enumerable : false,
        writable : false
    });
    return this;
}

function ( ) { Object . defineProperty ( this , "_impl" , { value : new EJDBImpl ( ) , configurable : false , enumerable : false , writable : false } ) ; return this ; }

The nodejs database wrapper.
@class
@memberOf ejdb


function dummyText ( opts ) {
        var corpus = opts.corpus || 'lorem',
            i = opts.start,
            isRandom = typeof(i) === 'undefined',
            mustReset = typeof(origin) === 'undefined',
            skip = opts.skip || 1,
            sentences = opts.sentences || 1,
            words = opts.words,
            text = texts[corpus] || texts.lorem,
            len = text.length,
            output = [],
            s;

        if ( isRandom ) {
            i = Math.floor( Math.random() * len );
        }

        if ( mustReset ) {
            origin = i;
        }

        if ( isRandom ) {
            // possible modulo of a negative number, so take care here.
            i = ((i + len - origin) % len + len) % len;
        }

        while( sentences-- ) {
            s = text[i];
            if ( words ) {
                s = s.split(' ').slice(0,words).join(' ');
            }
            output.push( s );
            i = (i + skip) % len;
        }

        return output.join(' ');
    }

function
dummyText
(
opts
)
{
var
corpus
=
opts
.
corpus
||
'lorem'
,
i
=
opts
.
start
,
isRandom
=
typeof
(
i
)
===
'undefined'
,
mustReset
=
typeof
(
origin
)
===
'undefined'
,
skip
=
opts
.
skip
||
1
,
sentences
=
opts
.
sentences
||
1
,
words
=
opts
.
words
,
text
=
texts
[
corpus
]
||
texts
.
lorem
,
len
=
text
.
length
,
output
=
[
]
,
s
;
if
(
isRandom
)
{
i
=
Math
.
floor
(
Math
.
random
(
)
*
len
)
;
}
if
(
mustReset
)
{
origin
=
i
;
}
if
(
isRandom
)
{
i
=
(
(
i
+
len
origin
)
%
len
+
len
)
%
len
;
}
while
(
sentences
)
{
s
=
text
[
i
]
;
if
(
words
)
{
s
=
s
.
split
(
' '
)
.
slice
(
0
,
words
)
.
join
(
' '
)
;
}
output
.
push
(
s
)
;
i
=
(
i
+
skip
)
%
len
;
}
return
output
.
join
(
' '
)
;
}


function Back(options) {
  if (!(this instanceof Back)) {
    return new Back(options);
  }

  this.settings = extend(options);
  this.reconnect = null;
}

function Back ( options ) { if ( ! ( this instanceof Back ) ) { return new Back ( options ) ; } this . settings = extend ( options ) ; this . reconnect = null ; }

Takes a set of reconnect options defined in README


function css(files, output, options) {
  options = Object.assign({
    banner: false
  }, options);

  return () => {
    var build = gulp.src(files)

    if (options.banner)
      build = build.pipe($.header(banner, {pkg}));

    build = build
      .pipe($.rename('d3.compose.css'))
      .pipe(gulp.dest(output));

    return build;
  };
}

function css ( files , output , options ) { options = Object . assign ( { banner : false } , options ) ; return ( ) => { var build = gulp . src ( files ) if ( options . banner ) build = build . pipe ( $ . header ( banner , { pkg } ) ) ; build = build . pipe ( $ . rename ( 'd3.compose.css' ) ) . pipe ( gulp . dest ( output ) ) ; return build ; } ; }

Create css build function

@method css
@param {String|Array} files glob
@param {String} output folder
@param {Object} [options]
@param {Boolean} [options.banner = false] Include banner with build
@return {Function}


function series() {
  const tasks = Array.prototype.slice.call(arguments);
  var fn = cb => cb();

  if (typeof tasks[tasks.length - 1] === 'function')
    fn = tasks.pop();

  return (cb) => {
    const tasks_with_cb = tasks.concat([(err) => {
      if (err) return cb(err);
      fn(cb);
    }]);

    runSequence.apply(this, tasks_with_cb);
  }
}

function series ( ) { const tasks = Array . prototype . slice . call ( arguments ) ; var fn = cb => cb ( ) ; if ( typeof tasks [ tasks . length - 1 ] === 'function' ) fn = tasks . pop ( ) ; return ( cb ) => { const tasks_with_cb = tasks . concat ( [ ( err ) => { if ( err ) return cb ( err ) ; fn ( cb ) ; } ] ) ; runSequence . apply ( this , tasks_with_cb ) ; } }

Approximate gulp 4.0 series

@param {...String} ...tasks
@param {Function} [fn] Function to call at end of series
@return {Function}


function simpleTypeFilter(doc, oldDoc, candidateDocType) {
    if (oldDoc) {
      if (doc._deleted) {
        return oldDoc.type === candidateDocType;
      } else {
        return doc.type === oldDoc.type && oldDoc.type === candidateDocType;
      }
    } else {
      return doc.type === candidateDocType;
    }
  }

function simpleTypeFilter ( doc , oldDoc , candidateDocType ) { if ( oldDoc ) { if ( doc . _deleted ) { return oldDoc . type === candidateDocType ; } else { return doc . type === oldDoc . type && oldDoc . type === candidateDocType ; } } else { return doc . type === candidateDocType ; } }

A type filter that matches on the document's type property

A type filter that matches on the document s type property


function padRight(value, desiredLength, padding) {
    while (value.length < desiredLength) {
      value += padding;
    }

    return value;
  }

function padRight ( value , desiredLength , padding ) { while ( value . length < desiredLength ) { value += padding ; } return value ; }

Add the specified padding to the right of the given string value until its length matches the desired length


function resolveCollectionDefinition(doc, oldDoc, collectionDefinition, itemPrefix) {
    if (utils.isValueNullOrUndefined(collectionDefinition)) {
      return [ ];
    } else {
      if (typeof collectionDefinition === 'function') {
        var fnResults = collectionDefinition(doc, oldDoc);

        return resolveCollectionItems(fnResults, itemPrefix);
      } else {
        return resolveCollectionItems(collectionDefinition, itemPrefix);
      }
    }
  }

function resolveCollectionDefinition ( doc , oldDoc , collectionDefinition , itemPrefix ) { if ( utils . isValueNullOrUndefined ( collectionDefinition ) ) { return [ ] ; } else { if ( typeof collectionDefinition === 'function' ) { var fnResults = collectionDefinition ( doc , oldDoc ) ; return resolveCollectionItems ( fnResults , itemPrefix ) ; } else { return resolveCollectionItems ( collectionDefinition , itemPrefix ) ; } } }

Transforms the given collection definition, which may have been defined as a single item, a list of items or a function that returns a list of items into a simple list, where each item has the specified prefix, if any

Transforms the given collection definition which may have been defined as a single item a list of items or a function that returns a list of items into a simple list where each item has the specified prefix if any


function assignRolesToUsers(doc, oldDoc, accessAssignmentDefinition) {
    var users = resolveCollectionDefinition(doc, oldDoc, accessAssignmentDefinition.users);
    var roles = resolveRoleCollectionDefinition(doc, oldDoc, accessAssignmentDefinition.roles);

    role(users, roles);

    return {
      type: 'role',
      users: users,
      roles: roles
    };
  }

function assignRolesToUsers ( doc , oldDoc , accessAssignmentDefinition ) { var users = resolveCollectionDefinition ( doc , oldDoc , accessAssignmentDefinition . users ) ; var roles = resolveRoleCollectionDefinition ( doc , oldDoc , accessAssignmentDefinition . roles ) ; role ( users , roles ) ; return { type : 'role' , users : users , roles : roles } ; }


function getAllDocChannels(docDefinition) {
    var docChannelMap = utils.resolveDocumentConstraint(docDefinition.channels);

    var allChannels = [ ];
    if (docChannelMap) {
      appendToAuthorizationList(allChannels, docChannelMap.view);
      appendToAuthorizationList(allChannels, docChannelMap.write);
      appendToAuthorizationList(allChannels, docChannelMap.add);
      appendToAuthorizationList(allChannels, docChannelMap.replace);
      appendToAuthorizationList(allChannels, docChannelMap.remove);
    }

    return allChannels;
  }

function getAllDocChannels ( docDefinition ) { var docChannelMap = utils . resolveDocumentConstraint ( docDefinition . channels ) ; var allChannels = [ ] ; if ( docChannelMap ) { appendToAuthorizationList ( allChannels , docChannelMap . view ) ; appendToAuthorizationList ( allChannels , docChannelMap . write ) ; appendToAuthorizationList ( allChannels , docChannelMap . add ) ; appendToAuthorizationList ( allChannels , docChannelMap . replace ) ; appendToAuthorizationList ( allChannels , docChannelMap . remove ) ; } return allChannels ; }

Retrieves a list of channels the document belongs to based on its specified type


function outputHelpIfNecessary(cmd, options) {
  options = options || [];
  for (var i = 0; i < options.length; i++) {
    if (options[i] === '--help' || options[i] === '-h') {
      cmd.outputHelp();
      process.exit(0);
    }
  }
}

function outputHelpIfNecessary ( cmd , options ) { options = options || [ ] ; for ( var i = 0 ; i < options . length ; i ++ ) { if ( options [ i ] === '--help' || options [ i ] === '-h' ) { cmd . outputHelp ( ) ; process . exit ( 0 ) ; } } }

Output help information if necessary

@param {Command} command to output help for
@param {Array} array of options to search for -h or --help
@api private


function humanReadableArgName(arg) {
  var nameOutput = arg.name + (arg.variadic === true ? '...' : '');

  return arg.required
    ? '<' + nameOutput + '>'
    : '[' + nameOutput + ']';
}

function humanReadableArgName ( arg ) { var nameOutput = arg . name + ( arg . variadic === true ? '...' : '' ) ; return arg . required ? '<' + nameOutput + '>' : '[' + nameOutput + ']' ; }

Takes an argument an returns its human readable equivalent for help usage.

@param {Object} arg
@return {String}
@api private

Takes an argument an returns its human readable equivalent for help usage .


function validateObjectProperties(propertyValidators, allowUnknownProperties, ignoreInternalProperties) {
        var currentItemEntry = itemStack[itemStack.length - 1];
        var objectValue = currentItemEntry.itemValue;
        var oldObjectValue = currentItemEntry.oldItemValue;

        var supportedProperties = [ ];
        for (var propertyValidatorName in propertyValidators) {
          var validator = propertyValidators[propertyValidatorName];
          if (utils.isValueNullOrUndefined(validator) || utils.isValueNullOrUndefined(resolveItemConstraint(validator.type))) {
            // Skip over non-validator fields/properties
            continue;
          }

          var propertyValue = objectValue[propertyValidatorName];

          var oldPropertyValue;
          if (!utils.isValueNullOrUndefined(oldObjectValue)) {
            oldPropertyValue = oldObjectValue[propertyValidatorName];
          }

          supportedProperties.push(propertyValidatorName);

          itemStack.push({
            itemValue: propertyValue,
            oldItemValue: oldPropertyValue,
            itemName: propertyValidatorName
          });

          validateItemValue(validator);

          itemStack.pop();
        }

        // Verify there are no unsupported properties in the object
        if (!allowUnknownProperties) {
          for (var propertyName in objectValue) {
            if (ignoreInternalProperties && propertyName.indexOf('_') === 0) {
              // These properties are special cases that should always be allowed - generally only applied at the root
              // level of the document
              continue;
            }

            if (supportedProperties.indexOf(propertyName) < 0) {
              var objectPath = buildItemPath(itemStack);
              var fullPropertyPath = objectPath ? objectPath + '.' + propertyName : propertyName;
              validationErrors.push('property "' + fullPropertyPath + '" is not supported');
            }
          }
        }
      }

function
validateObjectProperties
(
propertyValidators
,
allowUnknownProperties
,
ignoreInternalProperties
)
{
var
currentItemEntry
=
itemStack
[
itemStack
.
length
1
]
;
var
objectValue
=
currentItemEntry
.
itemValue
;
var
oldObjectValue
=
currentItemEntry
.
oldItemValue
;
var
supportedProperties
=
[
]
;
for
(
var
propertyValidatorName
in
propertyValidators
)
{
var
validator
=
propertyValidators
[
propertyValidatorName
]
;
if
(
utils
.
isValueNullOrUndefined
(
validator
)
||
utils
.
isValueNullOrUndefined
(
resolveItemConstraint
(
validator
.
type
)
)
)
{
continue
;
}
var
propertyValue
=
objectValue
[
propertyValidatorName
]
;
var
oldPropertyValue
;
if
(
!
utils
.
isValueNullOrUndefined
(
oldObjectValue
)
)
{
oldPropertyValue
=
oldObjectValue
[
propertyValidatorName
]
;
}
supportedProperties
.
push
(
propertyValidatorName
)
;
itemStack
.
push
(
{
itemValue
:
propertyValue
,
oldItemValue
:
oldPropertyValue
,
itemName
:
propertyValidatorName
}
)
;
validateItemValue
(
validator
)
;
itemStack
.
pop
(
)
;
}
if
(
!
allowUnknownProperties
)
{
for
(
var
propertyName
in
objectValue
)
{
if
(
ignoreInternalProperties
&&
propertyName
.
indexOf
(
'_'
)
===
0
)
{
continue
;
}
if
(
supportedProperties
.
indexOf
(
propertyName
)
<
0
)
{
var
objectPath
=
buildItemPath
(
itemStack
)
;
var
fullPropertyPath
=
objectPath
objectPath
+
'.'
+
propertyName
:
propertyName
;
validationErrors
.
push
(
'property "'
+
fullPropertyPath
+
'" is not supported'
)
;
}
}
}
}

The following functions are nested within this function so they can share access to the doc, oldDoc and validationErrors params and the attachmentReferenceValidators and itemStack variables

The following functions are nested within this function so they can share access to the doc oldDoc and validationErrors params and the attachmentReferenceValidators and itemStack variables


function buildItemPath(itemStack) {
    var nameComponents = [ ];
    for (var i = 0; i < itemStack.length; i++) {
      var itemName = itemStack[i].itemName;

      if (!itemName) {
        // Skip null or empty names (e.g. the first element is typically the root of the document, which has no name)
        continue;
      } else if (nameComponents.length < 1 || itemName.indexOf('[') === 0) {
        nameComponents.push(itemName);
      } else {
        nameComponents.push('.' + itemName);
      }
    }

    return nameComponents.join('');
  }

function buildItemPath ( itemStack ) { var nameComponents = [ ] ; for ( var i = 0 ; i < itemStack . length ; i ++ ) { var itemName = itemStack [ i ] . itemName ; if ( ! itemName ) { continue ; } else if ( nameComponents . length < 1 || itemName . indexOf ( '[' ) === 0 ) { nameComponents . push ( itemName ) ; } else { nameComponents . push ( '.' + itemName ) ; } } return nameComponents . join ( '' ) ; }

Constructs the fully qualified path of the item at the top of the given stack


function getBusinessId(doc, oldDoc) {
    var regex = /^biz\.([A-Za-z0-9_-]+)(?:\..+)?$/;
    var matchGroups = regex.exec(doc._id);
    if (matchGroups) {
      return matchGroups[1];
    } else if (oldDoc && oldDoc.businessId) {
      // The document ID doesn't contain a business ID, so use the property from the old document
      return oldDoc.businessId || null;
    } else {
      // Neither the document ID nor the old document's contents contain a business ID, so use the property from the new document
      return doc.businessId || null;
    }
  }

function getBusinessId ( doc , oldDoc ) { var regex = / ^biz\.([A-Za-z0-9_-]+)(?:\..+)?$ / ; var matchGroups = regex . exec ( doc . _id ) ; if ( matchGroups ) { return matchGroups [ 1 ] ; } else if ( oldDoc && oldDoc . businessId ) { return oldDoc . businessId || null ; } else { return doc . businessId || null ; } }

Retrieves the ID of the business to which the document belongs


function toDefaultSyncChannels(doc, oldDoc, basePrivilegeName) {
    var businessId = getBusinessId(doc, oldDoc);

    return function(doc, oldDoc) {
      return {
        view: [ toSyncChannel(businessId, 'VIEW_' + basePrivilegeName) ],
        add: [ toSyncChannel(businessId, 'ADD_' + basePrivilegeName) ],
        replace: [ toSyncChannel(businessId, 'CHANGE_' + basePrivilegeName) ],
        remove: [ toSyncChannel(businessId, 'REMOVE_' + basePrivilegeName) ]
      };
    };
  }

function toDefaultSyncChannels ( doc , oldDoc , basePrivilegeName ) { var businessId = getBusinessId ( doc , oldDoc ) ; return function ( doc , oldDoc ) { return { view : [ toSyncChannel ( businessId , 'VIEW_' + basePrivilegeName ) ] , add : [ toSyncChannel ( businessId , 'ADD_' + basePrivilegeName ) ] , replace : [ toSyncChannel ( businessId , 'CHANGE_' + basePrivilegeName ) ] , remove : [ toSyncChannel ( businessId , 'REMOVE_' + basePrivilegeName ) ] } ; } ; }

Builds a function that returns the view, add, replace, remove channels extrapolated from the specified base privilege, name which is formatted according to the de facto Books convention of "VIEW_FOOBAR", "ADD_FOOBAR", "CHANGE_FOOBAR" and "REMOVE_FOOBAR" assuming the base privilege name is "FOOBAR"

Builds a function that returns the view add replace remove channels extrapolated from the specified base privilege name which is formatted according to the de facto Books convention of VIEW_FOOBAR ADD_FOOBAR CHANGE_FOOBAR and REMOVE_FOOBAR assuming the base privilege name is FOOBAR


function isIso8601DateTimeString(value) {
    var dateAndTimePieces = splitDateAndTime(value);
    var date = extractDateStructureFromDateAndTime(dateAndTimePieces);
    if (date) {
      var timeAndTimezone = extractTimeStructuresFromDateAndTime(dateAndTimePieces);
      var time = timeAndTimezone.time;
      var timezone = timeAndTimezone.timezone;

      return isValidDateStructure(date) &&
        isValidTimeStructure(time) &&
        (timezone === null || isValidTimeZoneStructure(timezone));
    } else {
      return false;
    }
  }

function isIso8601DateTimeString ( value ) { var dateAndTimePieces = splitDateAndTime ( value ) ; var date = extractDateStructureFromDateAndTime ( dateAndTimePieces ) ; if ( date ) { var timeAndTimezone = extractTimeStructuresFromDateAndTime ( dateAndTimePieces ) ; var time = timeAndTimezone . time ; var timezone = timeAndTimezone . timezone ; return isValidDateStructure ( date ) && isValidTimeStructure ( time ) && ( timezone === null || isValidTimeZoneStructure ( timezone ) ) ; } else { return false ; } }

Check that a given value is a valid ISO 8601 format date string with optional time and time zone components


function normalizeIso8601Time(time, timezoneOffsetMinutes) {
    var msPerSecond = 1000;
    var msPerMinute = 60000;
    var msPerHour = 3600000;
    var effectiveTimezoneOffset = timezoneOffsetMinutes || 0;

    var rawTimeMs =
      (time.hour * msPerHour) + (time.minute * msPerMinute) + (time.second * msPerSecond) + time.millisecond;

    return rawTimeMs - (effectiveTimezoneOffset * msPerMinute);
  }

function normalizeIso8601Time ( time , timezoneOffsetMinutes ) { var msPerSecond = 1000 ; var msPerMinute = 60000 ; var msPerHour = 3600000 ; var effectiveTimezoneOffset = timezoneOffsetMinutes || 0 ; var rawTimeMs = ( time . hour * msPerHour ) + ( time . minute * msPerMinute ) + ( time . second * msPerSecond ) + time . millisecond ; return rawTimeMs - ( effectiveTimezoneOffset * msPerMinute ) ; }

Converts the given time to the number of milliseconds since hour 0


function compareTimes(a, b) {
    if (typeof a !== 'string' || typeof b !== 'string') {
      return NaN;
    }

    return normalizeIso8601Time(parseIso8601Time(a)) - normalizeIso8601Time(parseIso8601Time(b));
  }

function compareTimes ( a , b ) { if ( typeof a !== 'string' || typeof b !== 'string' ) { return NaN ; } return normalizeIso8601Time ( parseIso8601Time ( a ) ) - normalizeIso8601Time ( parseIso8601Time ( b ) ) ; }

Compares the given time strings. Returns a negative number if a is less than b, a positive number if a is greater than b, or zero if a and b are equal.

Compares the given time strings . Returns a negative number if a is less than b a positive number if a is greater than b or zero if a and b are equal .


function compareDates(a, b) {
    var aPieces = extractDatePieces(a);
    var bPieces = extractDatePieces(b);

    if (aPieces === null || bPieces === null) {
      return NaN;
    }

    for (var pieceIndex = 0; pieceIndex < aPieces.length; pieceIndex++) {
      if (aPieces[pieceIndex] < bPieces[pieceIndex]) {
        return -1;
      } else if (aPieces[pieceIndex] > bPieces[pieceIndex]) {
        return 1;
      }
    }

    // If we got here, the two parameters represent the same date/point in time
    return 0;
  }

function compareDates ( a , b ) { var aPieces = extractDatePieces ( a ) ; var bPieces = extractDatePieces ( b ) ; if ( aPieces === null || bPieces === null ) { return NaN ; } for ( var pieceIndex = 0 ; pieceIndex < aPieces . length ; pieceIndex ++ ) { if ( aPieces [ pieceIndex ] < bPieces [ pieceIndex ] ) { return - 1 ; } else if ( aPieces [ pieceIndex ] > bPieces [ pieceIndex ] ) { return 1 ; } } return 0 ; }

Compares the given date representations. Returns a negative number if a is less than b, a positive number if a is greater than b, or zero if a and b are equal.

Compares the given date representations . Returns a negative number if a is less than b a positive number if a is greater than b or zero if a and b are equal .


function normalizeIso8601TimeZone(value) {
    return value ? value.multiplicationFactor * ((value.hour * 60) + value.minute) : -(new Date().getTimezoneOffset());
  }

function normalizeIso8601TimeZone ( value ) { return value ? value . multiplicationFactor * ( ( value . hour * 60 ) + value . minute ) : - ( new Date ( ) . getTimezoneOffset ( ) ) ; }

Converts an ISO 8601 time zone into the number of minutes offset from UTC


async function start() {
					log.i('--Nexus/Start');
					//build the setup promise array
					let startArray = [];

					for (let pid in Start) {
						startArray.push(new Promise((resolve, _reject) => {
							let com = {};
							com.Cmd = Start[pid];
							com.Passport = {};
							com.Passport.To = pid;
							com.Passport.Pid = genPid();
							sendMessage(com, resolve);
						}));
					}

					await Promise.all(startArray);
					log.v('--Nexus: All Starts Complete');
				}

async
function
start
(
)
{
log
.
i
(
'--Nexus/Start'
)
;
let
startArray
=
[
]
;
for
(
let
pid
in
Start
)
{
startArray
.
push
(
new
Promise
(
(
resolve
,
_reject
)
=>
{
let
com
=
{
}
;
com
.
Cmd
=
Start
[
pid
]
;
com
.
Passport
=
{
}
;
com
.
Passport
.
To
=
pid
;
com
.
Passport
.
Pid
=
genPid
(
)
;
sendMessage
(
com
,
resolve
)
;
}
)
)
;
}
await
Promise
.
all
(
startArray
)
;
log
.
v
(
'--Nexus: All Starts Complete'
)
;
}

Call Start on the required Module Apexes


async function exit(code = 0) {
				log.i('--Nexus/Stop');
				//build the Stop promise array
				let stopTasks = [];

				log.i('Nexus unloading node modules');
				log.v(Object.keys(require.cache).join('\n'));

				for (let pid in Stop) {
					stopTasks.push(new Promise((resolve, _reject) => {
						let com = {};
						com.Cmd = Stop[pid];
						com.Passport = {};
						com.Passport.To = pid;
						com.Passport.Pid = genPid();
						sendMessage(com, resolve);
					}));
				}
				await Promise.all(stopTasks);
				log.v('--Nexus: All Stops Complete');

				dispatchEvent('exit', { exitCode: code });
			}

async
function
exit
(
code
=
0
)
{
log
.
i
(
'--Nexus/Stop'
)
;
let
stopTasks
=
[
]
;
log
.
i
(
'Nexus unloading node modules'
)
;
log
.
v
(
Object
.
keys
(
require
.
cache
)
.
join
(
'\n'
)
)
;
\n
for
(
let
pid
in
Stop
)
{
stopTasks
.
push
(
new
Promise
(
(
resolve
,
_reject
)
=>
{
let
com
=
{
}
;
com
.
Cmd
=
Stop
[
pid
]
;
com
.
Passport
=
{
}
;
com
.
Passport
.
To
=
pid
;
com
.
Passport
.
Pid
=
genPid
(
)
;
sendMessage
(
com
,
resolve
)
;
}
)
)
;
}
await
Promise
.
all
(
stopTasks
)
;
log
.
v
(
'--Nexus: All Stops Complete'
)
;
}

Helper Functions as well as Entity definition
@description stop the system by calling all stop methods and
releasing all entities from memory
exit code 0 will gracefully exit,
@param {number} [code=0] the code to exit with


function sendMessage(com, fun = _ => _) {
				if (!('Passport' in com)) {
					log.w('Message has no Passport, ignored');
					log.w('    ' + JSON.stringify(com));
					fun('No Passport');
					return;
				}
				if (!('To' in com.Passport) || !com.Passport.To) {
					log.w('Message has no destination entity, ignored');
					log.w('    ' + JSON.stringify(com));
					fun('No recipient in message', com);
					return;
				}
				if (!('Pid' in com.Passport)) {
					log.w('Message has no message id, ignored');
					log.w('    ' + JSON.stringify(com));
					fun('No message id', com);
					return;
				}

				let pid = com.Passport.To;
				let apx = com.Passport.Apex || pid;
				if (pid in EntCache) {
					done(null, EntCache[pid]);
					return;
				} else {
					getEntityContext(pid, done);
				}

				async function done(err, entContextVolatile) {
					let entApex = await new Promise(res =>
						entContextVolatile.lock((val) => {
							res(val.Apex);
							return val;
						})
					);

					if (err) {
						log.w(err);
						log.w(JSON.stringify(com, null, 2));
						fun(err, com);
						return;
					}

					//TODO pid instanceOf ApexEntity
					if ((EntCache[pid].Apex == EntCache[pid].Pid) || (entApex == apx)) {
						let entContext = await new Promise(res => entContextVolatile.lock((context) => {
							res(context); return context;
						}));
						entContext.instance.dispatch(com, reply);
					} else {
						let err = 'Trying to send a message to a non-Apex'
							+ 'entity outside of the sending module';
						log.w(err);
						log.w(JSON.stringify(com, null, 2));
						fun(err, com);
					}
				}
				function reply(err, q) {
					// log.i('NEXUS MESSAGE:', com)
					fun(err, q);
				}
			}

Send a message from an entity to an Apex entity.
If a callback is provided, return when finished
@param {object} com 			the message object
@param {string} com.Cmd 		the command of the message
@param {object} com.Passport	the information about the message
@param {string} com.Passport.To the Pid of the recipient module
@param {string} com.Passport.Pid the ID of the message
@param {string=} com.Passport.From the Pid of the sending module
@callback fun 				the callback function to return to when finished

Send a message from an entity to an Apex entity . If a callback is provided return when finished


function deleteEntity(pid, fun = (err, _pid) => { if (err) log.e(err); }) {
				cacheInterface.deleteEntity(pid, (err, removedPidArray) => {

					//remove ent from EntCache (in RAM)
					for (let i = 0; i < removedPidArray.length; i++) {
						let entPid = removedPidArray[i];
						if (entPid in EntCache) {
							delete EntCache[entPid];
						}
					}
					log.v(`Removed ${(removedPidArray.length == 1) ? 'Entity' : 'Entities'
					} ${removedPidArray.join(' ')}`);
					fun(err, pid);
				});
			}

function
deleteEntity
(
pid
,
fun
=
(
err
,
_pid
)
=>
{
if
(
err
)
log
.
e
(
err
)
;
}
)
{
cacheInterface
.
deleteEntity
(
pid
,
(
err
,
removedPidArray
)
=>
{
for
(
let
i
=
0
;
i
<
removedPidArray
.
length
;
i
++
)
{
let
entPid
=
removedPidArray
[
i
]
;
if
(
entPid
in
EntCache
)
{
delete
EntCache
[
entPid
]
;
}
}
log
.
v
(
`
${
(
removedPidArray
.
length
==
1
)
'Entity'
:
'Entities'
}
${
removedPidArray
.
join
(
' '
)
}
`
)
;
fun
(
err
,
pid
)
;
}
)
;
}

Delete an entity from the module's memory.  If the entity is an Apex of a Module,
then delete all the entities found in that module as well.
@param {string} pid 		the pid of the entity
@callback fun  			the callback to return the pid of the generated entity to

Delete an entity from the module s memory . If the entity is an Apex of a Module then delete all the entities found in that module as well .


async function saveEntity(par, fun = (err, _pid) => { if (err) log.e(err); }) {
				let saveEntity = (async (par) => {
					await new Promise((res, rej) => {
						cacheInterface.saveEntityPar(par, (err, pid) => {
							if (err){
								log.e(err, 'saving ', pid);
								rej(err);
							}
							log.v(`Saved entity ${par.Pid}`);
							res();
						});
					});
				});

				//check if the entity is the modules Apex
				if (par.Pid != par.Apex) {
					//check if the Apex exists in the cache
					cacheInterface.getEntityPar(par.Apex, async (err) => {
						if (err) {

							//get the Apex's par from the EntCache
							let apexPar = await new Promise((res, _rej) => {
								EntCache[par.Apex].lock((entityContext) => {
									res(entityContext.Par);
									return entityContext;
								});
							});

							log.v('Must first save the Apex -- Saving...');
							await saveEntity(apexPar);
							await saveEntity(par);
							fun(null, par.Pid);
						} else {
							//this entity is not the apex and the apex is alread in the cache
							await saveEntity(par);
							fun(null, par.Pid);
						}
					});
				} else {
					await saveEntity(par);
					fun(null, par.Pid);
				}
			}

async
function
saveEntity
(
par
,
fun
=
(
err
,
_pid
)
=>
{
if
(
err
)
log
.
e
(
err
)
;
}
)
{
let
saveEntity
=
(
async
(
par
)
=>
{
await
new
Promise
(
(
res
,
rej
)
=>
{
cacheInterface
.
saveEntityPar
(
par
,
(
err
,
pid
)
=>
{
if
(
err
)
{
log
.
e
(
err
,
'saving '
,
pid
)
;
rej
(
err
)
;
}
log
.
v
(
`
${
par
.
Pid
}
`
)
;
res
(
)
;
}
)
;
}
)
;
}
)
;
if
(
par
.
Pid
!=
par
.
Apex
)
{
cacheInterface
.
getEntityPar
(
par
.
Apex
,
async
(
err
)
=>
{
if
(
err
)
{
let
apexPar
=
await
new
Promise
(
(
res
,
_rej
)
=>
{
EntCache
[
par
.
Apex
]
.
lock
(
(
entityContext
)
=>
{
res
(
entityContext
.
Par
)
;
return
entityContext
;
}
)
;
}
)
;
log
.
v
(
'Must first save the Apex -- Saving...'
)
;
await
saveEntity
(
apexPar
)
;
await
saveEntity
(
par
)
;
fun
(
,
par
.
Pid
)
;
}
else
{
await
saveEntity
(
par
)
;
fun
(
,
par
.
Pid
)
;
}
}
)
;
}
else
{
await
saveEntity
(
par
)
;
fun
(
,
par
.
Pid
)
;
}
}

Save an entity file. Make sure that all nested files exist in the
cache prior to saving said file
@param {object} par 		the par of the entity
@callback fun  			the callback to return the pid of the generated entity to

Save an entity file . Make sure that all nested files exist in the cache prior to saving said file


function getFile(module, filename, fun = _ => _) {
				let mod = ModCache[module];
				if (filename in mod.files) {
					mod.file(filename).async('string').then((dat) => {
						fun(null, dat);
					});
					return;
				}
				let err = `Error: File ${filename} does not exist in module ${module}`;
				log.e(err);
				fun(err);
			}

function getFile ( module , filename , fun = _ => _ ) { let mod = ModCache [ module ] ; if ( filename in mod . files ) { mod . file ( filename ) . async ( 'string' ) . then ( ( dat ) => { fun ( null , dat ) ; } ) ; return ; } let err = ` ${ filename } ${ module } ` ; log . e ( err ) ; fun ( err ) ; }

Access a file that exists in the module.json
@param {string} module 		the module to look for the file in
@param {string} filename 	the name of the file we're looking for
@callback fun				the callback to return the pid of the generated entity to

Access a file that exists in the module . json


async function getEntityContext(pid, fun = _ => _) {
				EntCache[pid] = new Volatile({});
				await EntCache[pid].lock((_entityContext) => {
					return new Promise((res, _rej) => {
						cacheInterface.getEntityPar(pid, (err, data) => {
							let par = JSON.parse(data.toString());
							if (err) {
								log.e(`Error retrieving a ${data.moduleType} from cache. Pid: ${pid}`);
								log.e(err);
								fun('Unavailable');
								return;
							}
							let impkey = par.Module + '/' + par.Entity;
							if (impkey in ImpCache) {
								BuildEnt();
								return;
							}

							GetModule(par.Module, async function (err, mod) {
								if (err) {
									log.e('Module <' + par.Module + '> not available');
									fun('Module not available');
									return;
								}

								if (!(par.Entity in mod.files)) {
									log.e('<' + par.Entity + '> not in module <' + par.Module + '>');
									fun('Null entity');
									return;
								}

								let entString = await new Promise(async (res, _rej) => {
									mod.file(par.Entity).async('string').then((string) => res(string));
								});

								log.v(`Spinning up entity ${par.Module}-${par.Entity.split('.')[0]}`);
								ImpCache[impkey] = indirectEvalImp(impkey, entString, log,
									createRequireFromModuleType(par.Module));
								BuildEnt();
							});

							function BuildEnt() {
								// TODO: rethink the whole process of having to call out a setup and start
								res(new Entity(Nxs, ImpCache[impkey], par, log));

							}
						});
					});
				});
				fun(null, EntCache[pid]);
			}

async
function
getEntityContext
(
pid
,
fun
=
_
=>
_
)
{
EntCache
[
pid
]
=
new
Volatile
(
{
}
)
;
await
EntCache
[
pid
]
.
lock
(
(
_entityContext
)
=>
{
return
new
Promise
(
(
res
,
_rej
)
=>
{
cacheInterface
.
getEntityPar
(
pid
,
(
err
,
data
)
=>
{
let
par
=
JSON
.
parse
(
data
.
toString
(
)
)
;
if
(
err
)
{
log
.
e
(
`
${
data
.
moduleType
}
${
pid
}
`
)
;
log
.
e
(
err
)
;
fun
(
'Unavailable'
)
;
return
;
}
let
impkey
=
par
.
Module
+
'/'
+
par
.
Entity
;
if
(
impkey
in
ImpCache
)
{
BuildEnt
(
)
;
return
;
}
GetModule
(
par
.
Module
,
async
function
(
err
,
mod
)
{
if
(
err
)
{
log
.
e
(
'Module <'
+
par
.
Module
+
'> not available'
)
;
fun
(
'Module not available'
)
;
return
;
}
if
(
!
(
par
.
Entity
in
mod
.
files
)
)
{
log
.
e
(
'<'
+
par
.
Entity
+
'> not in module <'
+
par
.
Module
+
'>'
)
;
fun
(
'Null entity'
)
;
return
;
}
let
entString
=
await
new
Promise
(
async
(
res
,
_rej
)
=>
{
mod
.
file
(
par
.
Entity
)
.
async
(
'string'
)
.
then
(
(
string
)
=>
res
(
string
)
)
;
}
)
;
log
.
v
(
`
${
par
.
Module
}
${
par
.
Entity
.
split
(
'.'
)
[
0
]
}
`
)
;
ImpCache
[
impkey
]
=
indirectEvalImp
(
impkey
,
entString
,
log
,
createRequireFromModuleType
(
par
.
Module
)
)
;
BuildEnt
(
)
;
}
)
;
function
BuildEnt
(
)
{
res
(
new
Entity
(
Nxs
,
ImpCache
[
impkey
]
,
par
,
log
)
)
;
}
}
)
;
}
)
;
}
)
;
fun
(
,
EntCache
[
pid
]
)
;
}

Spin up an entity from cache into memory and retrievd its context
otherwise just return it's context from memory
@param {string} apx 		the pid of the entities apex
@param {string} pid 		the pid of the entity
@callback fun  			the callback to return the pid of the generated entity to

Spin up an entity from cache into memory and retrievd its context otherwise just return it s context from memory


function GetModule(ModName, fun = _ => _) {
				ModName = ModName.replace(/:\//g, '.');
				if (ModName in ModCache) return fun(null, ModCache[ModName]);
				else cacheInterface.getModule(ModName, (err, moduleZip) => {
					if (err) return fun(err);
					ModCache[ModName] = moduleZip;
					return fun(null, ModCache[ModName]);
				});
			}

function GetModule ( ModName , fun = _ => _ ) { ModName = ModName . replace ( / :\/ / g , '.' ) ; if ( ModName in ModCache ) return fun ( null , ModCache [ ModName ] ) ; else cacheInterface . getModule ( ModName , ( err , moduleZip ) => { if ( err ) return fun ( err ) ; ModCache [ ModName ] = moduleZip ; return fun ( null , ModCache [ ModName ] ) ; } ) ; }

For retrieving modules
Modules come from the cache directory on the harddrive or the
ModCache if its already been read to RAM.
@param {Object} modRequest
@param {String} modRequest.Module
@param {String=} modRequest.Source
@param {Function} fun
@returns mod

For retrieving modules Modules come from the cache directory on the harddrive or the ModCache if its already been read to RAM .


function processSources(cfg) {

					if (typeof cfg['Sources'] === 'undefined') {
						log.e('You must defined a Sources object.\n');
						rejectSetup('You must defined a Sources object.');
						return;
					}
					let val, sources, subval;
					for (let key in cfg) {
						val = cfg[key];
						if (key == 'Sources') {
							Config.Sources = {};
							sources = cfg['Sources'];
							for (let subkey in sources) {
								subval = sources[subkey];
								switch (typeof subval) {
									case 'string': {
										Config.Sources[subkey] = Macro(subval);
										break;
									}
									case 'object': {
										Config.Sources[subkey] = {};
										for (let id in subval) {
											Config.Sources[subkey][id.toLowerCase()] =
												(typeof subval[id] == 'string') ?
													Macro(subval[id]) : subval[id];
										}
										if (!('port' in Config.Sources[subkey])) {
											Config.Sources[subkey]['port'] = 27000;
										}
										break;
									}
									default: {
										log.e(`Invalid Source ${subkey} of type ${typeof subval}.` +
											'Must be of type string or object');
									}
								}
							}
						} else {
							Config[key] = val;
						}
					}
				}

function
processSources
(
cfg
)
{
if
(
typeof
cfg
[
'Sources'
]
===
'undefined'
)
{
log
.
e
(
'You must defined a Sources object.\n'
)
;
\n
rejectSetup
(
'You must defined a Sources object.'
)
;
}
return
;
let
val
,
sources
,
subval
;
}

Reads in the given config and fills in the Sources Macros


function generateModuleCatalog() {
					// Create new cache and install high level
					// module subdirectories. Each of these also
					// has a link to the source of that module (Module.json).
					let keys = Object.keys(Config.Modules);
					for (let i = 0; i < keys.length; i++) {
						let key = keys[i];
						if (key == 'Deferred') {
							let arr = Config.Modules['Deferred'];
							for (let idx = 0; idx < arr.length; idx++) {
								let mod = arr[idx];
								log.v(`Deferring ${mod.Module || mod}`);
								if (typeof mod == 'string') {
									log.w('Adding Module names directly to Deferred is deprecated');
									log.w(`Deferring { Module: '${mod}' } instead`);
									mod = { Module: mod };
								}
								if (!('Module' in mod)) {
									log.e('Malformed Deferred Module listing', mod);
									rejectSetup('Malformed Deferred Module listing');
									return;
								}
								logModule(key, mod);
							}
						} else {
							if (typeof Config.Modules[key].Module != 'string') {
								log.e('Malformed Module Definition');
								log.e(JSON.stringify(Config.Modules[key], null, 2));
							}
							logModule(key, Config.Modules[key]);
						}
					}

					/**
					 * Add the module to the Modules object if unique
					 * @param {object} mod 		The module object
					 * @param {string} mod.Module	The name of the module
					 * @param {object, string} mod.Source The Module broker or path reference
					 */
					function logModule(key, mod) {
						let folder = mod.Module.replace(/[/:]/g, '.');

						if (!('Source' in mod)) {
							log.e(`No Source Declared in module: ${key}: ${mod.Module}`);
							rejectSetup(`No Source Declared in module: ${key}`);
							return;
						}

						let source = {
							Source: mod.Source,
							Version: mod.Version
						};

						if (!(folder in Modules)) {
							Modules[folder] = source;
						} else {
							if (Modules[folder].Source != source.Source
								|| (Modules[folder].Version != source.Version)) {
								log.e(`Broker Mismatch Exception: ${key}\n`
									+ `${JSON.stringify(Modules[folder], null, 2)} - `
									+ `\n${JSON.stringify(source, null, 2)}`);
								rejectSetup('Broker Mismatch Exception');
								return;
							}
						}
					}
				}

Create a list of all required modules and their brokers


function logModule(key, mod) {
						let folder = mod.Module.replace(/[/:]/g, '.');

						if (!('Source' in mod)) {
							log.e(`No Source Declared in module: ${key}: ${mod.Module}`);
							rejectSetup(`No Source Declared in module: ${key}`);
							return;
						}

						let source = {
							Source: mod.Source,
							Version: mod.Version
						};

						if (!(folder in Modules)) {
							Modules[folder] = source;
						} else {
							if (Modules[folder].Source != source.Source
								|| (Modules[folder].Version != source.Version)) {
								log.e(`Broker Mismatch Exception: ${key}\n`
									+ `${JSON.stringify(Modules[folder], null, 2)} - `
									+ `\n${JSON.stringify(source, null, 2)}`);
								rejectSetup('Broker Mismatch Exception');
								return;
							}
						}
					}

function
logModule
(
key
,
mod
)
{
let
folder
=
mod
.
Module
.
replace
(
/
[/:]
/
g
,
'.'
)
;
if
(
!
(
'Source'
in
mod
)
)
{
log
.
e
(
`
${
key
}
${
mod
.
Module
}
`
)
;
rejectSetup
(
`
${
key
}
`
)
;
return
;
}
let
source
=
{
Source
:
mod
.
Source
,
Version
:
mod
.
Version
}
;
if
(
!
(
folder
in
Modules
)
)
{
Modules
[
folder
]
=
source
;
}
else
{
if
(
Modules
[
folder
]
.
Source
!=
source
.
Source
||
(
Modules
[
folder
]
.
Version
!=
source
.
Version
)
)
{
log
.
e
(
`
${
key
}
\n
`
+
`
${
JSON
.
stringify
(
Modules
[
folder
]
,
,
2
)
}
`
+
`
\n
${
JSON
.
stringify
(
source
,
,
2
)
}
`
)
;
rejectSetup
(
'Broker Mismatch Exception'
)
;
return
;
}
}
}

Add the module to the Modules object if unique
@param {object} mod 		The module object
@param {string} mod.Module	The name of the module
@param {object, string} mod.Source The Module broker or path reference


async function buildApexInstances(processPidReferences) {
					if (processPidReferences) {
						// Assign pids to all instance in Config.Modules
						for (let instname in Config.Modules) {
							if (instname == 'Deferred')
								continue;
							Apex[instname] = genPid();
						}
						log.v('Apex List', JSON.stringify(Apex, null, 2));
					}

					// Now populate all of the modules from config.json
					for (let instname in Config.Modules) {
						if (instname === 'Deferred')
							continue;
						await processApexPar(Apex[instname], Config.Modules[instname], processPidReferences);
					}
				}

async
function
buildApexInstances
(
processPidReferences
)
{
if
(
processPidReferences
)
{
for
(
let
instname
in
Config
.
Modules
)
{
if
(
instname
==
'Deferred'
)
continue
;
Apex
[
instname
]
=
genPid
(
)
;
}
log
.
v
(
'Apex List'
,
JSON
.
stringify
(
Apex
,
,
2
)
)
;
}
for
(
let
instname
in
Config
.
Modules
)
{
if
(
instname
===
'Deferred'
)
continue
;
await
processApexPar
(
Apex
[
instname
]
,
Config
.
Modules
[
instname
]
,
processPidReferences
)
;
}
}

Write the modules and all instances to the cache


async function buildDir(path) {
					let dirObj = {};
					if (fs.existsSync(path)) {
						let files = fs.readdirSync(path);
						let itemPromises = [];
						for (let file of files) {
							itemPromises.push(new Promise(async (resolve) => {
								let curPath = path + '/' + file;
								if (fs.lstatSync(curPath).isDirectory()) {
									// recurse
									dirObj[file] = await buildDir(curPath);
									resolve();
								} else {
									fs.readFile(curPath, function (err, data) {
										// log.v(curPath.length > 80 ? curPath.substr(0, 35)
										// + ' ... ' + curPath.substr(-40, 40) : curPath);
										dirObj[file] = data.toString();
										resolve();
									});
									// dirObj[file] = fs.readFileSync(curPath).toString(encoding);
								}
							}));
						}
						await Promise.all(itemPromises);
						return dirObj;
					}
				}

async function buildDir ( path ) { let dirObj = { } ; if ( fs . existsSync ( path ) ) { let files = fs . readdirSync ( path ) ; let itemPromises = [ ] ; for ( let file of files ) { itemPromises . push ( new Promise ( async ( resolve ) => { let curPath = path + '/' + file ; if ( fs . lstatSync ( curPath ) . isDirectory ( ) ) { dirObj [ file ] = await buildDir ( curPath ) ; resolve ( ) ; } else { fs . readFile ( curPath , function ( err , data ) { dirObj [ file ] = data . toString ( ) ; resolve ( ) ; } ) ; } } ) ) ; } await Promise . all ( itemPromises ) ; return dirObj ; } }

Build an object to represent a directory
@param {String} path


function genPid() {
					if (!Uuid) {
						// module.paths = [Path.join(Path.resolve(CacheDir), 'node_modules')];
						Uuid = require('uuid/v4');
					}
					let str = Uuid();
					let pid = str.replace(/-/g, '').toUpperCase();
					return pid;
				}

function genPid ( ) { if ( ! Uuid ) { Uuid = require ( 'uuid/v4' ) ; } let str = Uuid ( ) ; let pid = str . replace ( / - / g , '' ) . toUpperCase ( ) ; return pid ; }


async function genesis(system) {
			log.i(' [Save Cache]'.padStart(80, '='));
			log.i('Genesis Compile Start:');

			let cacheState = null;
			if (fs.existsSync(CacheDir)) cacheState = 'exists';

			cacheInterface = new CacheInterface({
				path: CacheDir, log
			});

			cleanCache();

			log.i('Saving modules and updating dependencies ...');
			await cacheModules(system.ModCache);

			if (!(__options.state == 'updateOnly')) {
				log.i('Saving entities ...');
				await cacheApexes(system.Apex, system.Config.Modules);
			}

			Stop();

			/////////////////////////////////////////////////////////////
			//
			//	Only helper functions beyond this point of this scope
			//

			/**
			*  Remove the cache if it currently exists in the given directory
			*/
			function cleanCache() {
				// Remove the provided cache directory
				if (__options.state == 'development' && cacheState) {
					__options.state = 'updateOnly';
					return;
				}
				log.v('Removing the old cache.');
				cacheInterface.clean();
			}

			/**
			 * Write the modules to the cache
			 * @param {Object} ModCache 	//the set of module zips required for this system
			 */
			async function cacheModules(ModCache) {
				let timer = log.time('cacheModules');
				let ModulePromiseArray = [];
				for (let folder in ModCache) {
					ModulePromiseArray.push(new Promise(async (res) => {
						await cacheInterface.addModule(folder, ModCache[folder]);
						log.v(`Finished installing dependencies for ${folder}`);
						res();
					}));

				}
				await Promise.all(ModulePromiseArray);

				log.timeEnd(timer);
			}

			/**
			 * Write the module Apexes to the cache
			 * @param {Object} Apexes 						//The id:Pid of each apex
			 * @param {Object} ModuleDefinitions 	//the id:ModuleDefinition from Config
			 */
			async function cacheApexes(Apexes, ModuleDefinitions) {
				let ModulePromiseArray = [];
				for (let moduleId in Apexes) {
					ModulePromiseArray.push(
						await cacheInterface.createInstance(ModuleDefinitions[moduleId], Apexes[moduleId])
					);
				}
				await Promise.all(ModulePromiseArray);
			}

			/**
			 * Resolves the main promise created during genesis call
			 */
			async function Stop() {
				log.i(`Genesis Compile Stop: ${new Date().toString()}`);
				log.i(' [Finished]'.padStart(80, '='));
				for(const xgrl in BrokerCache) {
					const broker = BrokerCache[xgrl];
					broker.cleanup();
				}
				log.timeEnd(compileTimer);
				resolveMain();
			}
		}


async function cacheModules(ModCache) {
				let timer = log.time('cacheModules');
				let ModulePromiseArray = [];
				for (let folder in ModCache) {
					ModulePromiseArray.push(new Promise(async (res) => {
						await cacheInterface.addModule(folder, ModCache[folder]);
						log.v(`Finished installing dependencies for ${folder}`);
						res();
					}));

				}
				await Promise.all(ModulePromiseArray);

				log.timeEnd(timer);
			}

async function cacheModules ( ModCache ) { let timer = log . time ( 'cacheModules' ) ; let ModulePromiseArray = [ ] ; for ( let folder in ModCache ) { ModulePromiseArray . push ( new Promise ( async ( res ) => { await cacheInterface . addModule ( folder , ModCache [ folder ] ) ; log . v ( ` ${ folder } ` ) ; res ( ) ; } ) ) ; } await Promise . all ( ModulePromiseArray ) ; log . timeEnd ( timer ) ; }

Write the modules to the cache
@param {Object} ModCache 	//the set of module zips required for this system


async function cacheApexes(Apexes, ModuleDefinitions) {
				let ModulePromiseArray = [];
				for (let moduleId in Apexes) {
					ModulePromiseArray.push(
						await cacheInterface.createInstance(ModuleDefinitions[moduleId], Apexes[moduleId])
					);
				}
				await Promise.all(ModulePromiseArray);
			}

async function cacheApexes ( Apexes , ModuleDefinitions ) { let ModulePromiseArray = [ ] ; for ( let moduleId in Apexes ) { ModulePromiseArray . push ( await cacheInterface . createInstance ( ModuleDefinitions [ moduleId ] , Apexes [ moduleId ] ) ) ; } await Promise . all ( ModulePromiseArray ) ; }

Write the module Apexes to the cache
@param {Object} Apexes 						//The id:Pid of each apex
@param {Object} ModuleDefinitions 	//the id:ModuleDefinition from Config


async function Stop() {
				log.i(`Genesis Compile Stop: ${new Date().toString()}`);
				log.i(' [Finished]'.padStart(80, '='));
				for(const xgrl in BrokerCache) {
					const broker = BrokerCache[xgrl];
					broker.cleanup();
				}
				log.timeEnd(compileTimer);
				resolveMain();
			}

async
function
Stop
(
)
{
log
.
i
(
`
${
new
Date
(
)
.
toString
(
)
}
`
)
;
log
.
i
(
' [Finished]'
.
padStart
(
80
,
'='
)
)
;
for
(
const
xgrl
in
BrokerCache
)
{
const
broker
=
BrokerCache
[
xgrl
]
;
broker
.
cleanup
(
)
;
}
log
.
timeEnd
(
compileTimer
)
;
resolveMain
(
)
;
}

Resolves the main promise created during genesis call


function getProtocolModule(protocol) {
	return new Promise(function (resolve, reject) {
		let cacheFilepath = path.join(appdata, protocol);
		if (fs.existsSync(cacheFilepath)) {
			return resolve(JSON.parse(fs.readFileSync(cacheFilepath).toString()));
		}

		let options = {
			host: 'protocols.xgraphdev.com',
			port: 443,
			path: '/' + protocol,
			method: 'GET',
			rejectUnauthorized: false,
		};

		let req = https.request(options, function (res) {
			res.setEncoding('utf8');
			let response = '';
			res.on('data', function (chunk) {
				response += chunk;
			});
			res.on('end', _ => {
				try {
					resolve(JSON.parse(response));
					try { fs.writeFileSync(cacheFilepath, response); } catch (e) {
						reject({
							code: 1,
							text: `fail to save protocol at ${cacheFilepath}` +
								'\n delete file and try again'
						});
					}
				} catch (e) {
					reject({ code: 0, text: 'try and retrieve locally' });
				}
			});
		});

		req.on('error', function (e) {
			log.e('problem with request: ' + e.message);
			reject({ code: 1, text: 'problem with request: ' + e.message });
		});

		// write data to request body
		req.end();
	});
}

function
getProtocolModule
(
protocol
)
{
return
new
Promise
(
function
(
resolve
,
reject
)
{
let
cacheFilepath
=
path
.
join
(
appdata
,
protocol
)
;
if
(
fs
.
existsSync
(
cacheFilepath
)
)
{
return
resolve
(
JSON
.
parse
(
fs
.
readFileSync
(
cacheFilepath
)
.
toString
(
)
)
)
;
}
let
options
=
{
host
:
'protocols.xgraphdev.com'
,
port
:
443
,
path
:
'/'
+
protocol
,
method
:
'GET'
,
rejectUnauthorized
:
false
,
}
;
let
req
=
https
.
request
(
options
,
function
(
res
)
{
res
.
setEncoding
(
'utf8'
)
;
let
response
=
''
;
res
.
on
(
'data'
,
function
(
chunk
)
{
response
+=
chunk
;
}
)
;
res
.
on
(
'end'
,
_
=>
{
try
{
resolve
(
JSON
.
parse
(
response
)
)
;
try
{
fs
.
writeFileSync
(
cacheFilepath
,
response
)
;
}
catch
(
e
)
{
reject
(
{
code
:
1
,
text
:
`
${
cacheFilepath
}
`
+
'\n delete file and try again'
}
)
;
}
}
\n
}
)
;
}
)
;
catch
(
e
)
{
reject
(
{
code
:
0
,
text
:
'try and retrieve locally'
}
)
;
}
req
.
on
(
'error'
,
function
(
e
)
{
log
.
e
(
'problem with request: '
+
e
.
message
)
;
reject
(
{
code
:
1
,
text
:
'problem with request: '
+
e
.
message
}
)
;
}
)
;
}
)
;
}


function remDir(path) {
			return (new Promise(async (resolve, _reject) => {
				if (fs.existsSync(path)) {
					let files = fs.readdirSync(path);
					let promiseArray = [];

					for (let fileIndex = 0; fileIndex < files.length; fileIndex++) {
						promiseArray.push(new Promise(async (resolve2, _reject2) => {
							let curPath = path + '/' + files[fileIndex];
							if (fs.lstatSync(curPath).isDirectory()) {
								// recurse
								await remDir(curPath);
								resolve2();
							} else {
								// delete file
								log.v('Removing Entity ', files[fileIndex].split('.')[0]);
								fs.unlinkSync(curPath);
								resolve2();
							}
						}));
					}
					//make sure all the sub files and directories have been removed;
					await Promise.all(promiseArray);
					log.v('Removing Module Directory ', path);
					fs.rmdirSync(path);
					resolve();
				} else {
					log.v('trying to remove nonexistant path ', path);
					resolve();
				}
			}));
		}

function
remDir
(
path
)
{
return
(
new
Promise
(
async
(
resolve
,
_reject
)
=>
{
if
(
fs
.
existsSync
(
path
)
)
{
let
files
=
fs
.
readdirSync
(
path
)
;
let
promiseArray
=
[
]
;
for
(
let
fileIndex
=
0
;
fileIndex
<
files
.
length
;
fileIndex
++
)
{
promiseArray
.
push
(
new
Promise
(
async
(
resolve2
,
_reject2
)
=>
{
let
curPath
=
path
+
'/'
+
files
[
fileIndex
]
;
if
(
fs
.
lstatSync
(
curPath
)
.
isDirectory
(
)
)
{
await
remDir
(
curPath
)
;
resolve2
(
)
;
}
else
{
log
.
v
(
'Removing Entity '
,
files
[
fileIndex
]
.
split
(
'.'
)
[
0
]
)
;
fs
.
unlinkSync
(
curPath
)
;
resolve2
(
)
;
}
}
)
)
;
}
await
Promise
.
all
(
promiseArray
)
;
log
.
v
(
'Removing Module Directory '
,
path
)
;
fs
.
rmdirSync
(
path
)
;
resolve
(
)
;
}
else
{
log
.
v
(
'trying to remove nonexistant path '
,
path
)
;
resolve
(
)
;
}
}
)
)
;
}

Recursive directory deletion
@param {string} path the directory to be recursively removed


function getMousePosition(e) {
                var mouseObj = void 0,
                    originalEvent = e.originalEvent ? e.originalEvent : e;
                mouseObj = 'changedTouches' in originalEvent && originalEvent.changedTouches ? originalEvent.changedTouches[0] : originalEvent;
                // clientX, Y 쓰면 스크롤에서 문제 발생
                return {
                    clientX: mouseObj.pageX,
                    clientY: mouseObj.pageY
                };
            }

function getMousePosition ( e ) { var mouseObj = void 0 , originalEvent = e . originalEvent ? e . originalEvent : e ; mouseObj = 'changedTouches' in originalEvent && originalEvent . changedTouches ? originalEvent . changedTouches [ 0 ] : originalEvent ; return { clientX : mouseObj . pageX , clientY : mouseObj . pageY } ; }

get mouse position
@param e
@returns {{clientX, clientY}}


function proxyRequest(req, res, rule) {
  var router,
      target,
      path;

  injectProxyHeaders(req, rule);

  // rewrite the base path of the requested URL
  path = req.url.replace(rule.regexp, rule.target.path);

  if (useGateway) {
    // for HTTP LAN proxies, rewrite the request URL from a relative URL to an absolute URL
    // also add a header that can be inspected by the unit tests
    req.url = url.parse(util.format('%s//%s:%s%s', rule.target.protocol, rule.target.host, rule.target.port, path)).href;
    req.headers['X-Forwarded-Url'] = req.url;

    // the proxy target is really the HTTP LAN proxy
    target = config.gateway;
    logger.info('proxy: %s %s --> %s:%s --> %s//%s:%s%s', req.method, req.url, config.gateway.host, config.gateway.port, rule.target.protocol, rule.target.host, rule.target.port, path);
  } else {
    target = rule.target;
    logger.info('proxy: %s %s --> %s//%s:%s%s', req.method, req.url, rule.target.protocol, rule.target.host, rule.target.port, path);
    req.url = path;
  }

  var errorCallback = function errorCallback(err, proxyRequest, proxyResponse) {
    var status = 500;
    if (proxyResponse !== undefined && proxyResponse !== null && proxyResponse.statusCode >= 400) {
      status = proxyResponse.statusCode;
    }

    logger.error('proxy: error - %s %s - %s', proxyRequest.method, proxyRequest.url, err.message);
    if( res.status && typeof res.status === 'function'  ){
        res.status(status).json({ error: status, message: err.message });
    }
  };

  // get a ProxyServer from the cache
  router = createRouter(target);

  // proxy the request
  router.web(req, res, errorCallback);
}

function
proxyRequest
(
req
,
res
,
rule
)
{
var
router
,
target
,
path
;
injectProxyHeaders
(
req
,
rule
)
;
path
=
req
.
url
.
replace
(
rule
.
regexp
,
rule
.
target
.
path
)
;
if
(
useGateway
)
{
req
.
url
=
url
.
parse
(
util
.
format
(
'%s//%s:%s%s'
,
rule
.
target
.
protocol
,
rule
.
target
.
host
,
rule
.
target
.
port
,
path
)
)
.
href
;
req
.
headers
[
'X-Forwarded-Url'
]
=
req
.
url
;
target
=
config
.
gateway
;
logger
.
info
(
'proxy: %s %s
,
req
.
method
,
req
.
url
,
config
.
gateway
.
host
,
config
.
gateway
.
port
,
rule
.
target
.
protocol
,
rule
.
target
.
host
,
rule
.
target
.
port
,
path
)
;
}
else
{
target
=
rule
.
target
;
logger
.
info
(
'proxy: %s %s
,
req
.
method
,
req
.
url
,
rule
.
target
.
protocol
,
rule
.
target
.
host
,
rule
.
target
.
port
,
path
)
;
req
.
url
=
path
;
}
var
errorCallback
=
function
errorCallback
(
err
,
proxyRequest
,
proxyResponse
)
{
var
status
=
500
;
if
(
proxyResponse
!==
undefined
&&
proxyResponse
!==
&&
proxyResponse
.
statusCode
>=
400
)
{
status
=
proxyResponse
.
statusCode
;
}
logger
.
error
(
'proxy: error - %s %s - %s'
,
proxyRequest
.
method
,
proxyRequest
.
url
,
err
.
message
)
;
if
(
res
.
status
&&
typeof
res
.
status
===
'function'
)
{
res
.
status
(
status
)
.
json
(
{
error
:
status
,
message
:
err
.
message
}
)
;
}
}
;
router
=
createRouter
(
target
)
;
router
.
web
(
req
,
res
,
errorCallback
)
;
}

injects any LAN proxy servers into the request


function injectProxyHeaders(req, rule){
  // the HTTP host header is often needed by the target webserver config
  req.headers['host'] = rule.target.host + (rule.target.originalPort ? util.format(':%d', rule.target.originalPort) : '');
  // document that this request was proxied
  req.headers['via'] =  util.format('http://%s:%s', req.connection.address().address, req.connection.address().port);

  // inject any custom headers as configured
  config.headers.forEach(function(header) {
    var value = header.value,
        name = header.name;

    if(typeof(value) === 'function') {
      value = value.call(undefined, req);
    }

    if(typeof(value) !== 'string') {
      value = '';
    }

    if (typeof(name) === 'string') {
      req.headers[name.toLowerCase()] = value;
    }
  });

  injectAuthHeader(req);
}

function injectProxyHeaders ( req , rule ) { req . headers [ 'host' ] = rule . target . host + ( rule . target . originalPort ? util . format ( ':%d' , rule . target . originalPort ) : '' ) ; req . headers [ 'via' ] = util . format ( 'http://%s:%s' , req . connection . address ( ) . address , req . connection . address ( ) . port ) ; config . headers . forEach ( function ( header ) { var value = header . value , name = header . name ; if ( typeof ( value ) === 'function' ) { value = value . call ( undefined , req ) ; } if ( typeof ( value ) !== 'string' ) { value = '' ; } if ( typeof ( name ) === 'string' ) { req . headers [ name . toLowerCase ( ) ] = value ; } } ) ; injectAuthHeader ( req ) ; }

inject any custom header values into a proxy request along with the x-forwarded-for, x-forwarded-port, and via headers

inject any custom header values into a proxy request along with the x - forwarded - for x - forwarded - port and via headers


function parseFile(filepath, config) {
  var contents;

  filepath = filepath || path.join(process.cwd(), '/json-proxy.json');

  // if we were passed a config file, read and parse it
  if (fs.existsSync(filepath)) {
    try {
      var data = fs.readFileSync(filepath);
      contents = JSON.parse(data.toString());
      config = parseConfig(contents, config);

      // replace the token $config_dir in the webroot arg
      if (config.server.webroot && config.server.webroot.length > 0) {
        config.server.webroot = config.server.webroot.replace("$config_dir", path.dirname(filepath));
      }
    } catch (ex) {
      throw new Error('Cannot parse the config file "' + filepath + '": ' + ex);
    }

  }

  return config;
}

function
parseFile
(
filepath
,
config
)
{
var
contents
;
filepath
=
filepath
||
path
.
join
(
process
.
cwd
(
)
,
'/json-proxy.json'
)
;
if
(
fs
.
existsSync
(
filepath
)
)
{
try
{
var
data
=
fs
.
readFileSync
(
filepath
)
;
contents
=
JSON
.
parse
(
data
.
toString
(
)
)
;
config
=
parseConfig
(
contents
,
config
)
;
if
(
config
.
server
.
webroot
&&
config
.
server
.
webroot
.
length
>
0
)
{
config
.
server
.
webroot
=
config
.
server
.
webroot
.
replace
(
"$config_dir"
,
path
.
dirname
(
filepath
)
)
;
}
}
catch
(
ex
)
{
throw
new
Error
(
'Cannot parse the config file "'
+
filepath
+
'": '
+
ex
)
;
}
}
return
config
;
}

reads a config file from either the config file specified on the command line, or fallback to a file name json-proxy.config in the working directory return true if the file can be read, otherwise return false

reads a config file from either the config file specified on the command line or fallback to a file name json - proxy . config in the working directory return true if the file can be read otherwise return false


function parseConfig(contents, config) {
  contents.server = contents.server || {};
  contents.proxy = contents.proxy || {};

  if (contents.proxy.gateway && typeof(contents.proxy.gateway) === "string" && contents.proxy.gateway.length > 0) {
    contents.proxy.gateway = parseGateway(contents.proxy.gateway);
  }

  contents.proxy.forward = parseConfigMap(contents.proxy.forward, parseForwardRule);
  contents.proxy.headers = parseConfigMap(contents.proxy.headers, parseHeaderRule);

  // override any values in the config object with values specified in the file;
  config.server.port = contents.server.port || config.server.port;
  config.server.webroot = contents.server.webroot || config.server.webroot;
  config.server.html5mode = contents.server.html5mode || config.server.html5mode;
  config.proxy.gateway = contents.proxy.gateway || config.proxy.gateway;
  config.proxy.forward = contents.proxy.forward || config.proxy.forward;
  config.proxy.headers = contents.proxy.headers || config.proxy.headers;

  return config;
}

function parseConfig ( contents , config ) { contents . server = contents . server || { } ; contents . proxy = contents . proxy || { } ; if ( contents . proxy . gateway && typeof ( contents . proxy . gateway ) === "string" && contents . proxy . gateway . length > 0 ) { contents . proxy . gateway = parseGateway ( contents . proxy . gateway ) ; } contents . proxy . forward = parseConfigMap ( contents . proxy . forward , parseForwardRule ) ; contents . proxy . headers = parseConfigMap ( contents . proxy . headers , parseHeaderRule ) ; config . server . port = contents . server . port || config . server . port ; config . server . webroot = contents . server . webroot || config . server . webroot ; config . server . html5mode = contents . server . html5mode || config . server . html5mode ; config . proxy . gateway = contents . proxy . gateway || config . proxy . gateway ; config . proxy . forward = contents . proxy . forward || config . proxy . forward ; config . proxy . headers = contents . proxy . headers || config . proxy . headers ; return config ; }

parse a config structure, overriding any values in config

parse a config structure overriding any values in config


function parseConfigMap(map, callback) {
  var result = [];

  if (!(map instanceof Object)) {
    return map;
  }

  for(var property in map) {
    if (map.hasOwnProperty(property)) {
      result.push(callback(property, map[property]));
    }
  }

  return result;
}

function parseConfigMap ( map , callback ) { var result = [ ] ; if ( ! ( map instanceof Object ) ) { return map ; } for ( var property in map ) { if ( map . hasOwnProperty ( property ) ) { result . push ( callback ( property , map [ property ] ) ) ; } } return result ; }

transform a config hash object into an array


function parseCommandLine(argv, config) {
  if (argv) {
    // read the command line arguments if no config file was given
    parseCommandLineArgument(argv.port, function(item){
      config.server.port = item;
    });

    parseCommandLineArgument(argv.html5mode, function(item){
      config.server.html5mode = item;
    });

    parseCommandLineArgument(argv._, function(item){
      config.server.webroot = path.normalize(item);
    });

    parseCommandLineArgument(argv.gateway, function(item){
      config.proxy.gateway = parseGateway(item);
    });

    parseCommandLineArgument(argv.forward, function(item){
      var rule = parseForwardRule(item);
      var match = false;

      config.proxy.forward.forEach(function(item) {
        if (item.regexp.source === rule.regexp.source) {
          item.target = rule.target;
          match = true;
        }
      });

      if (!match) {
        config.proxy.forward.push(rule);
      }
    });

    parseCommandLineArgument(argv.header, function(item){
      var rule = parseHeaderRule(item);
      var match = false;

      config.proxy.headers.forEach(function(item) {
        if (item.name === rule.name) {
          item.value = rule.value;
          match = true;
        }
      });

      if (!match) {
        config.proxy.headers.push(rule);
      }
    });
  }

  return config;
}

function parseCommandLine ( argv , config ) { if ( argv ) { parseCommandLineArgument ( argv . port , function ( item ) { config . server . port = item ; } ) ; parseCommandLineArgument ( argv . html5mode , function ( item ) { config . server . html5mode = item ; } ) ; parseCommandLineArgument ( argv . _ , function ( item ) { config . server . webroot = path . normalize ( item ) ; } ) ; parseCommandLineArgument ( argv . gateway , function ( item ) { config . proxy . gateway = parseGateway ( item ) ; } ) ; parseCommandLineArgument ( argv . forward , function ( item ) { var rule = parseForwardRule ( item ) ; var match = false ; config . proxy . forward . forEach ( function ( item ) { if ( item . regexp . source === rule . regexp . source ) { item . target = rule . target ; match = true ; } } ) ; if ( ! match ) { config . proxy . forward . push ( rule ) ; } } ) ; parseCommandLineArgument ( argv . header , function ( item ) { var rule = parseHeaderRule ( item ) ; var match = false ; config . proxy . headers . forEach ( function ( item ) { if ( item . name === rule . name ) { item . value = rule . value ; match = true ; } } ) ; if ( ! match ) { config . proxy . headers . push ( rule ) ; } } ) ; } return config ; }


function parseCommandLineArgument(arg, fn) {
  if (typeof(fn) !== 'function')
    return;

  if (Array.isArray(arg)) {
    arg.forEach(function(item) {
      fn.call(null, item);
    });
  } else {
    if (arg !== null && arg !== undefined) {
      fn.call(null, arg);
    }
  }
}

function parseCommandLineArgument ( arg , fn ) { if ( typeof ( fn ) !== 'function' ) return ; if ( Array . isArray ( arg ) ) { arg . forEach ( function ( item ) { fn . call ( null , item ) ; } ) ; } else { if ( arg !== null && arg !== undefined ) { fn . call ( null , arg ) ; } } }

argv.X will be an array if multiple -X options are provided otherwise argv.X will just be a scalar value

argv . X will be an array if multiple - X options are provided otherwise argv . X will just be a scalar value


function parseForwardRule() {
  var token,
      rule;

  if (arguments[0] === undefined || arguments[0] === null) {
    return;
  }

  if (typeof(arguments[0]) === "object") {
    return arguments[0];
  }

  try {
    token = tokenize.apply(null, arguments);
    rule = { regexp: new RegExp('^' + token.name, 'i'), target: parseTargetServer(token.value) };
  } catch (e) {
    throw new Error('cannot parse the forwarding rule ' + arguments[0] + ' - ' + e);
  }

  return rule;
}

function
parseForwardRule
(
)
{
var
token
,
rule
;
if
(
arguments
[
0
]
===
undefined
||
arguments
[
0
]
===
)
{
return
;
}
if
(
typeof
(
arguments
[
0
]
)
===
"object"
)
{
return
arguments
[
0
]
;
}
try
{
token
=
tokenize
.
apply
(
,
arguments
)
;
rule
=
{
regexp
:
new
RegExp
(
'^'
+
token
.
name
,
'i'
)
,
target
:
parseTargetServer
(
token
.
value
)
}
;
}
catch
(
e
)
{
throw
new
Error
(
'cannot parse the forwarding rule '
+
arguments
[
0
]
+
' - '
+
e
)
;
}
return
rule
;
}

parses rule syntax to create forwarding rules


function withCode(code, msg) {
    const err = new Error(msg);
    err.code = code;
    return err;
}

function withCode ( code , msg ) { const err = new Error ( msg ) ; err . code = code ; return err ; }


function updateWorkingState(repoState, branch, newWorkingState) {
    let workingStates = repoState.getWorkingStates();

    const key = branch.getFullName();
    if (newWorkingState === null) {
        // Delete
        workingStates = workingStates.delete(key);
    } else {
        // Update the entry in the map
        workingStates = workingStates.set(key, newWorkingState);
    }

    return repoState.set('workingStates', workingStates);
}

function updateWorkingState ( repoState , branch , newWorkingState ) { let workingStates = repoState . getWorkingStates ( ) ; const key = branch . getFullName ( ) ; if ( newWorkingState === null ) { workingStates = workingStates . delete ( key ) ; } else { workingStates = workingStates . set ( key , newWorkingState ) ; } return repoState . set ( 'workingStates' , workingStates ) ; }

Change workinState for a specific branch
@param {RepositoryState} repoState
@param {Branch} branch
@param {WorkingState | Null} newWorkingState Pass null to delete
@return {RepositoryState}


function fetchBranches(repoState, driver) {
    const oldBranches = repoState.getBranches();
    return driver.fetchBranches()
    .then((branches) => {
        return repoState.set('branches', branches);
    })
    .then(function refreshWorkingStates(repoState) {
        // Remove outdated WorkingStates
        return oldBranches.reduce((repoState, oldBranch) => {
            const fullName = oldBranch.getFullName();
            const newBranch = repoState.getBranch(fullName);
            if (newBranch === null || newBranch.getSha() !== oldBranch.getSha()) {
                // Was removed OR updated
                return updateWorkingState(repoState, oldBranch, null);
            } else {
                // Unchanged
                return repoState;
            }
        }, repoState);
    });
}

function fetchBranches ( repoState , driver ) { const oldBranches = repoState . getBranches ( ) ; return driver . fetchBranches ( ) . then ( ( branches ) => { return repoState . set ( 'branches' , branches ) ; } ) . then ( function refreshWorkingStates ( repoState ) { return oldBranches . reduce ( ( repoState , oldBranch ) => { const fullName = oldBranch . getFullName ( ) ; const newBranch = repoState . getBranch ( fullName ) ; if ( newBranch === null || newBranch . getSha ( ) !== oldBranch . getSha ( ) ) { return updateWorkingState ( repoState , oldBranch , null ) ; } else { return repoState ; } } , repoState ) ; } ) ; }

Fetch the list of branches in the repository and update them all. Will clear the
WorkingStates of all branches that have updated.
@param {RepositoryState} repoState
@param {Driver} driver
@return {Promise<RepositoryState>} with list of branches fetched

Fetch the list of branches in the repository and update them all . Will clear the WorkingStates of all branches that have updated .


function initialize(driver) {
    const repoState = RepositoryState.createEmpty();
    return fetchBranches(repoState, driver)
    .then((repoState) => {
        const branches = repoState.getBranches();
        const master = branches.find(function isMaster(branch) {
            return branch.getFullName() === 'master';
        });
        const branch = master || branches.first();

        return fetchTree(repoState, driver, branch)
        .then((repoState) => {
            return checkout(repoState, branch);
        });
    });
}

function initialize ( driver ) { const repoState = RepositoryState . createEmpty ( ) ; return fetchBranches ( repoState , driver ) . then ( ( repoState ) => { const branches = repoState . getBranches ( ) ; const master = branches . find ( function isMaster ( branch ) { return branch . getFullName ( ) === 'master' ; } ) ; const branch = master || branches . first ( ) ; return fetchTree ( repoState , driver , branch ) . then ( ( repoState ) => { return checkout ( repoState , branch ) ; } ) ; } ) ; }

Initialize a new RepositoryState from the repo of a Driver. Fetch
the branches, and checkout master or the first available branch.
@param {Driver} driver
@return {Promise<RepositoryState>}

Initialize a new RepositoryState from the repo of a Driver . Fetch the branches and checkout master or the first available branch .


function enforceArrayBuffer(b, encoding) {
    if (isArrayBuffer(b)) return b;
    else if (isBuffer(b)) return fromBuffer(b);
    else return fromString(b, encoding);
}

function enforceArrayBuffer ( b , encoding ) { if ( isArrayBuffer ( b ) ) return b ; else if ( isBuffer ( b ) ) return fromBuffer ( b ) ; else return fromString ( b , encoding ) ; }


function enforceString(b, encoding) {
    if (is.string(b)) return b;
    if (isArrayBuffer(b)) b = toBuffer(b);

    return b.toString(encoding);
}

function enforceString ( b , encoding ) { if ( is . string ( b ) ) return b ; if ( isArrayBuffer ( b ) ) b = toBuffer ( b ) ; return b . toString ( encoding ) ; }

Force conversion to string with specific encoding


function equals(buf1, buf2) {
    if (buf1.byteLength != buf2.byteLength) return false;
    const dv1 = new Int8Array(buf1);
    const dv2 = new Int8Array(buf2);
    for (let i = 0 ; i != buf1.byteLength ; i++) {
        if (dv1[i] != dv2[i]) return false;
    }
    return true;
}

function equals ( buf1 , buf2 ) { if ( buf1 . byteLength != buf2 . byteLength ) return false ; const dv1 = new Int8Array ( buf1 ) ; const dv2 = new Int8Array ( buf2 ) ; for ( let i = 0 ; i != buf1 . byteLength ; i ++ ) { if ( dv1 [ i ] != dv2 [ i ] ) return false ; } return true ; }

Tests equality of two ArrayBuffer
@param {ArrayBuffer} buf1
@param {ArrayBuffer} buf2
@return {Boolean}


function getMergedFileSet(workingState) {
    return Immutable.Set.fromKeys(
        getMergedTreeEntries(workingState).filter(
            treeEntry => treeEntry.getType() === TreeEntry.TYPES.BLOB
        )
    );
}

function getMergedFileSet ( workingState ) { return Immutable . Set . fromKeys ( getMergedTreeEntries ( workingState ) . filter ( treeEntry => treeEntry . getType ( ) === TreeEntry . TYPES . BLOB ) ) ; }

Returns a Seq of tree mixing changes and the fetched tree
@param {WorkingState}
@return {Set<Path>}


function getMergedTreeEntries(workingState) {
    const removedOrModified = workingState.getChanges().groupBy((change, path) => {
        if (change.getType() === CHANGES.REMOVE) {
            return 'remove';
        } else {
            // Must be UDPATE or CREATE
            return 'modified';
        }
    });

    const setToRemove = Immutable.Set.fromKeys(removedOrModified.get('remove', []));

    const withoutRemoved = workingState.getTreeEntries().filter((treeEntry, path) => {
        return !setToRemove.contains(path);
    });

    const addedTreeEntries = removedOrModified.get('modified', []).map(
        function toTreeEntry(change) {
            return new TreeEntry({
                sha: change.hasSha() ? change.getSha() : null,
                mode: '100644'
            });
        }
    );

    return withoutRemoved.concat(addedTreeEntries);
}

function getMergedTreeEntries ( workingState ) { const removedOrModified = workingState . getChanges ( ) . groupBy ( ( change , path ) => { if ( change . getType ( ) === CHANGES . REMOVE ) { return 'remove' ; } else { return 'modified' ; } } ) ; const setToRemove = Immutable . Set . fromKeys ( removedOrModified . get ( 'remove' , [ ] ) ) ; const withoutRemoved = workingState . getTreeEntries ( ) . filter ( ( treeEntry , path ) => { return ! setToRemove . contains ( path ) ; } ) ; const addedTreeEntries = removedOrModified . get ( 'modified' , [ ] ) . map ( function toTreeEntry ( change ) { return new TreeEntry ( { sha : change . hasSha ( ) ? change . getSha ( ) : null , mode : '100644' } ) ; } ) ; return withoutRemoved . concat ( addedTreeEntries ) ; }

Returns a Map of TreeEntry, with sha null when the content is not available as sha.
@param {WorkingState}
@return {Map<TreeEntries>}

Returns a Map of TreeEntry with sha null when the content is not available as sha .


function findSha(workingState, filepath) {
    // Lookup potential changes
    const change = workingState.getChanges().get(filepath);
    // Else lookup tree
    const treeEntry = workingState.getTreeEntries().get(filepath);

    if (change) {
        if (change.getType() == CHANGES.REMOVE) {
            throw error.fileNotFound(filepath);
        } else {
            return change.getSha();
        }
    } else if (treeEntry) {
        return treeEntry.getSha();
    } else {
        throw error.fileNotFound(filepath);
    }
}

function findSha ( workingState , filepath ) { const change = workingState . getChanges ( ) . get ( filepath ) ; const treeEntry = workingState . getTreeEntries ( ) . get ( filepath ) ; if ( change ) { if ( change . getType ( ) == CHANGES . REMOVE ) { throw error . fileNotFound ( filepath ) ; } else { return change . getSha ( ) ; } } else if ( treeEntry ) { return treeEntry . getSha ( ) ; } else { throw error . fileNotFound ( filepath ) ; } }

Attempts to find a SHA if available for the  given file
@param {Path}
@return {Sha | Null} null if Sha cannot be retrieved (because of pending change)
@throws NOT_FOUND if the file does not exist or was removed


function prepare(repoState, opts) {
    const workingState = repoState.getCurrentState();
    const changes = workingState.getChanges();

    // Is this an empty commit ?
    opts.empty = workingState.isClean();

    // Parent SHA
    opts.parents = new Immutable.List([
        workingState.getHead()
    ]);

    // Get merged tree (with applied changes)
    opts.treeEntries = WorkingUtils.getMergedTreeEntries(workingState);

    // Create map of blobs that needs to be created
    opts.blobs = changes.filter((change) => {
        return !change.hasSha();
    }).map((change) => {
        return change.getContent();
    });

    return CommitBuilder.create(opts);
}

function prepare ( repoState , opts ) { const workingState = repoState . getCurrentState ( ) ; const changes = workingState . getChanges ( ) ; opts . empty = workingState . isClean ( ) ; opts . parents = new Immutable . List ( [ workingState . getHead ( ) ] ) ; opts . treeEntries = WorkingUtils . getMergedTreeEntries ( workingState ) ; opts . blobs = changes . filter ( ( change ) => { return ! change . hasSha ( ) ; } ) . map ( ( change ) => { return change . getContent ( ) ; } ) ; return CommitBuilder . create ( opts ) ; }

Create a commit builder from the changes on current branch
@param {RepositoryState}
@param {Author} opts.author
@param {String} [opts.message]
@return {CommitBuilder}


function flush(repoState, driver, commitBuilder, options = {}) {
    options = Object.assign({
        branch: repoState.getCurrentBranch(),
        ignoreEmpty: true
    }, options);

    if (options.ignoreEmpty
        && commitBuilder.isEmpty()
        && commitBuilder.getParents().count() < 2) {
        return Q(repoState);
    }

    // Create new commit
    return driver.flushCommit(commitBuilder)
    // Forward the branch
    .then((commit) => {
        return driver.forwardBranch(options.branch, commit.getSha())
        // Fetch new workingState and replace old one
        .then(function updateBranch() {
            const updated = options.branch.merge({ commit });
            return repoState.updateBranch(options.branch, updated);

        }, function nonFF(err) {
            if (err.code === ERRORS.NOT_FAST_FORWARD) {
                // Provide the created commit to allow merging it back.
                err.commit = commit;
            }
            throw err;
        });
    })
    .then(function updateWorkingState(forwardedRepoState) {
        const forwardedBranch = forwardedRepoState.getBranch(options.branch.getFullName());
        return RepoUtils.fetchTree(forwardedRepoState, driver, forwardedBranch);
    });
}

function flush ( repoState , driver , commitBuilder , options = { } ) { options = Object . assign ( { branch : repoState . getCurrentBranch ( ) , ignoreEmpty : true } , options ) ; if ( options . ignoreEmpty && commitBuilder . isEmpty ( ) && commitBuilder . getParents ( ) . count ( ) < 2 ) { return Q ( repoState ) ; } return driver . flushCommit ( commitBuilder ) . then ( ( commit ) => { return driver . forwardBranch ( options . branch , commit . getSha ( ) ) . then ( function updateBranch ( ) { const updated = options . branch . merge ( { commit } ) ; return repoState . updateBranch ( options . branch , updated ) ; } , function nonFF ( err ) { if ( err . code === ERRORS . NOT_FAST_FORWARD ) { err . commit = commit ; } throw err ; } ) ; } ) . then ( function updateWorkingState ( forwardedRepoState ) { const forwardedBranch = forwardedRepoState . getBranch ( options . branch . getFullName ( ) ) ; return RepoUtils . fetchTree ( forwardedRepoState , driver , forwardedBranch ) ; } ) ; }

Flush a commit from the current branch using a driver
Then update the reference, and pull new workingState
@param {RepositoryState} repoState
@param {Driver} driver
@param {CommitBuilder} commitBuilder
@param {Branch} [options.branch] Optional branch to use instead of
current branch
@param {Boolean} [options.ignoreEmpty=true] Empty commits are
ignored, unless they merge several branches.
@return {Promise<RepositoryState, ERRORS.NOT_FAST_FORWARD>} If the
branch cannot be fast forwarded to the created commit, fails with
NOT_FAST_FORWARD. The error will contains the created Commit.

Flush a commit from the current branch using a driver Then update the reference and pull new workingState


function format(color, messages) {
  var length = messages.length;

  if (length === 0 || typeof(color) !== 'string') {
    return;
  }

  return (util.format.apply(null, messages)[color]);
}

function format ( color , messages ) { var length = messages . length ; if ( length === 0 || typeof ( color ) !== 'string' ) { return ; } return ( util . format . apply ( null , messages ) [ color ] ) ; }

colorize strings and send to console.log

colorize strings and send to console . log


function push(repoState, driver, opts = {}) {
    opts = Object.assign({
        branch: repoState.getCurrentBranch(),
        force: false,
        remote: {
            name: 'origin'
        }
    }, opts);

    return driver.push(opts) // Can fail with NOT_FAST_FORWARD
    // TODO update remote branch in repoState list of branches
    .thenResolve(repoState);
}

function push ( repoState , driver , opts = { } ) { opts = Object . assign ( { branch : repoState . getCurrentBranch ( ) , force : false , remote : { name : 'origin' } } , opts ) ; return driver . push ( opts ) . thenResolve ( repoState ) ; }

Push a local branch to a remote repository
@param {RepositoryState} repoState
@param {Driver} driver
@param {Branch} [opts.branch] Branch to push. Default to current
@param {String} [opts.remote.name=origin] Name of the remote
@param {String} [opts.remote.url] URL if the remote needs to be created
@param {Boolean} [opts.force=false] Ignore non fast forward
@param {String} [opts.auth.username] Authentication username
@param {String} [opts.auth.password] Authentication password
@return {Promise<RepositoryState>}
@throws {Promise<ERROR.NOT_FAST_FORWARD>}
@throws {Promise<ERROR.AUTHENTICATION_FAILED>}
@throws {Promise<ERROR.UNKNOWN_REMOTE>}


function pull(repoState, driver, opts = {}) {
    opts = Object.assign({
        branch: repoState.getCurrentBranch(),
        force: false,
        remote: {
            name: 'origin'
        }
    }, opts);

    return driver.pull(opts)
    // Update branch SHA
    .then(() => {
        return driver.fetchBranches();
    })
    .then((branches) => {
        const updatedBranch = branches.find((br) => {
            return br.name === opts.branch.name;
        });
        repoState = repoState.updateBranch(opts.branch, updatedBranch);

        return RepoUtils.fetchTree(repoState, driver, updatedBranch);
    });
}

function pull ( repoState , driver , opts = { } ) { opts = Object . assign ( { branch : repoState . getCurrentBranch ( ) , force : false , remote : { name : 'origin' } } , opts ) ; return driver . pull ( opts ) . then ( ( ) => { return driver . fetchBranches ( ) ; } ) . then ( ( branches ) => { const updatedBranch = branches . find ( ( br ) => { return br . name === opts . branch . name ; } ) ; repoState = repoState . updateBranch ( opts . branch , updatedBranch ) ; return RepoUtils . fetchTree ( repoState , driver , updatedBranch ) ; } ) ; }

Pulls changes for local branch, from remote repository. Loses any
pending changes on it.
@param {RepositoryState} repoState
@param {Driver} driver
@param {Branch} [opts.branch] Branch to pull. Default to current
@param {String} [opts.remote.name=origin] Name of the remote
@param {String} [opts.remote.url] URL if the remote needs to be created
@param {Boolean} [opts.force=false] Ignore non fast forward
@param {String} [opts.auth.username] Authentication username
@param {String} [opts.auth.password] Authentication password
@return {Promise<RepositoryState>}
@throws {Promise<ERROR.NOT_FAST_FORWARD>}
@throws {Promise<ERROR.AUTHENTICATION_FAILED>}
@throws {Promise<ERROR.UNKNOWN_REMOTE>}

Pulls changes for local branch from remote repository . Loses any pending changes on it .


function compareRefs(driver, base, head) {
    const baseRef = base instanceof Branch ? base.getFullName() : base;
    const headRef = head instanceof Branch ? head.getFullName() : head;

    return driver.findParentCommit(baseRef, headRef)
    .then((parentCommit) => {
        // There can be no parent commit
        return Q.all([
            parentCommit ? parentCommit.getSha() : null,
            baseRef,
            headRef
        ].map((ref) => {
            return ref ? driver.fetchWorkingState(ref) : WorkingState.createEmpty();
        }));
    })
    .spread((parent, base, head) => {
        const conflicts = _compareTrees(parent.getTreeEntries(),
                                     base.getTreeEntries(),
                                     head.getTreeEntries());

        return new TreeConflict({
            base,
            head,
            parent,
            conflicts
        });
    });
}

function compareRefs ( driver , base , head ) { const baseRef = base instanceof Branch ? base . getFullName ( ) : base ; const headRef = head instanceof Branch ? head . getFullName ( ) : head ; return driver . findParentCommit ( baseRef , headRef ) . then ( ( parentCommit ) => { return Q . all ( [ parentCommit ? parentCommit . getSha ( ) : null , baseRef , headRef ] . map ( ( ref ) => { return ref ? driver . fetchWorkingState ( ref ) : WorkingState . createEmpty ( ) ; } ) ) ; } ) . spread ( ( parent , base , head ) => { const conflicts = _compareTrees ( parent . getTreeEntries ( ) , base . getTreeEntries ( ) , head . getTreeEntries ( ) ) ; return new TreeConflict ( { base , head , parent , conflicts } ) ; } ) ; }

Computes a TreeConflict between to tree references. Fetches the
trees from the repo. The list of conflicts is the minimal set of
conflicts.
@param {Driver} driver
@param {Branch | String} base A branch, branch name, or SHA
@param {Branch | String} head A branch, branch name, or SHA
@return {Promise<TreeConflict>}

Computes a TreeConflict between to tree references . Fetches the trees from the repo . The list of conflicts is the minimal set of conflicts .


function solveTree(treeConflict, solved) {
    solved = treeConflict.getConflicts()
    .merge(solved)
    // Solve unresolved conflicts
    .map(function defaultSolve(conflict) {
        if (!conflict.isSolved()) {
            return conflict.keepBase();
        } else {
            return conflict;
        }
    });
    return treeConflict.set('conflicts', solved);
}

function solveTree ( treeConflict , solved ) { solved = treeConflict . getConflicts ( ) . merge ( solved ) . map ( function defaultSolve ( conflict ) { if ( ! conflict . isSolved ( ) ) { return conflict . keepBase ( ) ; } else { return conflict ; } } ) ; return treeConflict . set ( 'conflicts' , solved ) ; }

Merge solved Conflicts back into a TreeConflict. Unsolved conflicts
default to keep base.
@param {TreeConflict} treeConflict
@param {Map<Path, Conflict>} solved
@return {TreeConflict}

Merge solved Conflicts back into a TreeConflict . Unsolved conflicts default to keep base .


function mergeCommit(treeConflict, parents, options) {
    options = options || {};

    const opts = {};

    // Assume the commit is not empty
    opts.empty = false;

    // Parent SHAs
    opts.parents = new Immutable.List(parents);

    opts.author = options.author;
    opts.message = options.message || 'Merged commit';

    // Get the solved tree entries
    const solvedEntries = _getSolvedEntries(treeConflict);
    opts.treeEntries = solvedEntries;

    // Create map of blobs that needs to be created
    const solvedConflicts = treeConflict.getConflicts();
    opts.blobs = solvedEntries.filter((treeEntry) => {
        return !treeEntry.hasSha();
    }).map((treeEntry, path) => {
        return solvedConflicts.get(path).getSolvedContent();
    });

    return CommitBuilder.create(opts);
}

function
mergeCommit
(
treeConflict
,
parents
,
options
)
{
options
=
options
||
{
}
;
const
opts
=
{
}
;
opts
.
empty
=
false
;
opts
.
parents
=
new
Immutable
.
List
(
parents
)
;
opts
.
author
=
options
.
author
;
opts
.
message
=
options
.
message
||
'Merged commit'
;
const
solvedEntries
=
_getSolvedEntries
(
treeConflict
)
;
opts
.
treeEntries
=
solvedEntries
;
const
solvedConflicts
=
treeConflict
.
getConflicts
(
)
;
opts
.
blobs
=
solvedEntries
.
filter
(
(
treeEntry
)
=>
{
return
!
treeEntry
.
hasSha
(
)
;
}
)
.
map
(
(
treeEntry
,
path
)
=>
{
return
solvedConflicts
.
get
(
path
)
.
getSolvedContent
(
)
;
}
)
;
return
CommitBuilder
.
create
(
opts
)
;
}

Create a merge commit builder
@param {TreeConflict} treeConflict The solved TreeConflict
@param {Array<SHA>} parents Parent commits
@param {Author} options.author
@param {String} [options.message='Merge commit']
@return {CommitBuilder}


function _getSolvedEntries(treeConflict) {
    const parentEntries = treeConflict.getParent().getTreeEntries();
    const baseEntries = treeConflict.getBase().getTreeEntries();
    const headEntries = treeConflict.getHead().getTreeEntries();

    const baseDiff = _diffEntries(parentEntries, baseEntries);
    const headDiff = _diffEntries(parentEntries, headEntries);

    const resolvedEntries = treeConflict.getConflicts().map((solvedConflict) => {
        // Convert to TreeEntries (or null for deletion)
        if (solvedConflict.isDeleted()) {
            return null;
        } else {
            return new TreeEntry({
                sha: solvedConflict.getSolvedSha() || null
            });
        }
    });

    return parentEntries.merge(baseDiff, headDiff, resolvedEntries)
    // Remove deleted entries
    .filter(function nonNull(entry) {
        return entry !== null;
    });
}

function _getSolvedEntries ( treeConflict ) { const parentEntries = treeConflict . getParent ( ) . getTreeEntries ( ) ; const baseEntries = treeConflict . getBase ( ) . getTreeEntries ( ) ; const headEntries = treeConflict . getHead ( ) . getTreeEntries ( ) ; const baseDiff = _diffEntries ( parentEntries , baseEntries ) ; const headDiff = _diffEntries ( parentEntries , headEntries ) ; const resolvedEntries = treeConflict . getConflicts ( ) . map ( ( solvedConflict ) => { if ( solvedConflict . isDeleted ( ) ) { return null ; } else { return new TreeEntry ( { sha : solvedConflict . getSolvedSha ( ) || null } ) ; } } ) ; return parentEntries . merge ( baseDiff , headDiff , resolvedEntries ) . filter ( function nonNull ( entry ) { return entry !== null ; } ) ; }

Returns the final TreeEntries for a solved TreeConflict.
@param {TreeConflict} treeConflict
@return {Map<Path, TreeEntry>} Some TreeEntries have a null SHA
because of new solved content.

Returns the final TreeEntries for a solved TreeConflict .


function addBlob(cache, sha, blob) {
    const blobs = cache.getBlobs();
    const newBlobs = blobs.set(sha, blob);

    const newCache = cache.set('blobs', newBlobs);
    return newCache;
}

function addBlob ( cache , sha , blob ) { const blobs = cache . getBlobs ( ) ; const newBlobs = blobs . set ( sha , blob ) ; const newCache = cache . set ( 'blobs' , newBlobs ) ; return newCache ; }

Add a new blob to a cache instance
@param {Cache} cache
@param {String} sha Used as key
@param {Blob} blob
@return {Cache}


function get(repoState, dirPath) {
    // Remove trailing '/' etc.
    const normDirPath = Path.join(dirPath, '.');
    const filepaths = DirUtils.readFilenamesRecursive(repoState, normDirPath);

    const tree = {
        value: File.createDir(normDirPath),
        children: {}
    };

    for (let i = 0; i < filepaths.length; i++) {
        const relativePath = Path.relative(normDirPath, filepaths[i]);
        const parts = relativePath.split('/');
        let node = tree;
        let prefix = normDirPath;
        for (let j = 0; j < parts.length; j++) {
            const head = parts[j];
            const isLeaf = (j === parts.length - 1);
            prefix = Path.join(prefix, head);

            // Create node if doesn't exist
            if (!node.children[head]) {
                if (isLeaf) {
                    node.children[head] = {
                        value: FileUtils.stat(repoState, filepaths[i])
                    };
                } else {
                    node.children[head] = {
                        value: File.createDir(prefix),
                        children: {}
                    };
                }
            }
            node = node.children[head];
        }
    }

    return TreeNode.fromJS(tree);
}

function get ( repoState , dirPath ) { const normDirPath = Path . join ( dirPath , '.' ) ; const filepaths = DirUtils . readFilenamesRecursive ( repoState , normDirPath ) ; const tree = { value : File . createDir ( normDirPath ) , children : { } } ; for ( let i = 0 ; i < filepaths . length ; i ++ ) { const relativePath = Path . relative ( normDirPath , filepaths [ i ] ) ; const parts = relativePath . split ( '/' ) ; let node = tree ; let prefix = normDirPath ; for ( let j = 0 ; j < parts . length ; j ++ ) { const head = parts [ j ] ; const isLeaf = ( j === parts . length - 1 ) ; prefix = Path . join ( prefix , head ) ; if ( ! node . children [ head ] ) { if ( isLeaf ) { node . children [ head ] = { value : FileUtils . stat ( repoState , filepaths [ i ] ) } ; } else { node . children [ head ] = { value : File . createDir ( prefix ) , children : { } } ; } } node = node . children [ head ] ; } } return TreeNode . fromJS ( tree ) ; }

Generate a files tree from the current branch, taking pending changes into account.
@param {RepositoryState} repoState
@param {Path} [dir='.'] The directory to get the subtree from, default to root
@return {TreeNode<File>} The directory TreeNode with all its children

Generate a files tree from the current branch taking pending changes into account .


function normCreatedCommit(ghCommit) {
    const commit = Commit.create({
        sha: ghCommit.sha,
        message: ghCommit.message,
        author: getSimpleAuthor(ghCommit.author),
        date: ghCommit.author.date,
        parents: ghCommit.parents.map(function getSha(o) { return o.sha; })
    });

    return commit;
}

function normCreatedCommit ( ghCommit ) { const commit = Commit . create ( { sha : ghCommit . sha , message : ghCommit . message , author : getSimpleAuthor ( ghCommit . author ) , date : ghCommit . author . date , parents : ghCommit . parents . map ( function getSha ( o ) { return o . sha ; } ) } ) ; return commit ; }

Normalize a commit coming from the GitHub commit creation API
@param {JSON} ghCommit
@return {Commit}


function normListedCommit(ghCommit) {
    const commit = Commit.create({
        sha: ghCommit.sha,
        message: ghCommit.commit.message,
        author: getCompleteAuthor(ghCommit),
        date: ghCommit.commit.author.date,
        files: ghCommit.files,
        parents: ghCommit.parents.map(c => c.sha)
    });

    return commit;
}

function normListedCommit ( ghCommit ) { const commit = Commit . create ( { sha : ghCommit . sha , message : ghCommit . commit . message , author : getCompleteAuthor ( ghCommit ) , date : ghCommit . commit . author . date , files : ghCommit . files , parents : ghCommit . parents . map ( c => c . sha ) } ) ; return commit ; }

Normalize a commit coming from the GitHub commit listing API
@param {JSON} ghCommit
@return {Commit}