"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OldManifestStoreShim = exports.ManifestStore = void 0;
var tslib_1 = require("tslib");
var sp_core_library_1 = require("@microsoft/sp-core-library");
var sp_telemetry_1 = require("@ms/sp-telemetry");
var ComponentStore_1 = tslib_1.__importDefault(require("../../stores/ComponentStore"));
var componentConstants_1 = require("../../utilities/componentConstants");
var flights_1 = require("../common/flights");
var killSwitches_1 = require("../common/killSwitches");
var normalize_1 = require("../common/normalize");
var ManifestProvider_1 = require("./ManifestProvider");
/**
 * Manages all the manifests on the current page and provides access to fetching manifests
 * from the server.
 */
var ManifestStore = /** @class */ (function () {
    function ManifestStore(_provider) {
        this._provider = _provider;
        this._store = new Map();
        this._auditLog = [];
    }
    /**
     * Add a collection of manifests to the store.
     *
     * @param manifests - Array of manifests
     * @param shouldOverwrite - Should the manifest be added if there is an existing manifest
     * @param shouldPin - Should the manifest be pinned
     * @param isDebug - Are the manifests debug
     *
     * @returns A boolean indicating if all the manifests were added or not
     */
    ManifestStore.prototype.addManifests = function (manifests, shouldOverwrite, shouldPin, isDebug) {
        var isSuccess = true;
        for (var _i = 0, manifests_1 = manifests; _i < manifests_1.length; _i++) {
            var m = manifests_1[_i];
            var result = this.addManifest(m, shouldOverwrite, shouldPin, isDebug);
            isSuccess = isSuccess && result;
        }
        return isSuccess;
    };
    /**
     * Add a manifest to the store.
     *
     * @param manifest - manifest to add
     * @param shouldOverwrite - Should the manifest be added if there is an existing manifest
     * @param shouldPin - Should the manifest be pinned
     * @param isDebug - Is the manifest debug
     *
     * @returns A boolean indicating if the manifest was added or not
     */
    ManifestStore.prototype.addManifest = function (manifest, shouldOverwrite, shouldPin, isDebug) {
        var id = manifest.id, isInternal = manifest.isInternal, version = manifest.version;
        var queryParameters = new URL(window.location.href).searchParams;
        var isReactProfilingQueryStringEnabled = (queryParameters.get('enableReactDiagnostics') || '').toLowerCase() === 'true';
        var isReactDebugQueryStringEnabled = (queryParameters.get('enableReactDebug') || '').toLowerCase() === 'true';
        if (isReactDebugQueryStringEnabled) {
            augmentManifestForDebugMode(manifest);
        }
        else if (isReactProfilingQueryStringEnabled) {
            augmentManifestForProfiling(manifest);
        }
        var storeEntry = this._store.get(id);
        if (storeEntry === null || storeEntry === void 0 ? void 0 : storeEntry.isPinned) {
            return false;
        }
        var normalizedId = (0, normalize_1.normalizeComponentId)(id, version);
        var existingManifest = storeEntry === null || storeEntry === void 0 ? void 0 : storeEntry.manifests.get(normalizedId);
        if (existingManifest && !shouldOverwrite) {
            return false;
        }
        if (!storeEntry) {
            storeEntry = {
                manifests: new Map()
            };
            this._store.set(id, storeEntry);
        }
        if (shouldPin ||
            (!sp_core_library_1._SPFlight.isDebugFlightEnabled &&
                isInternal &&
                id !== componentConstants_1.reactComponentId &&
                id !== componentConstants_1.reactDomComponentId &&
                id !== componentConstants_1.tslibComponentId)) {
            storeEntry.isPinned = true;
        }
        var newEntry = { manifest: manifest };
        if (isDebug) {
            newEntry.isDebug = true;
        }
        storeEntry.manifests.set(normalizedId, newEntry);
        this._auditLog.push("addManifest: ".concat(normalizedId));
        return true;
    };
    ManifestStore.prototype.refreshWebPartManifests = function (manifests) {
        var _this = this;
        // 1) remove webpart manifests that aren't present in the new list
        this._store.forEach(function (sEntry, sKey) {
            sEntry.manifests.forEach(function (mEntry, mKey) {
                if (mEntry.manifest.componentType === 'WebPart' &&
                    !mEntry.manifest.isInternal &&
                    !mEntry.manifest._isDebug &&
                    !manifests.some(function (m) { return m.id === mEntry.manifest.id; })) {
                    // If there is only one manifest delete the whole entry
                    if (sEntry.manifests.size === 1) {
                        _this._store.delete(sKey);
                        _this._auditLog.push("clearManifests: ".concat(sKey));
                    }
                    else {
                        sEntry.manifests.delete(mKey);
                        _this._auditLog.push("clearManifests: ".concat(mKey));
                    }
                }
            });
        });
        // 2 add the new list
        this.addManifests(manifests, false, false, false);
    };
    /**
     * Delete all non-pinned manifests in the store.
     */
    ManifestStore.prototype.clearManifests = function () {
        var _this = this;
        this._store.forEach(function (sEntry, sKey) {
            if (!sEntry.isPinned &&
                sKey !== componentConstants_1.reactComponentId &&
                sKey !== componentConstants_1.reactDomComponentId &&
                sKey !== componentConstants_1.tslibComponentId) {
                sEntry.manifests.forEach(function (mEntry, mKey) {
                    if (!mEntry.isDebug) {
                        // If there is only one manifest delete the whole entry
                        if (sEntry.manifests.size === 1) {
                            _this._store.delete(sKey);
                            _this._auditLog.push("clearManifests: ".concat(sKey));
                        }
                        else {
                            sEntry.manifests.delete(mKey);
                            _this._auditLog.push("clearManiefsts: ".concat(mKey));
                        }
                    }
                });
            }
        });
    };
    /**
     * Delete a manifest from the store.
     *
     * @param id - Manifest id
     * @param version - Manifest version
     */
    ManifestStore.prototype.deleteManifest = function (id, version) {
        var storeEntry = this._store.get(id);
        if (!storeEntry || storeEntry.isPinned) {
            return false;
        }
        if (storeEntry.manifests.size === 1) {
            this._auditLog.push("deleteManifest: ".concat(id));
            return this._store.delete(id);
        }
        else {
            var cid = (0, normalize_1.normalizeComponentId)(id, version);
            this._auditLog.push("deleteManifest: ".concat(cid));
            return storeEntry.manifests.delete(cid);
        }
    };
    /**
     * Fetch manifests from the server.
     *
     * @param requests - Array of manifest requests
     *
     * @returns A promise resolving to an array of component manifests
     */
    ManifestStore.prototype.fetchManifests = function (requests) {
        var _this = this;
        return this._provider.fetchManifests(requests).then(function (manifests) {
            _this.addManifests(manifests, false, false, false);
            return manifests;
        });
    };
    /**
     *
     * @deprecated This is only used in debug scenarios. Needs further refactoring.
     */
    ManifestStore.prototype.getAllStoreEntries = function () {
        var entries = [];
        this._store.forEach(function (sEntry) {
            entries.push(sEntry);
        });
        return entries;
    };
    /**
     * Return all manifests in the store.
     *
     * @param onlyUnloaded -  Only return the set of non-debug manifests where the component has not been loaded.
     *
     * @returns An array of component manifests.
     */
    ManifestStore.prototype.getAllManifests = function (onlyUnloaded) {
        var manifests = [];
        this._store.forEach(function (sEntry) {
            sEntry.manifests.forEach(function (mEntry) {
                if (!onlyUnloaded ||
                    !ComponentStore_1.default.instance.tryGetComponentReference(mEntry.manifest.id, mEntry.manifest.version)) {
                    if (!onlyUnloaded) {
                        manifests.push(mEntry.manifest);
                    }
                    else if (!mEntry.isDebug) {
                        // If we're only fetching unloaded manifests, then only return non-debug manifests
                        manifests.push(mEntry.manifest);
                    }
                }
            });
        });
        return manifests;
    };
    /**
     * Get one manifest from the store.
     *
     * @param id - Manifest id
     * @param version - Manifest version
     *
     * @remarks If the version is not provided and there is no debug manifest, then if,
     *   and only if, one manifest exists return it. If multiple versions exist and there is
     *   a debug manifest, return it. Otherwise, an error is thrown. If the version is
     *   provided and multiple manifests exist, return the highest compatible version.
     *
     * @returns If the requested manifest exists, return it
     */
    ManifestStore.prototype.getManifest = function (id, version) {
        var storeEntry = this._store.get(id);
        var manifest;
        if (storeEntry) {
            var manifests = storeEntry.manifests;
            if (!version) {
                if (manifests.size > 1) {
                    manifests.forEach(function (mEntry) {
                        // If multiple manifests, then return the debug manifest
                        if (mEntry.isDebug) {
                            manifest = mEntry.manifest;
                        }
                    });
                    // If no debug manifest, then a version must always be requested with multiple manifests
                    if (!manifest) {
                        throw new Error("Multiple versions of ".concat(id));
                    }
                }
                else {
                    // We should only loop once but IE11 lacks support for Array.from and Map.values
                    manifests.forEach(function (mEntry) {
                        manifest = mEntry.manifest;
                    });
                }
            }
            else {
                var reqVersion_1 = sp_core_library_1.Version.parse(version);
                var originalReqVersion_1 = sp_core_library_1.Version.parse(version);
                var foundDebugVersion_1 = false;
                var storeVersions_1 = [];
                manifests.forEach(function (mEntry) {
                    var entryVersion = mEntry.manifest.version;
                    var mVersion = sp_core_library_1.Version.parse(entryVersion);
                    storeVersions_1.push("".concat(entryVersion));
                    if ((mEntry.isDebug &&
                        (foundDebugVersion_1
                            ? isVersionCompatible(mVersion, reqVersion_1)
                            : isVersionCompatible(mVersion, originalReqVersion_1))) ||
                        (!mEntry.isDebug && !foundDebugVersion_1 && isVersionCompatible(mVersion, reqVersion_1))) {
                        reqVersion_1 = mVersion;
                        manifest = mEntry.manifest;
                        if (mEntry.isDebug && !foundDebugVersion_1) {
                            foundDebugVersion_1 = true;
                        }
                    }
                });
                if (!manifest) {
                    var monitor = new sp_telemetry_1._QosMonitor('ManifestStore.getManifestFailures');
                    // TODO: demyren: move service worker version to createLoggerContext
                    var serviceWorkerVersion = " Service worker version: ".concat(window.__swstats ? window.__swstats.version : '(none)', ",DurableN1=").concat((0, flights_1.isDurableN1Enabled)());
                    var error = new Error("Cannot find manifest for ".concat(id, "_").concat(version, ". Store had [").concat(storeVersions_1.join(', '), "].").concat(serviceWorkerVersion));
                    monitor.writeUnexpectedFailure(undefined, error, { addedManifestsAuditLog: this._auditLog });
                    if (storeEntry.isPinned && !(0, killSwitches_1.isVersionMismatchThrowKSActivated)()) {
                        // If it's pinned and there isn't a compatible version then we this is an n+1 version mismatch
                        throw error;
                    }
                }
            }
        }
        return manifest;
    };
    return ManifestStore;
}());
exports.ManifestStore = ManifestStore;
function isVersionCompatible(mVersion, reqVersion) {
    return (mVersion.greaterThan(reqVersion) && mVersion.satisfies(reqVersion)) || mVersion.equals(reqVersion);
}
/**
 * Remap things that depend on react-dom to react-dom-profiling if react-dom profiling is enabled
 *
 * @param manifest - the manifest to augment
 */
function augmentManifestForProfiling(manifest) {
    if (manifest.loaderConfig && manifest.loaderConfig.scriptResources) {
        for (var resourceId in manifest.loaderConfig.scriptResources) {
            if (manifest.loaderConfig.scriptResources.hasOwnProperty(resourceId)) {
                var resource = manifest.loaderConfig.scriptResources[resourceId];
                if (resource.type === 'component') {
                    var componentResource = resource;
                    if (componentResource.id === componentConstants_1.reactDomComponentId) {
                        componentResource.id = componentConstants_1.reactDomProfilingComponentId;
                    }
                }
            }
        }
    }
}
/**
 * Remap things that depend on react and react-dom if react-dom debug mode is enabled
 *
 * @param manifest - the manifest to augment
 */
function augmentManifestForDebugMode(manifest) {
    if (manifest.loaderConfig && manifest.loaderConfig.scriptResources) {
        for (var resourceId in manifest.loaderConfig.scriptResources) {
            if (manifest.loaderConfig.scriptResources.hasOwnProperty(resourceId)) {
                var resource = manifest.loaderConfig.scriptResources[resourceId];
                if (resource.type === 'component') {
                    console.log(manifest.alias, 'Switched to using debug react');
                    var componentResource = resource;
                    if (componentResource.id === componentConstants_1.reactDomComponentId) {
                        componentResource.id = componentConstants_1.reactDomDebugComponentId;
                    }
                    else if (componentResource.id === componentConstants_1.reactComponentId) {
                        componentResource.id = componentConstants_1.reactDebugComponentId;
                    }
                }
            }
        }
    }
}
/**
 * Shim between the tiny ManifestStore and the old ManifestStore.
 *
 * To be deleted when isTinyLoaderKSActivated is removed.
 */
var OldManifestStoreShim = /** @class */ (function () {
    function OldManifestStoreShim(serviceScope, webAbsoluteUrl, buildNumber) {
        this.buildNumber = buildNumber;
        this._impl = new ManifestStore(new ManifestProvider_1.ManifestProvider(serviceScope, webAbsoluteUrl, buildNumber));
    }
    OldManifestStoreShim.prototype.registerPreloadedManifests = function (preloadedData) {
        var manifests = preloadedData.manifests;
        this._impl.addManifests(manifests, true, false, false);
        // tslint:disable-next-line:no-string-literal
        this._impl.addManifests(window['g_webPartManifests'] || [], true, false, false);
    };
    OldManifestStoreShim.prototype.registerDebugManifests = function (manifests) {
        this._impl.addManifests(manifests, true, false, true);
    };
    OldManifestStoreShim.prototype.tryGetManifest = function (id, version, shouldLog) {
        if (shouldLog === void 0) { shouldLog = true; }
        try {
            return this._impl.getManifest(id, version);
        }
        catch (e) {
            return undefined;
        }
    };
    OldManifestStoreShim.prototype.getManifest = function (id, version) {
        var m = this._impl.getManifest(id, version);
        if (m) {
            return m;
        }
        else {
            throw new Error("Manifest not found ".concat((0, normalize_1.normalizeComponentId)(id, version || '')));
        }
    };
    OldManifestStoreShim.prototype.getRegisteredManifests = function (onlyUnloaded) {
        return this._impl.getAllManifests(onlyUnloaded);
    };
    OldManifestStoreShim.prototype.replaceManifests = function (manifests) {
        this._impl.clearManifests();
        this._impl.addManifests(manifests, false, false, false);
    };
    OldManifestStoreShim.prototype._getManifestMap = function () {
        var retVal = new Map();
        this._impl._store.forEach(function (sEntry) {
            sEntry.manifests.forEach(function (mEntry, mEntryKey) {
                var isDebug = mEntry.isDebug, _a = mEntry.manifest, id = _a.id, version = _a.version;
                var val = { id: id, version: version };
                if (isDebug) {
                    val.debugManifest = mEntry.manifest;
                }
                else {
                    val.manifest = mEntry.manifest;
                }
                retVal.set(mEntryKey, val);
            });
        });
        return retVal;
    };
    OldManifestStoreShim.prototype.registerManifests = function (manifests, overwriteExisting) {
        this._impl.addManifests(manifests, overwriteExisting, false, false);
    };
    OldManifestStoreShim.prototype._pinManifest = function (componentId) {
        var entry = this._impl._store.get(componentId);
        if (entry) {
            entry.isPinned = true;
        }
    };
    OldManifestStoreShim.prototype.requestManifest = function (id, version) {
        var _this = this;
        var manifest = this._impl.getManifest(id, version);
        if (manifest) {
            return Promise.resolve(manifest);
        }
        else {
            // Only log on failures
            var monitor = new sp_telemetry_1._QosMonitor('ManifestStore.RequestManifest');
            monitor.writeUnexpectedFailure(undefined, new Error("Manifest ".concat(id, "_").concat(version, " not in store")));
            return this._impl.fetchManifests([{ id: id, version: version }]).then(function () {
                return _this._impl.getManifest(id, version);
            });
        }
    };
    OldManifestStoreShim.prototype.requestManifests = function (requests) {
        return this._impl.fetchManifests(requests);
    };
    OldManifestStoreShim.prototype.refreshWebPartManifests = function (manifests) {
        this._impl.refreshWebPartManifests(manifests);
    };
    return OldManifestStoreShim;
}());
exports.OldManifestStoreShim = OldManifestStoreShim;
//# sourceMappingURL=ManifestStore.js.map