forked from phcode-dev/staging.phcode.dev
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathExtensionLoader.js
More file actions
1 lines (1 loc) · 22.2 KB
/
ExtensionLoader.js
File metadata and controls
1 lines (1 loc) · 22.2 KB
1
define(function(require,exports,module){require("utils/Global");const _=require("thirdparty/lodash"),EventDispatcher=require("utils/EventDispatcher"),FileSystem=require("filesystem/FileSystem"),FileUtils=require("file/FileUtils"),Async=require("utils/Async"),ExtensionUtils=require("utils/ExtensionUtils"),ThemeManager=require("view/ThemeManager"),UrlParams=require("utils/UrlParams").UrlParams,NodeUtils=require("utils/NodeUtils"),PathUtils=require("thirdparty/path-utils/path-utils"),DefaultExtensions=JSON.parse(require("text!extensions/default/DefaultExtensions.json")),Dialogs=require("widgets/Dialogs"),PreferencesManager=require("preferences/PreferencesManager"),Mustache=require("thirdparty/mustache/mustache"),Strings=require("strings"),StringUtils=require("utils/StringUtils"),Metrics=require("utils/Metrics"),DeprecatedExtensionsTemplate=require("text!htmlContent/deprecated-extensions-dialog.html"),CommandManager=require("command/CommandManager"),EXTENSION_TAKEDOWN_LOCALSTORAGE_KEY="PH_EXTENSION_TAKEDOWN_LIST",STATE_DEPRECATED_EXTENSIONS_DIALOG_SHOWN="deprecatedExtensionsDialogShown";function _getTakedownListLS(){try{let list=localStorage.getItem(EXTENSION_TAKEDOWN_LOCALSTORAGE_KEY);if(list&&(list=JSON.parse(list),Array.isArray(list)))return list}catch(e){console.error(e)}return[]}const loadedExtensionIDs=new Map;let takedownExtensionList=new Set(_getTakedownListLS());const EXTENSION_TAKEDOWN_URL=brackets.config.extensionTakedownURL;function _anyTakenDownExtensionLoaded(){if(0===takedownExtensionList.size||0===loadedExtensionIDs.size)return[];let smaller,larger;takedownExtensionList.size<loadedExtensionIDs.size?(smaller=takedownExtensionList,larger=Array.from(loadedExtensionIDs.keys())):(smaller=Array.from(loadedExtensionIDs.keys()),larger=takedownExtensionList);const matches=[];for(const id of smaller)(larger.has?larger.has(id):larger.includes(id))&&matches.push(id);return matches}function fetchWithTimeout(url,ms){const c=new AbortController,t=setTimeout(()=>c.abort(),ms);return fetch(url,{signal:c.signal}).finally(()=>clearTimeout(t))}fetchWithTimeout(EXTENSION_TAKEDOWN_URL,2e4).then(response=>{if(!response.ok)throw new Error(`HTTP ${response.status} - ${response.statusText}`);return response.json()}).then(data=>{if(console.log("Extension takedown data:",data),!Array.isArray(data)||!data.every(x=>"string"==typeof x))return void console.error("Takedown list must be an array of strings.");const dataToWrite=JSON.stringify(data);localStorage.setItem(EXTENSION_TAKEDOWN_LOCALSTORAGE_KEY,dataToWrite),takedownExtensionList=new Set(data);const compromisedExtensionsLoaded=_anyTakenDownExtensionLoaded();if(!compromisedExtensionsLoaded.length)return;const writtenData=localStorage.getItem(EXTENSION_TAKEDOWN_LOCALSTORAGE_KEY);writtenData===dataToWrite?location.reload():console.error("Failed to write taken down extension to localstorage")}).catch(console.error);const desktopOnlyExtensions=DefaultExtensions.desktopOnly,DefaultExtensionsList=Phoenix.isNativeApp?[...DefaultExtensions.defaultExtensionsList,...desktopOnlyExtensions]:DefaultExtensions.defaultExtensionsList;if(Phoenix.isTestWindow){const index=DefaultExtensionsList.indexOf("Git");-1!==index&&DefaultExtensionsList.splice(index,1)}const customExtensionLoadPaths={},_DELETED_EXTENSION_FILE_MARKER="_phcode_extension_marked_for_delete";var EXTENSION_LOAD_TIMOUT_SECONDS=60,INIT_EXTENSION_TIMEOUT;const EVENT_EXTENSION_LOADED="load",EVENT_EXTENSION_DISABLED="disabled",EVENT_EXTENSION_LOAD_FAILED="loadFailed";var _init=!1,_extensions={},_initExtensionTimeout=1e3*EXTENSION_LOAD_TIMOUT_SECONDS,srcPath=FileUtils.getNativeBracketsDirectoryPath(),contexts={},pathLib=Phoenix.VFS.path;srcPath=srcPath.replace(/\/test$/,"/src");var globalPaths=brackets._getGlobalRequireJSConfig().paths;Object.keys(globalPaths).forEach(function(key){globalPaths[key]=PathUtils.makePathAbsolute(srcPath+"/"+globalPaths[key])});const DEFAULT_EXTENSIONS_PATH_BASE="extensions/default";function getDefaultExtensionPath(){return window.PhoenixBaseURL+DEFAULT_EXTENSIONS_PATH_BASE}function _getExtensionPath(){return pathLib.normalize(Phoenix.VFS.getExtensionDir())}function getDevExtensionPath(){return pathLib.normalize(Phoenix.VFS.getDevExtensionDir())}function getUserExtensionPath(){return pathLib.normalize(Phoenix.VFS.getUserExtensionDir())}function getRequireContextForExtension(name){return contexts[name]}function _getInitExtensionTimeout(){return _initExtensionTimeout}function _setInitExtensionTimeout(value){_initExtensionTimeout=value}function _mergeConfigFromURL(baseConfig){var deferred=new $.Deferred,extensionConfigFile=baseConfig.baseUrl+"/requirejs-config.json";return $.getJSON(extensionConfigFile).done(function(extensionConfig){if(0!==Object.keys(extensionConfig||{}).length)try{extensionConfig.paths||(extensionConfig.paths={}),_.extend(extensionConfig.paths,baseConfig.paths),_.extend(extensionConfig,_.omit(baseConfig,"paths")),deferred.resolve(extensionConfig)}catch(err){deferred.reject("failed to parse requirejs-config.json")}else deferred.resolve(baseConfig)}).fail(function(err){200===err.status&&console.error("[Extension] The require config file provided is invalid",extensionConfigFile),deferred.resolve(baseConfig)}),deferred.promise()}function _mergeConfig(baseConfig){if(baseConfig.baseUrl.startsWith("http://")||baseConfig.baseUrl.startsWith("https://")||baseConfig.baseUrl.startsWith("phtauri://")||baseConfig.baseUrl.startsWith("asset://"))return _mergeConfigFromURL(baseConfig);throw new Error("Config can only be loaded from an http url, but got"+baseConfig.baseUrl)}const savedFSlib=window.fs;function _loadNodeExtension(name,extensionMainPath,nodeConfig){const mainPlatformPath=Phoenix.fs.getTauriPlatformPath(extensionMainPath);console.log("Loading node extension for "+name,extensionMainPath,":",mainPlatformPath,nodeConfig),NodeUtils._loadNodeExtensionModule(mainPlatformPath)}function loadExtensionModule(name,config,entryPoint,metadata){let extensionConfig={context:name,baseUrl:config.baseUrl,paths:globalPaths,locale:brackets.getLocale(),waitSeconds:EXTENSION_LOAD_TIMOUT_SECONDS,config:{text:{useXhr:function(_url,_protocol,_hostname,_port){return!0}}}};const isDefaultExtensionModule=extensionConfig.baseUrl&&extensionConfig.baseUrl.startsWith(`${window.PhoenixBaseURL}extensions/default/`);return _mergeConfig(extensionConfig).then(function(mergedConfig){var extensionRequire=brackets.libRequire.config(mergedConfig),extensionRequireDeferred=new $.Deferred;if(!isDefaultExtensionModule&&config.nativeDir&&metadata.nodeConfig){if(!Phoenix.isNativeApp&&metadata.nodeConfig.nodeIsRequired)return extensionRequireDeferred.reject(new Error(`Extension ${name} cannot be loaded in browser as it needs node(nodeConfig.nodeIsRequired:true)`)),extensionRequireDeferred.promise();if(Phoenix.isNativeApp){if(!metadata.nodeConfig.main)return extensionRequireDeferred.reject(new Error(`Extension ${name} doesnt specify a main file(nodeConfig.main) in package.json!`)),extensionRequireDeferred.promise();_loadNodeExtension(name,path.join(config.nativeDir,metadata.nodeConfig.main),metadata.nodeConfig)}else console.log(`Extension ${name} optionally needs node. Node not loaded in browser.`)}return contexts[name]=extensionRequire,extensionRequire([entryPoint],extensionRequireDeferred.resolve,extensionRequireDeferred.reject),extensionRequireDeferred.promise()}).then(function(module){var initPromise;if(savedFSlib!==window.fs&&(console.error("fslib overwrite detected while loading extension. This means that some extension tried to modify a core library. reverting to original lib.."),window.fs=savedFSlib),_extensions[name]=module,module&&module.initExtension&&"function"==typeof module.initExtension){try{initPromise=Async.withTimeout(module.initExtension(),_getInitExtensionTimeout())}catch(err){return console.error("[Extension] Error -- error thrown during initExtension for "+name+": "+err),logger.reportError(err),(new $.Deferred).reject(err).promise()}if(initPromise)return initPromise.fail(function(err){let errorMessage="[Extension] Error -- timeout during initExtension for "+name;err===Async.ERROR_TIMEOUT?console.error(errorMessage):(errorMessage="[Extension] Error -- failed initExtension for "+name,console.error(errorMessage+(err?": "+err:""))),isDefaultExtensionModule&&logger.reportError(err,errorMessage)}),initPromise}},function errback(err){var additionalInfo=String(err);"scripterror"===err.requireType&&err.originalError&&(additionalInfo="Module does not exist: "+err.originalError.target.src),console.error("[Extension] failed to load "+config.baseUrl+" - "+additionalInfo),isDefaultExtensionModule&&logger.reportError(err,"[Extension] failed to load "+config.baseUrl+" - "+additionalInfo),"define"===err.requireType&&console.log(err.stack)})}function loadExtension(name,config,entryPoint){var promise=new $.Deferred;return ExtensionUtils.loadMetadata(config.baseUrl,name).always(promise.resolve),promise.then(function(metadata){return isExtensionTakenDown(metadata.name)?(logger.leaveTrail("skip load taken down extension: "+metadata.name),console.warn("skip load taken down extension: "+metadata.name),(new $.Deferred).reject("disabled").promise()):(metadata.name&&loadedExtensionIDs.set(metadata.name,{loadedFromDisc:!!config.nativeDir,extensionPath:config.nativeDir||config.baseUrl,extensionName:metadata.title||metadata.name}),metadata&&metadata.theme?void 0:metadata.disabled?(new $.Deferred).reject("disabled").promise():loadExtensionModule(name,config,entryPoint,metadata))}).then(function(){exports.trigger(EVENT_EXTENSION_LOADED,config.baseUrl)},function(err){"disabled"===err?exports.trigger(EVENT_EXTENSION_DISABLED,config.baseUrl):exports.trigger(EVENT_EXTENSION_LOAD_FAILED,config.baseUrl)})}function _testExtensionByURL(name,config,entryPoint){var result=new $.Deferred;try{var extensionRequire;brackets.libRequire.config({context:name,baseUrl:config.baseUrl,paths:$.extend({},config.paths,globalPaths),waitSeconds:EXTENSION_LOAD_TIMOUT_SECONDS})([entryPoint],function(){console.log("Test extension loaded: ",name),result.resolve()},function(err){console.log("Unit tests not found for:",name,err),result.reject()})}catch(e){console.error("Test extension load failed: ",name,e),result.resolve()}return result.promise()}function testExtension(name,config,entryPoint){var result=new $.Deferred,extensionPath=config.baseUrl+"/"+entryPoint+".js";return extensionPath.startsWith("http://")||extensionPath.startsWith("https://")||extensionPath.startsWith("phtauri://")||extensionPath.startsWith("asset://")?_testExtensionByURL(name,config,entryPoint):(FileSystem.resolve(extensionPath,function(err,entry){var extensionRequire;!err&&entry.isFile?brackets.libRequire.config({context:name,baseUrl:config.baseUrl,paths:$.extend({},config.paths,globalPaths)})([entryPoint],function(){result.resolve()}):result.reject()}),result.promise())}async function _removeExtensionsMarkedForDelete(directory,contents){let extensions=[],promises=[];for(let extensionEntry of contents)try{if(extensionEntry.isDirectory){const extensionName=extensionEntry.name;let markedRemove=FileSystem.getFileForPath(path.join(directory,extensionName,_DELETED_EXTENSION_FILE_MARKER));promises.push(markedRemove.existsAsync().then(deleteMarkerExists=>{if(deleteMarkerExists)return new Promise(resolve=>{extensionEntry.unlink(err=>{err?(console.error("Error removing extension marked for removal:",extensionName,extensionEntry.fullPath,err),resolve(err)):(console.log("Removed extension marked for delete:",extensionName,extensionEntry.fullPath),resolve())})});extensions.push(extensionName)}))}}catch(e){console.error("Error processing extension path:",extensionEntry)}return await Promise.all(promises),extensions}function _loadAll(directory,entryPoint,processExtension){var result=new $.Deferred;return FileSystem.getDirectoryForPath(directory).getContents(async function(err,contents){if(err)return console.error("[Extension] Error -- could not read native directory: "+directory),void result.reject();const extensions=await _removeExtensionsMarkedForDelete(directory,contents);0!==extensions.length?Async.doInParallel(extensions,function(item){const extConfig={baseUrl:Phoenix.VFS.getVirtualServingURLForPath(directory+"/"+item),nativeDir:directory+"/"+item,paths:{}};return console.log("Loading Extension from virtual fs: ",extConfig),processExtension(item,extConfig,entryPoint)}).always(function(){result.resolve()}):result.resolve()}),result.promise()}function loadAllDefaultExtensions(){const extensionPath=getDefaultExtensionPath(),result=new $.Deferred;return Async.doInParallel(DefaultExtensionsList,function(extensionEntry){var extConfig;return logger.leaveTrail("loading default extension: "+extensionEntry),loadExtension(extensionEntry,{baseUrl:extensionPath+"/"+extensionEntry},"main")}).always(function(){result.resolve()}),result.promise()}function _loadDefaultExtension(extensionFolderName){const extensionPath=getDefaultExtensionPath();var extConfig;return logger.leaveTrail("loading default extension: "+extensionFolderName),loadExtension(extensionFolderName,{baseUrl:extensionPath+"/"+extensionFolderName},"main")}function loadAllExtensionsInNativeDirectory(directory){return _loadAll(directory,"main",loadExtension)}function loadExtensionFromNativeDirectory(directory){logger.leaveTrail("loading custom extension from path: "+directory);const extConfig={baseUrl:Phoenix.VFS.getVirtualServingURLForPath(directory.replace(/\/$/,"")),nativeDir:directory};return loadExtension("ext"+directory.replace("/","-"),extConfig,"main")}function testAllExtensionsInNativeDirectory(directory){const result=new $.Deferred,extensionsDir=_getExtensionPath()+"/"+directory,config={baseUrl:Phoenix.VFS.getVirtualServingURLForPath(extensionsDir)};return config.paths={perf:Phoenix.VFS.getVirtualServingURLForPath("/test/perf"),spec:Phoenix.VFS.getVirtualServingURLForPath("/test/spec")},FileSystem.getDirectoryForPath(extensionsDir).getContents(function(err,contents){if(err)console.error("[Extension Load Test] Error -- could not read native directory: "+directory),result.reject();else{let i,extensions=[];for(i=0;i<contents.length;i++)contents[i].isDirectory&&extensions.push(contents[i].name);if(0===extensions.length)return void result.resolve();Async.doInParallel(extensions,function(extensionName){let loadResult=new $.Deferred;var extConfig={basePath:"extensions/default",baseUrl:config.baseUrl+"/"+extensionName,paths:config.paths};return console.log("Loading Extension Test from virtual fs: ",extConfig),_testExtensionByURL(extensionName,extConfig,"unittests").always(function(){console.log("tested",extensionName),loadResult.resolve()}),loadResult.promise()}).always(function(){result.resolve()})}}),result.promise()}function testAllDefaultExtensions(){const bracketsPath=FileUtils.getNativeBracketsDirectoryPath(),baseUrl=window.PhoenixBaseURL;let srcBaseUrl=new URL(baseUrl+"../src").href,result=new $.Deferred;return srcBaseUrl.endsWith("/")||(srcBaseUrl+="/"),Async.doInParallel(DefaultExtensionsList,function(extensionEntry){const loadResult=new $.Deferred,extConfig={basePath:"extensions/default",baseUrl:new URL(srcBaseUrl+DEFAULT_EXTENSIONS_PATH_BASE+"/"+extensionEntry).href,paths:{perf:bracketsPath+"/perf",spec:bracketsPath+"/spec"}};return console.log("Testing default extension: ",extensionEntry),_testExtensionByURL(extensionEntry,extConfig,"unittests").always(function(){console.log("load complete",extensionEntry),loadResult.resolve()}),loadResult.promise()}).always(function(){result.resolve()}),result.promise()}function getSourcePathForExtension(extensionPath){const devTempExtDir=`${Phoenix.VFS.getDevTempExtensionDir()}/`;if(extensionPath.startsWith(devTempExtDir))for(let customExtensionLoadPath of Object.keys(customExtensionLoadPaths)){let srcBasePath=customExtensionLoadPaths[customExtensionLoadPath];if(extensionPath.startsWith(Phoenix.VFS.ensureTrailingSlash(customExtensionLoadPath))){const relativePath=extensionPath.replace(Phoenix.VFS.ensureTrailingSlash(customExtensionLoadPath),"");return srcBasePath.endsWith("/")||(srcBasePath+="/"),`${srcBasePath}${relativePath}`}}return extensionPath}function _attachThemeLoadListeners(){ThemeManager.off(`${ThemeManager.EVENT_THEME_LOADED}.extensionLoader`),ThemeManager.on(`${ThemeManager.EVENT_THEME_LOADED}.extensionLoader`,()=>{ThemeManager.refresh(!0)})}function _getRandomPrefix(){let uuid;return crypto.randomUUID().split("-")[0]}function _loadCustomExtensionPath(extPath){const assetsServeDir=Phoenix.VFS.getTauriAssetServeDir();if(assetsServeDir&&extPath.startsWith(Phoenix.VFS.getTauriDir())&&!extPath.startsWith(assetsServeDir)){const newExtVersionStr=_getRandomPrefix(),extParentPath=`${Phoenix.VFS.getDevTempExtensionDir()}/${Phoenix.path.basename(extPath)}`,extDestPath=`${extParentPath}/${newExtVersionStr}`;customExtensionLoadPaths[extDestPath]=extPath,Phoenix.fs.unlink(extParentPath,()=>{Phoenix.VFS.ensureExistsDirAsync(extParentPath).then(()=>{Phoenix.fs.copy(extPath,extDestPath,function(err,_copiedPath){err?(console.error(`Error copying extension from ${extPath} to ${extDestPath}`,err),result.reject(err)):(_attachThemeLoadListeners(),loadExtensionFromNativeDirectory(extDestPath).fail(console.error))})}).catch(err=>{console.error(`Error creating dir ${extDestPath}`,err),result.reject(err)})});let result=new $.Deferred;return result.resolve(),result.promise()}return loadExtensionFromNativeDirectory(extPath)}function init(paths){require("extensionsIntegrated/loader");var params=new UrlParams;if(_init)return(new $.Deferred).resolve().promise();if(!paths&&(params.parse(),paths="true"!==params.get("reloadWithoutUserExts")?["default",getUserExtensionPath(),getDevExtensionPath()]:[],params.get("loadDevExtensionPath"))){let customLoadPaths=params.get("loadDevExtensionPath").split(",");for(let customPath of customLoadPaths)paths.push("custom:"+customPath)}var extensionPath=getUserExtensionPath();FileSystem.getDirectoryForPath(extensionPath).create(),FileSystem.getDirectoryForPath(getDevExtensionPath()).create();var disabledExtensionPath=extensionPath.replace(/\/user$/,"/disabled");FileSystem.getDirectoryForPath(disabledExtensionPath).create(),delete window.KernalModeTrust;var promise=Async.doInParallel(paths,function(extPath){return"default"===extPath?loadAllDefaultExtensions():extPath.startsWith("custom:")?_loadCustomExtensionPath(extPath.replace("custom:","")):loadAllExtensionsInNativeDirectory(extPath)},!1);return promise.always(function(){_init=!0,_checkAndShowDeprecatedExtensionsDialog()}),promise}function isExtensionTakenDown(extensionID){return!!extensionID&&takedownExtensionList.has(extensionID)}async function uninstallExtension(extensionID){const extensionInfo=loadedExtensionIDs.get(extensionID);if(!extensionInfo)throw new Error(`Extension ${extensionID} not found in loaded extensions`);if(!extensionInfo.loadedFromDisc)throw new Error(`Cannot uninstall built-in extension: ${extensionID}`);const extensionDir=FileSystem.getDirectoryForPath(extensionInfo.extensionPath);await extensionDir.unlinkAsync()}function _checkAndShowDeprecatedExtensionsDialog(){let needsRestart=!1;const deprecatedExtensionsConfig=DefaultExtensions.deprecatedExtensions;if(!deprecatedExtensionsConfig||!deprecatedExtensionsConfig.extensionIDsAndDocs)return;const deprecatedExtensionIDs=deprecatedExtensionsConfig.extensionIDsAndDocs;let shownDeprecatedExtensions=PreferencesManager.stateManager.get(STATE_DEPRECATED_EXTENSIONS_DIALOG_SHOWN);shownDeprecatedExtensions&&"object"==typeof shownDeprecatedExtensions||(shownDeprecatedExtensions={});const deprecatedExtensionsFound=[];for(const extensionID of loadedExtensionIDs.keys())if(deprecatedExtensionIDs[extensionID]&&!shownDeprecatedExtensions[extensionID]){const extensionInfo=loadedExtensionIDs.get(extensionID),extensionName=extensionInfo&&extensionInfo.extensionName;deprecatedExtensionsFound.push({id:extensionID,name:extensionName||extensionID,docUrl:deprecatedExtensionIDs[extensionID]})}if(0===deprecatedExtensionsFound.length)return;const templateVars={extensions:deprecatedExtensionsFound,Strings:Strings},$template=$(Mustache.render(DeprecatedExtensionsTemplate,templateVars)),dialog=Dialogs.showModalDialogUsingTemplate($template,!1);$template.on("click",".uninstall-extension-btn",async function(){const $button=$(this),extensionID=$button.data("extension-id"),$extensionItem=$button.closest(".deprecated-extension-item");$button.prop("disabled",!0),$button.text(Strings.REMOVING);try{Metrics.countEvent(Metrics.EVENT_TYPE.EXTENSIONS,"removeDep",extensionID),await uninstallExtension(extensionID);const $okButton=$template.find('[data-button-id="ok"]');$okButton.text(Strings.RESTART_APP_BUTTON),$extensionItem.find(".extension-info strong").addClass("striked"),$button.remove(),needsRestart=!0}catch(err){Metrics.countEvent(Metrics.EVENT_TYPE.EXTENSIONS,"removeDep","fail"),logger.reportError(err,"Failed to uninstall deprecated extension:"+extensionID);const message=StringUtils.format(Strings.ERROR_UNINSTALLING_EXTENSION_MESSAGE,extensionID);Dialogs.showErrorDialog(Strings.ERROR_UNINSTALLING_EXTENSION_TITLE,message),$button.prop("disabled",!1),$button.text(Strings.REMOVE)}}),$template.on("click",'[data-button-id="ok"]',function(){needsRestart?CommandManager.execute("debug.refreshWindow"):dialog.close()});for(const ext of deprecatedExtensionsFound)shownDeprecatedExtensions[ext.id]=!0;PreferencesManager.stateManager.set(STATE_DEPRECATED_EXTENSIONS_DIALOG_SHOWN,shownDeprecatedExtensions)}EventDispatcher.makeEventDispatcher(exports),Phoenix.isTestWindow&&(exports._loadDefaultExtension=_loadDefaultExtension,exports._setInitExtensionTimeout=_setInitExtensionTimeout,exports._getInitExtensionTimeout=_getInitExtensionTimeout),exports._DELETED_EXTENSION_FILE_MARKER=_DELETED_EXTENSION_FILE_MARKER,exports.init=init,exports.getDefaultExtensionPath=getDefaultExtensionPath,exports.getUserExtensionPath=getUserExtensionPath,exports.getRequireContextForExtension=getRequireContextForExtension,exports.getSourcePathForExtension=getSourcePathForExtension,exports.loadExtension=loadExtension,exports.testExtension=testExtension,exports.loadAllExtensionsInNativeDirectory=loadAllExtensionsInNativeDirectory,exports.loadExtensionFromNativeDirectory=loadExtensionFromNativeDirectory,exports.isExtensionTakenDown=isExtensionTakenDown,exports.uninstallExtension=uninstallExtension,exports.testAllExtensionsInNativeDirectory=testAllExtensionsInNativeDirectory,exports.testAllDefaultExtensions=testAllDefaultExtensions,exports.EVENT_EXTENSION_LOADED=EVENT_EXTENSION_LOADED,exports.EVENT_EXTENSION_DISABLED=EVENT_EXTENSION_DISABLED,exports.EVENT_EXTENSION_LOAD_FAILED=EVENT_EXTENSION_LOAD_FAILED});