var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _GlobalStore_locks, _GlobalStore_subscribers;
/* eslint-disable class-methods-use-this */
/**
 * Global state store. Acts as a wrapper around localStorage.
 *
 * Supports a simple key-value store with subscriptions and async value replacement,
 * while ensuring that only one replacement is in progress at a time.
 */
class GlobalStore {
    constructor() {
        /**
         * Locks for each key to ensure only one replacement is in progress at a time.
         * Each promise represents an ongoing replacement which will resolve to the new value or reject if replacement fails.
         */
        _GlobalStore_locks.set(this, void 0);
        /**
         * Subscribers for each key.
         */
        _GlobalStore_subscribers.set(this, void 0);
        __classPrivateFieldSet(this, _GlobalStore_locks, {}, "f");
        __classPrivateFieldSet(this, _GlobalStore_subscribers, {}, "f");
    }
    /**
     * Clear the store.
     * Removes all values and subscribers.
     */
    clear() {
        __classPrivateFieldSet(this, _GlobalStore_locks, {}, "f");
        __classPrivateFieldSet(this, _GlobalStore_subscribers, {}, "f");
    }
    /**
     * Get the value for a key.
     * If the value is being replaced, the promise for the new value is returned.
     *
     * @param key - key to get value for
     */
    getValue(key) {
        return __awaiter(this, void 0, void 0, function* () {
            if (__classPrivateFieldGet(this, _GlobalStore_locks, "f")[key]) {
                return __classPrivateFieldGet(this, _GlobalStore_locks, "f")[key];
            }
            return localStorage.getItem(key);
        });
    }
    /**
     * Get the value for a key synchronously.
     * Even if the value is being replaced, the current value is returned.
     *
     * @param key - key to get value for
     */
    getValueSync(key) {
        return localStorage.getItem(key);
    }
    /**
     * Set the value for a key.
     *
     * @param key - key to set value for
     * @param value - value to set
     */
    setValue(key, value) {
        if (value === null) {
            localStorage.removeItem(key);
        }
        else {
            localStorage.setItem(key, value);
        }
        // Notify any local subscribers of a change in the value
        if (__classPrivateFieldGet(this, _GlobalStore_subscribers, "f")[key]) {
            __classPrivateFieldGet(this, _GlobalStore_subscribers, "f")[key].forEach((cb) => cb(value));
        }
    }
    /**
     * Replace the value for a key.
     * If the value is being replaced, the promise for the new value is returned.
     *
     * @param key - key to replace value for
     * @param fn - function to get the new value
     */
    replaceValue(key, fn) {
        return __awaiter(this, void 0, void 0, function* () {
            // If there's already a replacement in progress, return the promise of the ongoing replacement
            if (__classPrivateFieldGet(this, _GlobalStore_locks, "f")[key]) {
                return __classPrivateFieldGet(this, _GlobalStore_locks, "f")[key];
            }
            // Create a new Promise to resolve to the replaced value
            // TODO: this can be Promise.withResolvers once it's more widely supported
            let unlock;
            let unlockError;
            const lockPromise = new Promise((resolve, reject) => {
                unlock = resolve;
                unlockError = reject;
            });
            // Store it for the given key so we can resolve other replacements/retrievals to the same promise
            __classPrivateFieldGet(this, _GlobalStore_locks, "f")[key] = lockPromise;
            let result;
            try {
                // Run the provided function to get the new value
                result = yield fn();
                // On success - set the value (notifying subscribers) and resolve the 'lock' promise.
                // This resolves both the caller and other ongoing requests blocking on the lock to the new value.
                this.setValue(key, result);
                unlock(result);
            }
            catch (e) {
                // On error - error out the 'lock' promise.
                // This errors both the caller and other ongoing requests blocking on the lock.
                unlockError(e);
            }
            finally {
                // Clear the lock for the key so future replaceValue calls call the `fn` again
                delete __classPrivateFieldGet(this, _GlobalStore_locks, "f")[key];
            }
            return lockPromise;
        });
    }
    /**
     * Subscribe to changes to a key.
     *
     * @param key - key to subscribe to changes to
     * @param callback - callback invoked when the value is updated
     */
    subscribe(key, callback) {
        if (!__classPrivateFieldGet(this, _GlobalStore_subscribers, "f")[key]) {
            __classPrivateFieldGet(this, _GlobalStore_subscribers, "f")[key] = [];
        }
        // create a subscription scoped to the key
        const subFunc = (e) => {
            if (e.storageArea === localStorage && e.key === key) {
                callback(e.newValue);
            }
        };
        // Add the callback to the local subscribers list
        // The storage event only fires when localStorage is changed from another document so we need to track changes
        // locally as well.
        __classPrivateFieldGet(this, _GlobalStore_subscribers, "f")[key].push(callback);
        // Add a listener to the storage event for changes coming from other tabs
        window.addEventListener('storage', subFunc);
        // Cleanup the subscriptions
        return () => {
            var _a;
            window.removeEventListener('storage', subFunc);
            (_a = __classPrivateFieldGet(this, _GlobalStore_subscribers, "f")[key]) === null || _a === void 0 ? void 0 : _a.splice(__classPrivateFieldGet(this, _GlobalStore_subscribers, "f")[key].findIndex((val) => val === callback), 1);
        };
    }
}
_GlobalStore_locks = new WeakMap(), _GlobalStore_subscribers = new WeakMap();
const store = new GlobalStore();
export default store;
//# sourceMappingURL=global-state-store.js.map