/**
 * Gets a singleton. Returns null if the singleton doesn't yet exist.
 *
 * Use singleton() or singletonFactory() to lazily create singletons.
 */
export const getSingleton = (key) => {
    const SINGLETON_KEY = Symbol.for(`com.finsemble.singleton.${key}`);
    const globalSymbols = Object.getOwnPropertySymbols(global);
    const singletonExists = globalSymbols.indexOf(SINGLETON_KEY) > -1;
    if (!singletonExists)
        return null;
    return global[SINGLETON_KEY];
};
/**
 * This code creates a *true* singleton that will never be duplicated across webpack module loads.
 * If the singleton already exists then it is returned, otherwise a new one is created from the
 * provided constructor.
 *
 * Two situations that force the need for true singletons are preloads and storage adapters. In
 * both cases, code is being inserted (as a preload or a <script>) that contains the same modules
 * that a developer might otherwise *import* into their own code base. For instance, the preload
 * contains a router client, so if you also import a router client then you'll have two separately
 * webpacked router client modules running in parallel. The true singleton pattern ensures that
 * both of those modules are using the same underlying instance, otherwise one of the instances
 * will probably be non-operational.
 *
 * In general, avoid singletons (the module instance pattern) altogether. Instead, pass instances into
 * functions.
 *
 * // dangerous
 * import RouterClient from "./RouterClient";
 *
 * blah() {
 *    RouterClient.send("blah")
 * }
 *
 * // safe
 *
 * blah(routerClient : RouterClent){
 *    RouterClient.send("blah")
 * }
 *
 * // safe
 *
 * class Blah {
 *    static routerClient: RouterClient;
 *    constructor(routerClient : RouterClient){
 *        this.routerClient = routerClient;
 *    }
 *
 *    blah(){
 *       this.routerClient.send("blah")
 *    }
 * }
 *
 * Internals:
 *
 * `Symbol.for("blah")` is a safe equivalent to `window.blah =`.
 * It won't appear on the window object and it won't conflict with other applications.
 *
 * Recipe:
 * https://derickbailey.com/2016/03/09/creating-a-true-singleton-in-node-js-with-es6-symbols/
 *
 * Typescript for a constructor:
 * https://stackoverflow.com/questions/39392853/is-there-a-type-for-class-in-typescript-and-does-any-include-it
 *
 * @param key A string key that is unique within the application.
 * @param Ctor A constructor class.
 * @param [...args] Any remaining parameters will be passed to the constructor as arguments
 *
 * @example
 * if(!MyClass.instance) MyClass.instance = singleton("MyClassKey", MyClass);
 */
export const singleton = (key, Ctor, ...args) => {
    const result = getSingleton(key);
    if (!result) {
        const SINGLETON_KEY = Symbol.for(`com.finsemble.singleton.${key}`);
        global[SINGLETON_KEY] = new Ctor(...args);
        return global[SINGLETON_KEY];
    }
    return result;
};
/**
 * Behaves just like singleton except that it accepts a function instead of a constructor to
 * create the instance
 */
export const singletonFactory = (key, func, ...args) => {
    const result = getSingleton(key);
    if (!result) {
        const SINGLETON_KEY = Symbol.for(`com.finsemble.singleton.${key}`);
        global[SINGLETON_KEY] = func(...args);
        return global[SINGLETON_KEY];
    }
    return result;
};
//# sourceMappingURL=Singleton.js.map