"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
    if (typeof path === "string" && /^\.\.?\//.test(path)) {
        return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
            return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
        });
    }
    return path;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getComponentName = exports.loadedPaths = exports.TRUSTED_RESOURCE_PLUGINS = void 0;
exports.loadComponentDirectories = loadComponentDirectories;
exports.setErrorReporter = setErrorReporter;
exports.loadComponent = loadComponent;
const manageThreads_js_1 = require("../server/threads/manageThreads.js");
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const node_worker_threads_1 = require("node:worker_threads");
const yaml_1 = require("yaml");
const env = __importStar(require("../utility/environment/environmentManager.js"));
const packageUtils_js_1 = require("../utility/packageUtils.js");
const hdbTerms_ts_1 = require("../utility/hdbTerms.js");
const graphqlHandler = __importStar(require("../resources/graphql.js"));
const graphqlQueryHandler = __importStar(require("../server/graphqlQuerying.js"));
const roles = __importStar(require("../resources/roles.js"));
const jsHandler = __importStar(require("../resources/jsResource.js"));
const login = __importStar(require("../resources/login.js"));
const REST = __importStar(require("../server/REST.js"));
const fastifyRoutesHandler = __importStar(require("../server/fastifyRoutes.js"));
const staticFiles = __importStar(require("../server/static.js"));
const loadEnv = __importStar(require("../resources/loadEnv.js"));
const harper_logger_js_1 = __importDefault(require("../utility/logging/harper_logger.js"));
const dataLoader = __importStar(require("../resources/dataLoader.js"));
const manageThreads_js_2 = require("../server/threads/manageThreads.js");
const jsLoader_ts_1 = require("../security/jsLoader.js");
const Server_ts_1 = require("../server/Server.js");
const databases_ts_1 = require("../resources/databases.js");
const socketRouter_ts_1 = require("../server/threads/socketRouter.js");
const environmentManager_js_1 = require("../utility/environment/environmentManager.js");
const operationsServer = __importStar(require("../server/operationsServer.js"));
const auth = __importStar(require("../security/auth.js"));
const mqtt = __importStar(require("../server/mqtt.js"));
const configUtils_js_1 = require("../config/configUtils.js");
const Request_ts_1 = require("../server/serverHelpers/Request.js");
const ErrorResource_ts_1 = require("../resources/ErrorResource.js");
const Scope_ts_1 = require("./Scope.js");
const ApplicationScope_ts_1 = require("./ApplicationScope.js");
const ComponentV1_ts_1 = require("./ComponentV1.js");
const httpComponent = __importStar(require("../server/http.js"));
const index_ts_1 = require("../server/status/index.js");
const index_ts_2 = require("./status/index.js");
const DEFAULT_CONFIG_ts_1 = require("./DEFAULT_CONFIG.js");
const Application_ts_1 = require("./Application.js");
const node_url_1 = require("node:url");
const CF_ROUTES_DIR = (0, configUtils_js_1.getConfigPath)(hdbTerms_ts_1.CONFIG_PARAMS.COMPONENTSROOT);
let loadedComponents = new Map();
let watchesSetup;
let resources;
/**
 * Load all the applications registered in Harper, those in the components directory as well as any directly
 * specified to run
 * @param loadedPluginModules
 * @param loadedResources
 */
function loadComponentDirectories(loadedPluginModules, loadedResources) {
    if (loadedResources)
        resources = loadedResources;
    if (loadedPluginModules)
        loadedComponents = loadedPluginModules;
    const cfsLoaded = [];
    if ((0, node_fs_1.existsSync)(CF_ROUTES_DIR)) {
        const cfFolders = (0, node_fs_1.readdirSync)(CF_ROUTES_DIR, { withFileTypes: true });
        for (const appEntry of cfFolders) {
            if (!appEntry.isDirectory() && !appEntry.isSymbolicLink())
                continue;
            const appName = appEntry.name;
            const appFolder = (0, node_path_1.join)(CF_ROUTES_DIR, appName);
            cfsLoaded.push(loadComponent(appFolder, resources, hdbTerms_ts_1.HDB_ROOT_DIR_NAME, { isRoot: false, autoReload: false, appName }));
        }
    }
    const hdbAppFolder = process.env.RUN_HDB_APP;
    if (hdbAppFolder) {
        cfsLoaded.push(loadComponent(hdbAppFolder, resources, hdbAppFolder, {
            isRoot: false,
            autoReload: Boolean(process.env.DEV_MODE),
            appName: hdbAppFolder,
        }));
    }
    return Promise.all(cfsLoaded).then(() => {
        watchesSetup = true;
    });
}
exports.TRUSTED_RESOURCE_PLUGINS = {
    REST, // for backwards compatibility with older configs
    rest: REST,
    graphql: graphqlQueryHandler,
    graphqlSchema: graphqlHandler,
    roles,
    jsResource: jsHandler,
    fastifyRoutes: fastifyRoutesHandler,
    login,
    static: staticFiles,
    operationsApi: operationsServer,
    customFunctions: {},
    http: httpComponent,
    authentication: auth,
    mqtt,
    loadEnv,
    logging: harper_logger_js_1.default,
    dataLoader,
    /*
    static: ...
    login: ...
     */
};
for (const { name, packageIdentifier } of (0, Application_ts_1.getEnvBuiltInComponents)()) {
    exports.TRUSTED_RESOURCE_PLUGINS[name] = packageIdentifier;
}
const BUILT_INS = Object.keys(exports.TRUSTED_RESOURCE_PLUGINS);
const portsStarted = [];
exports.loadedPaths = new Map();
let errorReporter;
function setErrorReporter(reporter) {
    errorReporter = reporter;
}
let compName;
const getComponentName = () => compName;
exports.getComponentName = getComponentName;
function symlinkHarperModule(componentDirectory) {
    return new Promise((resolve, reject) => {
        const store = index_ts_1.Status.primaryStore;
        // Create timeout to avoid deadlocks
        const timeout = setTimeout(() => {
            store.unlock(componentDirectory);
            reject(new Error('symlinking harperdb module timed out'));
        }, 10_000);
        const callback = () => {
            clearTimeout(timeout);
            resolve();
        };
        const lockAcquired = store.tryLock(componentDirectory, callback);
        if (!lockAcquired) {
            clearTimeout(timeout);
        }
        else {
            try {
                // validate node_modules directory exists
                const nodeModulesDir = (0, node_path_1.join)(componentDirectory, 'node_modules');
                if (!(0, node_fs_1.existsSync)(nodeModulesDir)) {
                    // create it if not
                    (0, node_fs_1.mkdirSync)(nodeModulesDir);
                }
                // validate harper module
                const harperModule = (0, node_path_1.join)(nodeModulesDir, 'harper');
                if ((0, node_fs_1.existsSync)(harperModule)) {
                    if ((0, node_fs_1.realpathSync)(harperModule) !== (0, node_fs_1.realpathSync)(packageUtils_js_1.PACKAGE_ROOT)) {
                        // if it exists but is incorrectly linked, fix it
                        (0, node_fs_1.rmSync)(harperModule, { recursive: true, force: true });
                        // create link to harper module
                        (0, node_fs_1.symlinkSync)(packageUtils_js_1.PACKAGE_ROOT, harperModule, 'dir');
                    }
                }
                else {
                    // create link to harper module
                    (0, node_fs_1.symlinkSync)(packageUtils_js_1.PACKAGE_ROOT, harperModule, 'dir');
                }
                // if there is a harperdb module, fix that too
                const harperdbModule = (0, node_path_1.join)(nodeModulesDir, 'harperdb');
                if ((0, node_fs_1.existsSync)(harperdbModule) && (0, node_fs_1.realpathSync)(harperdbModule) !== (0, node_fs_1.realpathSync)(packageUtils_js_1.PACKAGE_ROOT)) {
                    // if it exists but is incorrectly linked, fix it
                    (0, node_fs_1.rmSync)(harperdbModule, { recursive: true, force: true });
                    // create link to harper module
                    (0, node_fs_1.symlinkSync)(packageUtils_js_1.PACKAGE_ROOT, harperdbModule, 'dir');
                }
                resolve();
            }
            finally {
                // finally release the lock
                store.unlock(componentDirectory);
            }
        }
    });
}
/**
 * This function handles the `handleApplication` call for a plugin in a sequential manner.
 * It ensures the execution of `handleApplication` happens on one thread at a time for a given scope.
 * If the lock cannot be acquired, it waits for the lock to be released and retries.
 * If the lock is not acquired within the specified timeout, it rejects with a timeout error.
 *
 * @param scope
 * @param plugin
 * @returns
 */
function sequentiallyHandleApplication(scope, plugin) {
    return scope.ready.then(async () => {
        // Timeout priority is user config, plugin default, finally 30 seconds
        const timeout = scope.options.get(['timeout']) || plugin.defaultTimeout || 30_000; // default 30 second timeout
        if (typeof timeout !== 'number') {
            throw new Error(`Invalid timeout value for ${scope.pluginName}. Expected a number, received: ${typeof timeout}`);
        }
        let whenResolved, timer;
        const callback = () => {
            clearTimeout(timer);
            whenResolved(sequentiallyHandleApplication(scope, plugin));
        };
        const store = index_ts_1.Status.primaryStore;
        const lockAcquired = store.tryLock(scope.pluginName, callback);
        if (!lockAcquired) {
            return new Promise((resolve, reject) => {
                whenResolved = resolve;
                timer = setTimeout(() => {
                    reject(new Error(`Timeout waiting for lock on ${scope.pluginName}`));
                }, timeout + 5_000); // extra time for lock acquisition
            });
        }
        let loadTimeout;
        try {
            // note that handleApplication can throw sync or async errors, need to run finally block for both
            await Promise.race([
                Promise.resolve(plugin.handleApplication(scope)).then(async () => {
                    // Wait for any initial entry handler loads to complete
                    // This ensures all async operations (like secureImport) finish before the component is marked as loaded
                    await scope.waitForInitialLoads();
                }),
                new Promise((_, reject) => (loadTimeout = setTimeout(() => reject(new Error(`handleApplication timed out after ${timeout}ms for ${scope.pluginName} on behalf of ${scope.appName}`)), timeout))),
            ]);
        }
        finally {
            index_ts_1.Status.primaryStore.unlock(scope.pluginName);
            clearTimeout(loadTimeout);
        }
    });
}
/**
 * Load a component from the specified directory
 * @param componentPath
 * @param resources
 * @param origin
 * @param portsAllowed
 * @param providedLoadedComponents
 */
async function loadComponent(componentDirectory, resources, origin, options = {}) {
    const resolvedFolder = (0, node_fs_1.realpathSync)(componentDirectory);
    if (exports.loadedPaths.has(resolvedFolder))
        return exports.loadedPaths.get(resolvedFolder);
    exports.loadedPaths.set(resolvedFolder, true);
    const { providedLoadedComponents, applicationScope = new ApplicationScope_ts_1.ApplicationScope((0, node_path_1.basename)(componentDirectory), resources, Server_ts_1.server, options.isRoot), isRoot, autoReload, appName, } = options;
    applicationScope.allowedPath ??= (0, node_fs_1.realpathSync)(componentDirectory);
    if (providedLoadedComponents)
        loadedComponents = providedLoadedComponents;
    try {
        let config;
        let configPath = (0, node_path_1.join)(componentDirectory, 'harper-config.yaml'); // look for the specific harperdb-config.yaml first
        if (!(0, node_fs_1.existsSync)(configPath) && (0, node_path_1.join)(componentDirectory, 'harperdb-config.yaml')) {
            configPath = (0, node_path_1.join)(componentDirectory, 'harperdb-config.yaml');
        }
        if ((0, node_fs_1.existsSync)(configPath)) {
            config = isRoot ? (0, configUtils_js_1.getConfigObj)() : (0, yaml_1.parseDocument)((0, node_fs_1.readFileSync)(configPath, 'utf8')).toJSON();
            // if not found, look for the generic config.yaml, the config filename we have historically used, but only if not the root
        }
        else if (!isRoot && (0, node_fs_1.existsSync)((configPath = (0, node_path_1.join)(componentDirectory, 'config.yaml')))) {
            config = (0, yaml_1.parseDocument)((0, node_fs_1.readFileSync)(configPath, 'utf8')).toJSON();
        }
        else {
            config = DEFAULT_CONFIG_ts_1.DEFAULT_CONFIG;
        }
        applicationScope.config ??= config;
        if (!isRoot) {
            try {
                await symlinkHarperModule(componentDirectory);
            }
            catch (error) {
                harper_logger_js_1.default.error('Error symlinking harperdb module', error);
                if (error.code == 'EPERM' && process.platform === 'win32') {
                    harper_logger_js_1.default.error('You may need to enable developer mode in "Settings" / "System" (or "Update & Security") / "For developers", in order to enable symlinks so components can use `import from "harperdb"`');
                }
            }
        }
        const parentCompName = compName;
        const componentFunctionality = {};
        // iterate through the app handlers so they can each do their own loading process
        for (const componentName in config) {
            // For root components, use just the component name
            // For application components, use applicationName.componentName format (directoryName.componentName)
            const componentStatusName = isRoot ? componentName : `${(0, node_path_1.basename)(componentDirectory)}.${componentName}`;
            compName = componentName;
            const componentConfig = config[componentName];
            if (!componentConfig)
                continue;
            // Initialize loading status for all components (applications and extensions)
            index_ts_2.lifecycle.loading(componentStatusName);
            const subApplicationScope = isRoot
                ? new ApplicationScope_ts_1.ApplicationScope(componentName, resources, Server_ts_1.server, exports.TRUSTED_RESOURCE_PLUGINS.hasOwnProperty(componentName))
                : applicationScope;
            let extensionModule;
            const pkg = componentConfig.package;
            const loadComponentOption = componentConfig.loadComponent ?? 'always';
            try {
                if (pkg) {
                    if (loadComponentOption === 'dev-only' && !process.env.DEV_MODE) {
                        index_ts_2.lifecycle.loaded(componentStatusName, `Component '${componentStatusName}' skipped (dev-only)`);
                        continue;
                    }
                    let componentPath = null;
                    if (isRoot) {
                        componentPath = (0, node_path_1.join)(componentDirectory, 'components', componentName);
                    }
                    else {
                        let containerFolder = componentDirectory;
                        componentPath = (0, node_path_1.join)(containerFolder, 'node_modules', componentName);
                        while (!(0, node_fs_1.existsSync)(componentPath)) {
                            containerFolder = (0, node_path_1.dirname)(containerFolder);
                            if (containerFolder.length < (0, environmentManager_js_1.getHdbBasePath)().length) {
                                componentPath = null;
                                break;
                            }
                            componentPath = (0, node_path_1.join)(containerFolder, 'node_modules', componentName);
                        }
                    }
                    if (componentPath) {
                        subApplicationScope.allowedPath ??= (0, node_fs_1.realpathSync)(componentPath);
                        if (!process.env.HARPER_SAFE_MODE) {
                            extensionModule = await loadComponent(componentPath, resources, origin, {
                                isRoot: false,
                                applicationScope: subApplicationScope,
                                autoReload: false,
                                appName: appName || componentName,
                            });
                            componentFunctionality[componentName] = true;
                        }
                    }
                    else if (loadComponentOption === 'if-installed') {
                        index_ts_2.lifecycle.loaded(componentStatusName, `Component '${componentStatusName}' skipped (not installed)`);
                        continue;
                    }
                    else {
                        throw new Error(`Unable to find package ${componentName}:${pkg}`);
                    }
                }
                else {
                    const plugin = exports.TRUSTED_RESOURCE_PLUGINS[componentName];
                    extensionModule =
                        typeof plugin === 'string'
                            ? await import(__rewriteRelativeImportExtension(plugin.startsWith('@/') ? (0, node_url_1.pathToFileURL)((0, node_path_1.join)(packageUtils_js_1.PACKAGE_ROOT, plugin.slice(1))).toString() : plugin))
                            : plugin;
                }
                if (!extensionModule) {
                    // This is an application-only component (no extension module)
                    // Mark it as loaded since it exists in the config
                    index_ts_2.lifecycle.loaded(componentStatusName, `Application component '${componentStatusName}' processed`);
                    continue;
                }
                // our own trusted modules can be directly retrieved from our map, otherwise use the (configurable) secure module loader
                const ensureTable = (options) => {
                    options.origin = origin;
                    return (0, databases_ts_1.table)(options);
                };
                // call the main start hook
                const network = componentConfig.network || ((componentConfig.port || componentConfig.securePort) && componentConfig);
                const securePort = network?.securePort ||
                    // legacy support for switching to securePort
                    (network?.https && network.port);
                const port = !network?.https && network?.port;
                if ('handleApplication' in extensionModule &&
                    ('start' in extensionModule ||
                        'startOnMainThread' in extensionModule ||
                        'handleFile' in extensionModule ||
                        'handleDirectory' in extensionModule ||
                        'setupFile' in extensionModule ||
                        'setupDirectory' in extensionModule)) {
                    const error = new Error(`Plugin ${componentName} is exporting old extension APIs. Remove them.`);
                    index_ts_2.lifecycle.failed(componentStatusName, error, `Component '${componentStatusName}' failed to load`);
                    throw error;
                }
                // New Plugin API (`handleApplication`)
                if (resources.isWorker && extensionModule.handleApplication) {
                    const scope = new Scope_ts_1.Scope(appName || 'harper', componentName, componentDirectory, configPath, applicationScope);
                    (0, manageThreads_js_1.onMessageByType)(hdbTerms_ts_1.ITC_EVENT_TYPES.SHUTDOWN, () => scope.close());
                    await sequentiallyHandleApplication(scope, extensionModule);
                    // Mark component as loaded after successful handleApplication call
                    index_ts_2.lifecycle.loaded(componentStatusName, `Component '${componentStatusName}' loaded successfully`);
                    continue;
                }
                // Old Extension API (`start` or `startOnMainThread`)
                if (!BUILT_INS.includes(componentName) &&
                    ('startOnMainThread' in extensionModule ||
                        'start' in extensionModule ||
                        'handleFile' in extensionModule ||
                        'handleDirectory' in extensionModule ||
                        'setupFile' in extensionModule ||
                        'setupDirectory' in extensionModule)) {
                    harper_logger_js_1.default.warn?.(`Component ${componentName} is using deprecated extension API. Upgrade to the new Plugin API. For more information: https://docs.harperdb.io/docs/reference/components/plugins`);
                }
                if (node_worker_threads_1.isMainThread) {
                    extensionModule =
                        (await extensionModule.startOnMainThread?.({
                            server: Server_ts_1.server,
                            ensureTable,
                            port,
                            securePort,
                            resources,
                            ...componentConfig,
                        })) || extensionModule;
                    if (isRoot && network) {
                        for (const possiblePort of [port, securePort]) {
                            try {
                                if (+possiblePort && !portsStarted.includes(possiblePort)) {
                                    const sessionAffinity = env.get(hdbTerms_ts_1.CONFIG_PARAMS.HTTP_SESSIONAFFINITY);
                                    if (sessionAffinity)
                                        harper_logger_js_1.default.warn('Session affinity is not recommended and may cause memory leaks');
                                    if (sessionAffinity || !Request_ts_1.createReuseportFd) {
                                        // if there is a TCP port associated with the plugin, we set up the routing on the main thread for it
                                        portsStarted.push(possiblePort);
                                        (0, socketRouter_ts_1.startSocketServer)(possiblePort, sessionAffinity);
                                    }
                                }
                            }
                            catch (error) {
                                console.error('Error listening on socket', possiblePort, error, componentName);
                            }
                        }
                    }
                }
                if (resources.isWorker)
                    extensionModule =
                        (await extensionModule.start?.({
                            server: Server_ts_1.server,
                            ensureTable,
                            port,
                            securePort,
                            resources,
                            ...componentConfig,
                        })) || extensionModule;
                loadedComponents.set(extensionModule, true);
                if ((extensionModule.handleFile ||
                    extensionModule.handleDirectory ||
                    extensionModule.setupFile ||
                    extensionModule.setupDirectory) &&
                    componentConfig.files != undefined) {
                    const component = new ComponentV1_ts_1.ComponentV1({
                        config: componentConfig,
                        name: componentName,
                        directory: componentDirectory,
                        module: extensionModule,
                        resources,
                    });
                    componentFunctionality[componentName] = await (0, ComponentV1_ts_1.processResourceExtensionComponent)(component);
                }
                // Mark component as healthy after successful loading
                index_ts_2.lifecycle.loaded(componentStatusName, `Component '${componentStatusName}' loaded successfully`);
            }
            catch (error) {
                error.message = `Could not load component '${componentName}' for application '${(0, node_path_1.basename)(componentDirectory)}' due to: ${error.message}`;
                errorReporter?.(error);
                ((0, manageThreads_js_2.getWorkerIndex)() === 0 ? console : harper_logger_js_1.default).error(error);
                resources.set(componentConfig.path || '/', new ErrorResource_ts_1.ErrorResource(error), null, true);
                index_ts_2.lifecycle.failed(componentStatusName, error, `Could not load component '${componentStatusName}'`);
            }
        }
        compName = parentCompName;
        // Auto restart threads on changes to any app folder. TODO: Make this configurable
        if (node_worker_threads_1.isMainThread && !watchesSetup && autoReload) {
            (0, manageThreads_js_2.watchDir)(componentDirectory, async () => {
                return loadComponentDirectories(); // return the promise
            });
        }
        if ((config.extensionModule || config.pluginModule) && (!node_worker_threads_1.isMainThread || config.runOnMainThread)) {
            const extensionModule = await (0, jsLoader_ts_1.scopedImport)((0, node_path_1.join)(componentDirectory, config.extensionModule || config.pluginModule), applicationScope);
            exports.loadedPaths.set(resolvedFolder, extensionModule);
            return extensionModule;
        }
        const componentFunctionalityValues = Object.values(componentFunctionality);
        if (componentFunctionalityValues.length > 0 &&
            componentFunctionalityValues.every((functionality) => !functionality) &&
            resources.isWorker) {
            const errorMessage = `${componentDirectory} did not load any modules, resources, or files, is this a valid component?`;
            errorReporter?.(new Error(errorMessage));
            ((0, manageThreads_js_2.getWorkerIndex)() === 0 ? console : harper_logger_js_1.default).error(errorMessage);
            index_ts_2.lifecycle.failed((0, node_path_1.basename)(componentDirectory), errorMessage);
        }
        for (const [componentName, functionality] of Object.entries(componentFunctionality)) {
            if (!functionality)
                harper_logger_js_1.default.warn(`Component ${componentName} from (${(0, node_path_1.basename)(componentDirectory)}) did not load any functionality.`);
        }
    }
    catch (error) {
        console.error(`Could not load application directory ${componentDirectory}`, error);
        error.message = `Could not load application due to ${error.message}`;
        errorReporter?.(error);
        resources.set('', new ErrorResource_ts_1.ErrorResource(error));
    }
}
//# sourceMappingURL=componentLoader.js.map