docs: function(resources) {
      appsettings = (resources.settings) ? resources.settings : resources;
      resources = resources;
  
      initial_install = (typeof resources.initial_install !== 'undefined' || typeof resources.initial_install === 'boolean') ? resources.initial_install : initial_install;
  
  
      // console.log('resources.core',resources.core);
      if (resources && resources.logger) {
          logger = resources.logger;
      } else {
          logger.silly = console.log;
          logger.warn = console.warn;
          logger.error = console.error;
          logger.debug = console.info;
          logger.info = console.info;
      }
      if (resources.core) {
          CoreUtilities = resources.core.utilities;
      } else {
          console.log('need to fix extension', appsettings, resources);
          var Utilities = require('periodicjs.core.utilities');
          CoreUtilities = new Utilities({
              settings: resources
          });
      }
      var extensionsConfig = {},
          extensionsFiles = [];
  
      /** 
       * gets the configuration information
       * @return { string } file path for config file
       */
      this.settings = function() {
          return extensionsConfig;
      };
  
      this.testConfig = function(options, callback) {
          try {
              var periodicext = fs.readJsonSync(path.join(process.cwd(), 'package.json'));
              var arrayObjectIndexOf = function(myArray, searchTerm, property) {
                  for (var i = 0, len = myArray.length; i < len; i++) {
                      if (myArray[i][property] === searchTerm) {
                          return i; }
                  }
                  return -1;
              };
              extensionsConfig.extensions.forEach(function(ext, i) {
                  if (Object.keys(ext).length < 6) {
                      throw new Error('Extension[' + i + '] (' + ext.name + ') is missing one of the standard properties [name,version,periodicCompatibility,installed,enabled,date,periodicConfig]');
                  } else if (semver.gt(ext.periodicCompatibility, periodicext.version)) {
                      if (ext.enabled) {
                          throw new Error('Extension[' + i + '] (' + ext.name + ') is not capatible with this version(' + periodicext.version + ') of Periodic');
                      } else if (resources && resources.logger) {
                          resources.logger.warn('Extension[' + i + '] (' + ext.name + ') is not capatible with this version(' + periodicext.version + ') of Periodic');
                      }
                  } else if (ext.periodicConfig.periodicDependencies) {
                      let depPositionIndex;
                      ext.periodicConfig.periodicDependencies.forEach(function(extDep) {
                          depPositionIndex = arrayObjectIndexOf(extensionsConfig.extensions, extDep.extname, 'name');
                          if (depPositionIndex === -1 && (typeof extDep.optional === 'undefined' || (typeof extDep === 'boolean' && extDep.optional === false))) {
                              throw new Error('Extension[' + i + '] (' + ext.name + ') is missing dependency (' + extDep.extname + ')');
                          } else if (depPositionIndex >= i && (typeof extDep.optional === 'undefined' || (typeof extDep === 'boolean' && extDep.optional === false))) {
                              throw new Error('Extension[' + i + '] (' + ext.name + ') is being loaded before dependency (' + extDep.extname + ')[' + depPositionIndex + ']');
                          } else if (depPositionIndex < i && (typeof extDep.optional === 'undefined' || (typeof extDep === 'boolean' && extDep.optional !== true)) && semver.ltr(extensionsConfig.extensions[depPositionIndex].version, extDep.version)) {
                              throw new Error('Extension[' + i + '] ' + ext.name + ' requires ' + extDep.extname + '@' + extDep.version + ' (Not capatible with installed dependency ' + extDep.extname + '@' + extensionsConfig.extensions[depPositionIndex].version + ')');
                          }
                          // console.log(ext.name+'[is at position'+i+'] dependency ('+extDep.extname+') is at depPositionIndex',depPositionIndex);
                      });
                  }
                  // else{
  
                  // }
              });
              return callback(null, 'extension config is valid');
          } catch (e) {
              return callback(e);
          }
      };
  
      this.files = function() {
          return extensionsFiles;
      };
  
      this.savePluginConfig = function(name, value) {
          this[name] = value;
      }.bind(this);
  
      /**
       * require extension files from config based on enabled status and compatibilty version, also ignore and extension and return index of ignored extension. This is used to ignore default routes and load them last if enabled.
       * @param  {object} obj periodic resources object
       * @return {number}     index of ignored extension
       */
      this.loadExtensions = function(obj) {
          try {
              extensionsFiles.forEach(function(file, index) {
                  // console.log('file',file);
                  if (obj.ignoreExtension) {
                      if (file.search(obj.ignoreExtension) === -1) {
                          obj = require(file)(obj);
                      } else {
                          obj.ignoreExtensionIndex = index;
                      }
                  } else {
                      obj = require(file)(obj);
                  }
                  if (!obj) {
                      throw new Error('Extension: ' + file + ', does not return periodic obj');
                  }
              });
  
              return obj;
          } catch (e) {
              throw e;
          }
      };
  
      /**
       * load a route and return periodic object
       * @param  {string} file extension file
       * @param  {object} obj  periodic object
       * @return {object}      periodic object
       */
      this.loadExtensionRoute = function(file, obj) {
          try {
              obj = require(file)(obj);
              if (!obj) {
                  throw new Error('Extension: ' + file + ', does not return periodic obj');
              }
              return obj;
          } catch (e) {
              throw e;
          }
      };
  
      /** 
       * load extension config file: content/config/extensions.json
       * @throws {Error} If missing config file
       */
      this.init = function(appsettings) {
          extensionSkipModifyConf = typeof process.env.npm_config_skip_install_periodic_ext === 'string' || typeof process.env.npm_config_skip_ext_conf === 'string';
          /** 
           * @description if passing dirname of extension, load the config file relative to that dirname
           */
          if (typeof appsettings !== 'undefined' && typeof appsettings.dirname !== 'undefined') {
              extensionDirName = appsettings.dirname;
              if (extensionDirName.match('node_modules/@')) {
                  extensionFilePath = path.resolve(extensionDirName, '../../../content/config/extensions.json');
              } else {
                  extensionFilePath = path.resolve(extensionDirName, '../../content/config/extensions.json');
              }
          }
          /**
           * @description define extension file path to use
           */
          else if (typeof appsettings !== 'undefined' && typeof appsettings.extensionFilePath !== 'undefined') {
              extensionFilePath = appsettings.extensionFilePath;
          }
          /**
           * @description this is the default extension config file path
           */
          else {
              // console.log('initial_install',initial_install);
              extensionFilePath = path.join(path.resolve(__dirname, '../../../content/config/'), 'extensions.json');
          }
          /**
           * @description use the existing config file without modifying it
           */
          if (extensionSkipModifyConf !== true || appsettings.skipconffile !== true) {
              // console.log('appsettings',appsettings);
              extensionsConfig = fs.readJSONSync(extensionFilePath);
          }
          /**
           * @description set array of compatible extensions if the semver is compatible, only set this array if loading extension core to add extension routes to express
           */
          if (typeof appsettings !== 'undefined' && typeof appsettings.version !== 'undefined') {
              extensionsConfig.extensions.forEach(function(val) {
                  try {
                      if (semver.lte(val.periodicCompatibility, appsettings.version) && val.enabled) {
  
                          if (val.name.match('@')) {
                              val.name = val.name;
                          } else if (val.name.match('/')) {
                              val.name = val.name.split('/')[1];
                          }
  
                          extensionsFiles.push(this.getExtensionFilePath(val.name));
                      }
                  } catch (e) {
                      console.error(e);
                      throw new Error('Invalid Extension Configuration');
                  }
              }.bind(this));
          }
      }.bind(this);
      this.init(appsettings);
  }