diff --git a/packages/ecmascript/src/Module.ts b/packages/ecmascript/src/Module.ts index 095b456ef..2af042d8e 100644 --- a/packages/ecmascript/src/Module.ts +++ b/packages/ecmascript/src/Module.ts @@ -17,18 +17,31 @@ const GLOBAL = System.getContext() || (function () { }).call(null); /** - * Function to handle errors when importing a module/action. + * Function to handle errors when loading/importing an action or module. * @param {string | Error} err - error (message) - * @returns null - indicates an unsuccessful attempt to load the module was made (as opposed to undefined) */ -export type ErrorHandler = (error: string | Error) => null; - -/** Default function to handle errors when importing a module/action. Logs an error and returns null. */ -export const DEFAULT_ERR_HANDLER: ErrorHandler = (err) => { System.error(err?.toString()); return null; } +export type ModuleErrorHandler = (error: string | Error) => void; + +/** Predefined {@link ModuleErrorHandler error handler options} for use in {@link Module.setModuleErrorHandler} */ +export enum DefaultModuleErrorHandlers { + /** [Default] Creates a System ERROR level log entry for the error. */ + SYS_ERROR, + /** Creates a System WARNING level log entry for the error. */ + SYS_WARN, + /** Creates a System INFO level log entry for the error. */ + SYS_INFO, + /** Creates a System DEBUG level log entry for the error. */ + SYS_DEBUG, + /** Ignores the error. */ + SILENT, + /** Rethtows the error without handling it. */ + THROW_ERROR +} export interface ModuleConstructor extends Function { /** - * @param {string[]} specifiers - listed names of elements (actions/modules) to import from a Module. At least 1 is required. + * @param {string[]} specifiers - listed names of elements (actions/modules) to import from a Module. + * At least 1 is required. * Also accepts "default" and "*" as specifiers for the default exports/full contents of the module. * The module can be specified via chained call to from(modulePath) * @returns {ModuleImport} @@ -40,13 +53,27 @@ export interface ModuleConstructor extends Function { */ export(): ModuleExport; /** - * Loads an action or module (namespace) by its path. - * @param {string} name - module/action name (path) - * @param {ErrorHandler} [onError] - function to handle errors when loading a module/action. + * Loads a module (namespace) by its name. Error handling (as defined in + * {@link ModuleConstructor.setModuleErrorHandler}) is invoked if: + * - the path is missing or invalid + * - the System cannot load a module for the given path. + * @param {string} name - module name: + * - may consist of one or more words (comprising of numbers, latin lower/upercase letters, '-' or '_'), + * separated by '.' + * - may not start or end with '.' * @returns {any} - action or module. Null if the path is null/undefined * or if System cannot find a module with the given path. */ - load(path: string, onError?: ErrorHandler): ModuleDescriptor; + load(path: string): ModuleDescriptor; + + /** + * Changes the active {@link ModuleErrorHandler} used in the import and load operations + * @param {DefaultModuleErrorHandlers | ModuleErrorHandler} eh - can be: + * - a custom module {@link ModuleErrorHandler error handler function} + * - one of the predefined {@link DefaultModuleErrorHandlers} options + * Default is {@link DefaultModuleErrorHandlers.SYS_ERROR} + */ + setModuleErrorHandler(eh: DefaultModuleErrorHandlers | ModuleErrorHandler): void; } export interface ModuleDescriptor { @@ -60,15 +87,20 @@ export interface ModuleImportConstructor { export interface ModuleImport { /** * Attemps to import the specified action or module (namespace). - * @param {string} path - path of the module to import from - * @param {string} [base] - base path of the module to import from. Required when the path is relative. - * @param {ErrorHandler} [onError] - function to handle errors when importing a module/action. Invoked if: + * Error handling (as defined in {@link ModuleConstructor.setModuleErrorHandler}) is invoked if: * - no valid specifiers were provided. * - the path is missing or invalid relative to the provided basePath * - the System cannot load an action or module for the given path. + * @param {string} path - path of the module to import from: + * - may consist of one or more words (comprising of numbers, latin lower/upercase letters, '-' or '_'), + * separated by '.' or '/' + * - may start with './' or any number of '../', but cannot have consecutive separators or end with a separator + * - slashes (/) and backslashes(\) can be used interchangeably + * @param {string} [base] - base path of the module to import from. Required when the path is relative. + * NOTE: base is not officially part of the VROESModuleImport interface. * @returns {any|Array} the imported module/actions(s). */ - from(path: string, base?: string, onError?: ErrorHandler): any | any[]; + from(path: string, base?: string): any | any[]; } export interface ModuleExportConstructor { @@ -100,6 +132,44 @@ export interface ModuleElementList { [name: string]: any; } +/** + * Key of the Module attribute that holds the last set error handler via {@link Module.setModuleErrorHandler}. + */ +const MODULE_ERROR_HANDLER_KEY = "__onError"; + +/** + * Predefined handlers for errors on loading/importing a module or action - see {@link DefaultModuleErrorHandlers} + */ +const ModuleErrorHandlers = { + [DefaultModuleErrorHandlers.SYS_ERROR]: error => System.error(error?.toString()), + [DefaultModuleErrorHandlers.SYS_WARN]: error => System.warn(error?.toString()), + [DefaultModuleErrorHandlers.SYS_INFO]: error => System.log(error?.toString()), + [DefaultModuleErrorHandlers.SYS_DEBUG]: error => System.debug(error?.toString()), + [DefaultModuleErrorHandlers.SILENT]: error => { }, + [DefaultModuleErrorHandlers.THROW_ERROR]: error => { throw (typeof error === "string" ? new Error(error) : error); } +} as const; + +/** + * Regular expression for validating a path for action/module import: + * - may consist of one or more words (comprising of numbers, latin lower/upercase letters, '-' or '_'), + * separated by '.' or '/' + * - may start with './' or any number of '../', but cannot have consecutive separators or end with a separator + * - slashes (/) and backslashes(\) can be used interchangeably + * Note - use of '-' and capital letters is not limited, despite not being recommended. + * Using non-capturing groups (?:) to reduce overhead. + */ +const IMPORT_PATH_REGEX = /^(?:(?:\.\.\/|\.\.\\)+|\.\/|\.\\)?(?:[\w-]+[.\/\\])*[\w-]+$/g; + +/** + * Regular expression for validating a base path for action/module import: + * - may consist of one or more words (comprising of numbers, latin lower/upercase letters, '-' or '_'), + * separated by '.' + * - may not start or end with '.' + * Note - use of '-' and capital letters is not limited, despite not being recommended. + * Using non-capturing group (?:) to reduce overhead. + */ +const IMPORT_BASE_REGEX = /^(?:[\w-]+\.)*[\w-]+$/g; + /** * @return {Any} */ @@ -108,84 +178,19 @@ export interface ModuleElementList { this.specifiers = specifiers || []; }; - /** - * Attemps to import the specified action or module (namespace). - * @param {string} path - path of the module to import from - * @param {string} [base] - base path of the module to import from. Required when the path is relative. - * @param {ErrorHandler} [onError = DEFAULT_ERR_HANDLER] - function to handle errors when importing a module/action. Invoked if: - * - no valid specifiers were provided. - * - the path is missing or invalid relative to the provided basePath - * - the System cannot load an action or module for the given path. - * Passed on recursively to any child modules of the loaded one. - * @returns {any|Array} the imported module/actions(s). - */ - Import.prototype.from = function (path: string, base?: string, onError: ErrorHandler = DEFAULT_ERR_HANDLER): any | any[] { - let error = !this.specifiers?.length ? "No actions or modules were specified for import! " - : (!base && path?.[0] === "." ? `Cannot resolve relative path '${path}' without a base path!` : ""); + Import.prototype.from = function (path: string, base?: string): any | any[] { + try { + const absolutePath = constructAbsolutePath(path, base); + const actionResult = loadActionOrModule(absolutePath); + const result = extractImports(this.specifiers, actionResult); - if (error) { - return onError(error); - } - if (path?.[0] === ".") { - error = `Relative path '${path}' is not valid for base path '${base}'!`; - path = path.replace(/[\\]/g, "/"); - if (path.indexOf("./") === 0) { - path = path.substring(2); - } - else { - while (base && path.indexOf("../") === 0) { - path = path.substring(3); - base = base.substring(0, base.lastIndexOf(".")); - } - } - if (!path || !base || path.indexOf("./") >= 0) { - return onError(error); - } - path = `${base}.${path}`; + return result.length > 1 ? result : result[0]; } - path = path && path.replace(/[\/]/g, "."); - const actionResult = loadActionOrModule(path, onError); - if (!actionResult) { - return onError(`Cannot import from module with path '${path}'!`); - } - - const invalidNames = checkForInvalidImportSpecifiers(this.specifiers, actionResult); - if (invalidNames.length) { - return onError("Some of the specified elements for import are invalid:\n" + invalidNames.join("\n")); + catch (err) { + return onError(`Cannot import from module with ${path?.[0] === "." ? "relative " : ""}path '${path}'! ${err?.toString()}`); } - - let result = this.specifiers.map(name => (name === "*" ? actionResult : actionResult[name])); - return result.length > 1 ? result : result[0]; }; - /** - * Checks for invalid specifiers - * @param {string[]} specifiers - list of specifiers ("default", "*", names of module elements) - * @param {any} [importedModule] - module descriptor - * @returns {string[]} - list of invalid specifiers (blank, duplicate, missing from the provided module) - */ - function checkForInvalidImportSpecifiers(specifiers: string[], importedModule?: any): string[] { - const invalidNames: string[] = []; - const importNames: string[] = []; - - specifiers.forEach((specifier, index) => { - if (!specifier) { - invalidNames.push(`[${index}]: '${specifier}'`); - } - else if (importNames.indexOf(specifier) >= 0) { - invalidNames.push(`[${index}]: '${specifier}' (duplicate)`); - } - else if (specifier !== "*" && specifier !== "default" && importedModule && !importedModule[specifier]) { - invalidNames.push(`[${index}]: '${specifier}' (module contains no such action or namespace)`); - } - else { - importNames.push(specifier); - } - }); - - return invalidNames; - } - let Export: ModuleExportConstructor = function () { this.elements = {}; }; @@ -249,38 +254,25 @@ export interface ModuleElementList { return new Export(); }; - /** - * Loads an action or module (namespace) by its path. - * @param {string} name - module name (path) - * @param {ErrorHandler} [onError = DEFAULT_ERR_HANDLER] - function to handle errors when loading a module/action. - * Invoked on failure to create the module or (JIT) its child actions/modules. - * @returns {any} - action or module. Null if the path is null/undefined - * or if System cannot find a module with the given path. - */ - Module.load = function (name: string, onError: ErrorHandler = DEFAULT_ERR_HANDLER): any { - let result: any; - let error: any = ""; - try { - const moduleInfo = !name ? null : System.getModule(name); - result = !moduleInfo ? null : createModule(moduleInfo, onError); - } catch (err) { - error = err; - } - return result || onError(`Failed to load module '${name}'! ${error}`); + Module.load = function (name: string): any { + const moduleInfo = !name ? null : System.getModule(name); + return moduleInfo ? createModule(moduleInfo) : onError(`Failed to load module '${name}'!`); + }; + + Module.setModuleErrorHandler = function (eh: DefaultModuleErrorHandlers | ModuleErrorHandler) { + Module[MODULE_ERROR_HANDLER_KEY] = eh; }; /** * Loads and caches an action or module (namespace) by its path. * @param {string} name - module name (path). Used as key for caching. - * @param {ErrorHandler} [onError = DEFAULT_ERR_HANDLER] - function to handle errors when loading a module/action. Invoked if: - * - a circular dependency is detected. - * - the System cannot find a module with the given path. * @returns {any} - (cached) action or module or NULL, if not found or the path is blank or null/undefined. + * @throws Error when a circular dependency is detected. + * @throws Error when the System cannot find a module with the given path. */ - function loadActionOrModule(path: string, onError: ErrorHandler = DEFAULT_ERR_HANDLER): any { + function loadActionOrModule(path: string): any { const actionResults = GLOBAL.__actions__ = (GLOBAL.__actions__ || {}); - let actionResult = !path ? null : actionResults[path]; - let error: any = ""; + let actionResult = actionResults[path]; if (actionResult === undefined) { let loadStack = GLOBAL.__importStack__ = (GLOBAL.__importStack__ || []); @@ -289,38 +281,37 @@ export interface ModuleElementList { const circPath = loadStack.slice(indexOfPathInStack); circPath.push(path); loadStack = []; - error = `Detected circular dependency in module loading. Path: ${JSON.stringify(circPath)}`; + throw new Error(`Detected circular dependency in module loading. Path: ${JSON.stringify(circPath)}`); } - else { - loadStack.push(path); - try { - actionResult = invokeActionOrModule(path, onError); - } - catch (err) { - error = err?.toString(); - } - finally { - loadStack.pop(); - } + + loadStack.push(path); + try { + actionResult = invokeActionOrModule(path); + } + finally { + loadStack.pop(); } + actionResults[path] = actionResult || null; // won't reattempt unsuccessful load } - return actionResult || onError(`Failed to load action or module with path '${path}'! ${error}`); + if (!actionResult) { + throw new Error(`Failed to load action or module with path '${path}'!`) + } + + return actionResult; } /** * Attempts to invoke the action or module (namespace) with the given path. * @param {string} path - absolute path with dot (.) separators. Cannot be blank or null/undefined. - * @param {ErrorHandler} [onError = DEFAULT_ERR_HANDLER] - function to handle errors when loading a module/action. - * Passed on recursively to a module's child modules - to be invoked on creation. * @returns any - action or module: * - If there is an action at the given path, it will be returned. * - if there is a module with an "index" action at the given path, the "index" action will be returned. * - if there is a module at the given path without an "index" action, a module descriptor for it will be returned. * @throws Error if no module exists for the given path. */ - function invokeActionOrModule(path: string, onError: ErrorHandler = DEFAULT_ERR_HANDLER): any { + function invokeActionOrModule(path: string): any { const classIndex = path.lastIndexOf("."); const moduleName = path.substring(0, classIndex); const actionName = path.substring(classIndex + 1); @@ -328,13 +319,13 @@ export interface ModuleElementList { if (hasAction(moduleInfo, actionName)) { return invokeAction(moduleInfo, actionName); } - moduleInfo = !path ? null : System.getModule(path); + moduleInfo = System.getModule(path); if (!moduleInfo) { throw new Error(`No action or module found for paths: '${path}', '${path}/index'!`); } return hasAction(moduleInfo, "index") ? invokeAction(moduleInfo, "index") - : createModule(moduleInfo, onError); + : createModule(moduleInfo); } /** @@ -388,16 +379,14 @@ export interface ModuleElementList { /** * Creates a new {@link ModuleDescriptor} * @param {Module} moduleInfo - Module info from System.getModule(). Required. - * @param {ErrorHandler} [onError = DEFAULT_ERR_HANDLER] - function to handle errors when loading a module/action. - * Passed on recursively to a module's child modules - to be invoked on creation. * @returns {ModuleDescriptor} */ - function createModule(moduleInfo: Module, onError: ErrorHandler = DEFAULT_ERR_HANDLER): ModuleDescriptor { + function createModule(moduleInfo: Module): ModuleDescriptor { const ModuleCtor = function () { moduleInfo.actionDescriptions.forEach(actionInfo => { Object.defineProperty(ModuleCtor.prototype, actionInfo.name, { get: () => { - return loadActionOrModule(`${moduleInfo.name}.${actionInfo.name}`, onError); + return loadActionOrModule(`${moduleInfo.name}.${actionInfo.name}`); }, enumerable: true, configurable: true @@ -410,7 +399,7 @@ export interface ModuleElementList { const childModuleName = childModuleInfo.name.substring(childModuleInfo.name.lastIndexOf(".") + 1); Object.defineProperty(ModuleCtor.prototype, childModuleName, { get: () => { - return loadActionOrModule(childModuleInfo.name, onError); + return loadActionOrModule(childModuleInfo.name); }, enumerable: true, configurable: true @@ -421,5 +410,104 @@ export interface ModuleElementList { return new ModuleCtor(); } + /** + * Helper function to handle errors when loading/importing an action or module. + * Invokes the error handler set via {@link Module.setModuleErrorHandler} or, if not set, + * via the {@link DefaultModuleErrorHandlers.SYS_ERROR default error handler}. + * @param {string | Error} err - error (message) + * @returns NULL, unless the error handler rethrows the error. + */ + function onError(error): null { + let eh: ModuleErrorHandler | DefaultModuleErrorHandlers = Module[MODULE_ERROR_HANDLER_KEY]; + if (typeof eh !== "function") { + eh = ModuleErrorHandlers[eh] || ModuleErrorHandlers[DefaultModuleErrorHandlers.SYS_ERROR]; + } + eh(error); + return null; + } + + /** + * Helper function to construct the absolute path of an action or a module + * based on an action name / relative path and (optional) base module name. + * @param {string} path - action name or relative path: + * - may consist of one or more words (comprising of numbers, latin lower/upercase letters, '-' or '_'), + * separated by '.' or '/' + * - may start with './' or any number of '../', but cannot have consecutive separators or end with a separator + * - slashes (/) and backslashes(\) can be used interchangeably + * @param {string} base - base module name: + * - may consist of one or more words (comprising of numbers, latin lower/upercase letters, '-' or '_'), + * separated by '.' + * - may not start or end with '.' + * Note - use of '-' and capital letters is not limited, despite not being recommended. + * @returns {string} path, conforming to the module name convention + * @throws Error if the path is missing/invalid + * @throws Error if a base path is provided but is invalid + * @throws Error if the path is relative and the base path is missing + * @throws Error if the relative path goes too many steps back (via ../) in the base path + */ + + function constructAbsolutePath(path: string, base: string): string { + if (!path?.match(IMPORT_PATH_REGEX)) { + throw new Error(`Path is invalid!`); + } + if (path[0] !== "." && !base) { + return path.replace(/[\/\\]/g, "."); + } + if (!base?.match(IMPORT_BASE_REGEX)) { + throw new Error(`Base path is invalid: '${base}'!`); + } + path = path.replace(/[\\]/g, "/"); + let pathStartIndex = path.lastIndexOf('./'); + pathStartIndex = pathStartIndex >= 0 ? pathStartIndex + 2 : 0; + path = path.substring(pathStartIndex).replace(/[\/]/g, "."); + const backSteps = Math.floor(pathStartIndex / 3); + if (backSteps) { + const baseSplit = base.split("."); + if (backSteps >= baseSplit.length) { + throw new Error(`Too many steps back for base path '${base}'!`); + } + baseSplit.length = baseSplit.length - backSteps; + base = baseSplit.join('.'); + } + return `${base}.${path}`; + } + + /** + * Extracts the imports as objects from the module as an array. + * @param {string[]} specifiers - list of specifiers ("default", "*", names of module elements) + * @param {any} importedModule - module descriptor + * @retruns an array of imports matching the given specifiers + * @throws Error if there are no specifiers + * @throws Errir containing a list of invalid specifiers (blank, duplicate, missing from the provided module), if any. + */ + function extractImports(specifiers: string[], importedModule: any): any[] { + if (!specifiers?.length) { + throw new Error("No actions or modules were specified for import!"); + } + const invalidNames: string[] = []; + const importNames: string[] = []; + + specifiers.forEach((specifier, index) => { + if (!specifier) { + invalidNames.push(`[${index}]: '${specifier}'`); + } + else if (importNames.indexOf(specifier) >= 0) { + invalidNames.push(`[${index}]: '${specifier}' (duplicate)`); + } + else if (specifier !== "*" && specifier !== "default" && importedModule && !importedModule[specifier]) { + invalidNames.push(`[${index}]: '${specifier}' (module contains no such action or namespace)`); + } + else { + importNames.push(specifier); + } + }); + + if (invalidNames?.length) { + throw new Error("Some of the specified elements for import are invalid:\n" + invalidNames.join("\n")); + }; + + return specifiers.map(name => (name === "*" ? importedModule : importedModule[name])); + } + return Module; }); diff --git a/packages/ecmascript/src/VROES.ts b/packages/ecmascript/src/VROES.ts index 9374a8c5c..7b6c847fc 100644 --- a/packages/ecmascript/src/VROES.ts +++ b/packages/ecmascript/src/VROES.ts @@ -30,8 +30,8 @@ * * @param {String} name */ - function formatName(name:string) { - return name.replace(/([^\\\/]+$)/, function ( match ){ +function formatName(name: string) { + return name.replace(/([^\\\/]+$)/, function (match) { const repeatingDots = /([.]).*(\1)/g return repeatingDots.test(match) ? match : match.replace(/[.]([^.]*)$/, '_$1'); }).replace(/[\\\/]/g, "."); @@ -53,16 +53,17 @@ vroes.export = Module.export; vroes.load = Module.load; vroes.class = Module.import("default").from(`${moduleName}.Class`); + vroes.setModuleErrorHandler = Module.setModuleErrorHandler; vroes.require = function (name: string) { - return Module.import("*").from(formatName( name )); + return Module.import("*").from(formatName(name)); }; vroes.importLazy = function (name: string) { const result: any = {}; Object.defineProperty(result, "_", { get: () => { - return result.__lazyAction__ || (result.__lazyAction__ = Module.import("*").from(formatName( name ))) + return result.__lazyAction__ || (result.__lazyAction__ = Module.import("*").from(formatName(name))) }, enumerable: true, configurable: true diff --git a/packages/ecmascript/src/module.test.ts b/packages/ecmascript/src/module.test.ts index e24eae137..034d41e5f 100644 --- a/packages/ecmascript/src/module.test.ts +++ b/packages/ecmascript/src/module.test.ts @@ -68,36 +68,47 @@ describe("Module", () => { }); beforeEach(() => loggedSystemErrors.length = 0); - [null, undefined, ""].forEach(invalidPath => it(`import from invalid path (${invalidPath})`, () => { - expect(ESModule.import("default").from(invalidPath)).toBeNull(); - expect(loggedSystemErrors).toEqual([ - `Failed to load action or module with path '${invalidPath}'! `, - `Cannot import from module with path '${invalidPath}'!` - ]); - })); - it("import with relative path without base path", () => { - const path = "./Class"; - expect(ESModule.import("default").from(path)).toBeNull(); - expect(loggedSystemErrors).toEqual([`Cannot resolve relative path '${path}' without a base path!`]); - }); + [ + null, undefined, "", " ", "/start/separator", "end.separator.", "dupl..separator", "repative/in/../middle", "dis@llowedCharacters?!" + ].forEach(invalidPath => + it(`import from invalid path (${invalidPath})`, () => { + expect(ESModule.import("default").from(invalidPath)).toBeNull(); + expect(loggedSystemErrors).toEqual([ + `Cannot import from module with path '${invalidPath}'! Error: Path is invalid!` + ]); + }) + ); + [ + null, undefined, "", " ", "non/dot/separators", "duplicate..separators", ".start.separator", "end.separator.", "dis@llowedCharacters?!" + ].forEach(invalidBasePath => + it(`import with relative path with invalid base path '${invalidBasePath}'`, () => { + const path = "./Class"; + expect(ESModule.import("default").from(path, invalidBasePath)).toBeNull(); + expect(loggedSystemErrors).toEqual([ + `Cannot import from module with relative path '${path}'! Error: Base path is invalid: '${invalidBasePath}'!` + ]); + }) + ); it("import with relative path invalid for given base path", () => { const basePath = "com.vmware.pscoe.library.class.Class"; const invalidRelativePath = basePath.split(".").map(s => "../").join("") + "Class"; // too many steps back expect(ESModule.import("default").from(invalidRelativePath, basePath)).toBeNull(); - expect(loggedSystemErrors).toEqual([`Relative path '${invalidRelativePath}' is not valid for base path '${basePath}'!`]); + expect(loggedSystemErrors).toEqual([ + `Cannot import from module with relative path '${invalidRelativePath}'! Error: Too many steps back for base path '${basePath}'!` + ]); }); it("import with invalid module path", () => { const wrongPath = "com.wrong.path.library.class"; expect(ESModule.import("Class").from(wrongPath)).toBeNull(); expect(loggedSystemErrors).toEqual([ - `Failed to load action or module with path '${wrongPath}'! Error: No action or module found for paths: '${wrongPath}', '${wrongPath}/index'!`, - `Cannot import from module with path '${wrongPath}'!` + `Cannot import from module with path '${wrongPath}'! Error: No action or module found for paths: '${wrongPath}', '${wrongPath}/index'!` ]); }); it("import without valid specifiers", () => { const path = "com.vmware.pscoe.library.class"; expect(ESModule.import(null, undefined, "", "whatever", "Class", "default", "*", "Class").from(path)).toBeNull(); expect(loggedSystemErrors).toEqual([ + `Cannot import from module with path '${path}'! Error: ` + "Some of the specified elements for import are invalid:\n" + "[0]: 'null'\n" + "[1]: 'undefined'\n" + diff --git a/vro-types/ecmascript/index.d.ts b/vro-types/ecmascript/index.d.ts index 6588b5bb0..842f85702 100644 --- a/vro-types/ecmascript/index.d.ts +++ b/vro-types/ecmascript/index.d.ts @@ -1,9 +1,14 @@ declare var VROES: VROES; +declare type ModuleErrorHandler = (error: string | Error) => null; + +declare enum DefaultModuleErrorHandlers { SYS_ERROR, SYS_WARN, SYS_INFO, SYS_DEBUG, SILENT, THROW_ERROR } + declare interface VROES { import(...specifiers: string[]): VROESModuleImport; - export(path: string): VROESModuleExport; + export(): VROESModuleExport; load(path: string): VROESModuleDescriptor; + setModuleErrorHandler(eh: DefaultModuleErrorHandlers | ModuleErrorHandler): void; } declare interface VROESModuleDescriptor {