UNPKG

matrix-react-sdk

Version:
126 lines (114 loc) 13.9 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.Singleflight = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _maps = require("./maps"); /* Copyright 2024 New Vector Ltd. Copyright 2021 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ // Inspired by https://pkg.go.dev/golang.org/x/sync/singleflight const keyMap = new _maps.EnhancedMap(); /** * Access class to get a singleflight context. Singleflights execute a * function exactly once, unless instructed to forget about a result. * * Typically this is used to de-duplicate an action, such as a save button * being pressed, without having to track state internally for an operation * already being in progress. This doesn't expose a flag which can be used * to disable a button, however it would be capable of returning a Promise * from the first call. * * The result of the function call is cached indefinitely, just in case a * second call comes through late. There are various functions named "forget" * to have the cache be cleared of a result. * * Singleflights in our use case are tied to an instance of something, combined * with a string key to differentiate between multiple possible actions. This * means that a "save" key will be scoped to the instance which defined it and * not leak between other instances. This is done to avoid having to concatenate * variables to strings to essentially namespace the field, for most cases. */ class Singleflight { constructor() {} /** * A void marker to help with returning a value in a singleflight context. * If your code doesn't return anything, return this instead. */ /** * Acquire a singleflight context. * @param {Object} instance An instance to associate the context with. Can be any object. * @param {string} key A string key relevant to that instance to namespace under. * @returns {SingleflightContext} Returns the context to execute the function. */ static for(instance, key) { if (!instance || !key) throw new Error("An instance and key must be supplied"); return new SingleflightContext(instance, key); } /** * Forgets all results for a given instance. * @param {Object} instance The instance to forget about. */ static forgetAllFor(instance) { keyMap.delete(instance); } /** * Forgets all cached results for all instances. Intended for use by tests. */ static forgetAll() { for (const k of keyMap.keys()) { keyMap.remove(k); } } } exports.Singleflight = Singleflight; (0, _defineProperty2.default)(Singleflight, "Void", Symbol("void")); class SingleflightContext { constructor(instance, key) { this.instance = instance; this.key = key; } /** * Forget this particular instance and key combination, discarding the result. */ forget() { const map = keyMap.get(this.instance); if (!map) return; map.remove(this.key); if (!map.size) keyMap.remove(this.instance); } /** * Execute a function. If a result is already known, that will be returned instead * of executing the provided function. However, if no result is known then the function * will be called, with its return value cached. The function must return a value * other than `undefined` - take a look at Singleflight.Void if you don't have a return * to make. * * Note that this technically allows the caller to provide a different function each time: * this is largely considered a bad idea and should not be done. Singleflights work off the * premise that something needs to happen once, so duplicate executions will be ignored. * * For ideal performance and behaviour, functions which return promises are preferred. If * a function is not returning a promise, it should return as soon as possible to avoid a * second call potentially racing it. The promise returned by this function will be that * of the first execution of the function, even on duplicate calls. * @param {Function} fn The function to execute. * @returns The recorded value. */ do(fn) { const map = keyMap.getOrCreate(this.instance, new _maps.EnhancedMap()); // We have to manually getOrCreate() because we need to execute the fn let val = map.get(this.key); if (val === undefined) { val = fn(); map.set(this.key, val); } return val; } } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbWFwcyIsInJlcXVpcmUiLCJrZXlNYXAiLCJFbmhhbmNlZE1hcCIsIlNpbmdsZWZsaWdodCIsImNvbnN0cnVjdG9yIiwiZm9yIiwiaW5zdGFuY2UiLCJrZXkiLCJFcnJvciIsIlNpbmdsZWZsaWdodENvbnRleHQiLCJmb3JnZXRBbGxGb3IiLCJkZWxldGUiLCJmb3JnZXRBbGwiLCJrIiwia2V5cyIsInJlbW92ZSIsImV4cG9ydHMiLCJfZGVmaW5lUHJvcGVydHkyIiwiZGVmYXVsdCIsIlN5bWJvbCIsImZvcmdldCIsIm1hcCIsImdldCIsInNpemUiLCJkbyIsImZuIiwiZ2V0T3JDcmVhdGUiLCJ2YWwiLCJ1bmRlZmluZWQiLCJzZXQiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvU2luZ2xlZmxpZ2h0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qXG5Db3B5cmlnaHQgMjAyNCBOZXcgVmVjdG9yIEx0ZC5cbkNvcHlyaWdodCAyMDIxIFRoZSBNYXRyaXgub3JnIEZvdW5kYXRpb24gQy5JLkMuXG5cblNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBR1BMLTMuMC1vbmx5IE9SIEdQTC0zLjAtb25seVxuUGxlYXNlIHNlZSBMSUNFTlNFIGZpbGVzIGluIHRoZSByZXBvc2l0b3J5IHJvb3QgZm9yIGZ1bGwgZGV0YWlscy5cbiovXG5cbmltcG9ydCB7IEVuaGFuY2VkTWFwIH0gZnJvbSBcIi4vbWFwc1wiO1xuXG4vLyBJbnNwaXJlZCBieSBodHRwczovL3BrZy5nby5kZXYvZ29sYW5nLm9yZy94L3N5bmMvc2luZ2xlZmxpZ2h0XG5cbmNvbnN0IGtleU1hcCA9IG5ldyBFbmhhbmNlZE1hcDxPYmplY3QsIEVuaGFuY2VkTWFwPHN0cmluZywgdW5rbm93bj4+KCk7XG5cbi8qKlxuICogQWNjZXNzIGNsYXNzIHRvIGdldCBhIHNpbmdsZWZsaWdodCBjb250ZXh0LiBTaW5nbGVmbGlnaHRzIGV4ZWN1dGUgYVxuICogZnVuY3Rpb24gZXhhY3RseSBvbmNlLCB1bmxlc3MgaW5zdHJ1Y3RlZCB0byBmb3JnZXQgYWJvdXQgYSByZXN1bHQuXG4gKlxuICogVHlwaWNhbGx5IHRoaXMgaXMgdXNlZCB0byBkZS1kdXBsaWNhdGUgYW4gYWN0aW9uLCBzdWNoIGFzIGEgc2F2ZSBidXR0b25cbiAqIGJlaW5nIHByZXNzZWQsIHdpdGhvdXQgaGF2aW5nIHRvIHRyYWNrIHN0YXRlIGludGVybmFsbHkgZm9yIGFuIG9wZXJhdGlvblxuICogYWxyZWFkeSBiZWluZyBpbiBwcm9ncmVzcy4gVGhpcyBkb2Vzbid0IGV4cG9zZSBhIGZsYWcgd2hpY2ggY2FuIGJlIHVzZWRcbiAqIHRvIGRpc2FibGUgYSBidXR0b24sIGhvd2V2ZXIgaXQgd291bGQgYmUgY2FwYWJsZSBvZiByZXR1cm5pbmcgYSBQcm9taXNlXG4gKiBmcm9tIHRoZSBmaXJzdCBjYWxsLlxuICpcbiAqIFRoZSByZXN1bHQgb2YgdGhlIGZ1bmN0aW9uIGNhbGwgaXMgY2FjaGVkIGluZGVmaW5pdGVseSwganVzdCBpbiBjYXNlIGFcbiAqIHNlY29uZCBjYWxsIGNvbWVzIHRocm91Z2ggbGF0ZS4gVGhlcmUgYXJlIHZhcmlvdXMgZnVuY3Rpb25zIG5hbWVkIFwiZm9yZ2V0XCJcbiAqIHRvIGhhdmUgdGhlIGNhY2hlIGJlIGNsZWFyZWQgb2YgYSByZXN1bHQuXG4gKlxuICogU2luZ2xlZmxpZ2h0cyBpbiBvdXIgdXNlIGNhc2UgYXJlIHRpZWQgdG8gYW4gaW5zdGFuY2Ugb2Ygc29tZXRoaW5nLCBjb21iaW5lZFxuICogd2l0aCBhIHN0cmluZyBrZXkgdG8gZGlmZmVyZW50aWF0ZSBiZXR3ZWVuIG11bHRpcGxlIHBvc3NpYmxlIGFjdGlvbnMuIFRoaXNcbiAqIG1lYW5zIHRoYXQgYSBcInNhdmVcIiBrZXkgd2lsbCBiZSBzY29wZWQgdG8gdGhlIGluc3RhbmNlIHdoaWNoIGRlZmluZWQgaXQgYW5kXG4gKiBub3QgbGVhayBiZXR3ZWVuIG90aGVyIGluc3RhbmNlcy4gVGhpcyBpcyBkb25lIHRvIGF2b2lkIGhhdmluZyB0byBjb25jYXRlbmF0ZVxuICogdmFyaWFibGVzIHRvIHN0cmluZ3MgdG8gZXNzZW50aWFsbHkgbmFtZXNwYWNlIHRoZSBmaWVsZCwgZm9yIG1vc3QgY2FzZXMuXG4gKi9cbmV4cG9ydCBjbGFzcyBTaW5nbGVmbGlnaHQge1xuICAgIHByaXZhdGUgY29uc3RydWN0b3IoKSB7fVxuXG4gICAgLyoqXG4gICAgICogQSB2b2lkIG1hcmtlciB0byBoZWxwIHdpdGggcmV0dXJuaW5nIGEgdmFsdWUgaW4gYSBzaW5nbGVmbGlnaHQgY29udGV4dC5cbiAgICAgKiBJZiB5b3VyIGNvZGUgZG9lc24ndCByZXR1cm4gYW55dGhpbmcsIHJldHVybiB0aGlzIGluc3RlYWQuXG4gICAgICovXG4gICAgcHVibGljIHN0YXRpYyBWb2lkID0gU3ltYm9sKFwidm9pZFwiKTtcblxuICAgIC8qKlxuICAgICAqIEFjcXVpcmUgYSBzaW5nbGVmbGlnaHQgY29udGV4dC5cbiAgICAgKiBAcGFyYW0ge09iamVjdH0gaW5zdGFuY2UgQW4gaW5zdGFuY2UgdG8gYXNzb2NpYXRlIHRoZSBjb250ZXh0IHdpdGguIENhbiBiZSBhbnkgb2JqZWN0LlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBrZXkgQSBzdHJpbmcga2V5IHJlbGV2YW50IHRvIHRoYXQgaW5zdGFuY2UgdG8gbmFtZXNwYWNlIHVuZGVyLlxuICAgICAqIEByZXR1cm5zIHtTaW5nbGVmbGlnaHRDb250ZXh0fSBSZXR1cm5zIHRoZSBjb250ZXh0IHRvIGV4ZWN1dGUgdGhlIGZ1bmN0aW9uLlxuICAgICAqL1xuICAgIHB1YmxpYyBzdGF0aWMgZm9yKGluc3RhbmNlPzogT2JqZWN0IHwgbnVsbCwga2V5Pzogc3RyaW5nIHwgbnVsbCk6IFNpbmdsZWZsaWdodENvbnRleHQge1xuICAgICAgICBpZiAoIWluc3RhbmNlIHx8ICFrZXkpIHRocm93IG5ldyBFcnJvcihcIkFuIGluc3RhbmNlIGFuZCBrZXkgbXVzdCBiZSBzdXBwbGllZFwiKTtcbiAgICAgICAgcmV0dXJuIG5ldyBTaW5nbGVmbGlnaHRDb250ZXh0KGluc3RhbmNlLCBrZXkpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZvcmdldHMgYWxsIHJlc3VsdHMgZm9yIGEgZ2l2ZW4gaW5zdGFuY2UuXG4gICAgICogQHBhcmFtIHtPYmplY3R9IGluc3RhbmNlIFRoZSBpbnN0YW5jZSB0byBmb3JnZXQgYWJvdXQuXG4gICAgICovXG4gICAgcHVibGljIHN0YXRpYyBmb3JnZXRBbGxGb3IoaW5zdGFuY2U6IE9iamVjdCk6IHZvaWQge1xuICAgICAgICBrZXlNYXAuZGVsZXRlKGluc3RhbmNlKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBGb3JnZXRzIGFsbCBjYWNoZWQgcmVzdWx0cyBmb3IgYWxsIGluc3RhbmNlcy4gSW50ZW5kZWQgZm9yIHVzZSBieSB0ZXN0cy5cbiAgICAgKi9cbiAgICBwdWJsaWMgc3RhdGljIGZvcmdldEFsbCgpOiB2b2lkIHtcbiAgICAgICAgZm9yIChjb25zdCBrIG9mIGtleU1hcC5rZXlzKCkpIHtcbiAgICAgICAgICAgIGtleU1hcC5yZW1vdmUoayk7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbmNsYXNzIFNpbmdsZWZsaWdodENvbnRleHQge1xuICAgIHB1YmxpYyBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHJpdmF0ZSBpbnN0YW5jZTogT2JqZWN0LFxuICAgICAgICBwcml2YXRlIGtleTogc3RyaW5nLFxuICAgICkge31cblxuICAgIC8qKlxuICAgICAqIEZvcmdldCB0aGlzIHBhcnRpY3VsYXIgaW5zdGFuY2UgYW5kIGtleSBjb21iaW5hdGlvbiwgZGlzY2FyZGluZyB0aGUgcmVzdWx0LlxuICAgICAqL1xuICAgIHB1YmxpYyBmb3JnZXQoKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IG1hcCA9IGtleU1hcC5nZXQodGhpcy5pbnN0YW5jZSk7XG4gICAgICAgIGlmICghbWFwKSByZXR1cm47XG4gICAgICAgIG1hcC5yZW1vdmUodGhpcy5rZXkpO1xuICAgICAgICBpZiAoIW1hcC5zaXplKSBrZXlNYXAucmVtb3ZlKHRoaXMuaW5zdGFuY2UpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEV4ZWN1dGUgYSBmdW5jdGlvbi4gSWYgYSByZXN1bHQgaXMgYWxyZWFkeSBrbm93biwgdGhhdCB3aWxsIGJlIHJldHVybmVkIGluc3RlYWRcbiAgICAgKiBvZiBleGVjdXRpbmcgdGhlIHByb3ZpZGVkIGZ1bmN0aW9uLiBIb3dldmVyLCBpZiBubyByZXN1bHQgaXMga25vd24gdGhlbiB0aGUgZnVuY3Rpb25cbiAgICAgKiB3aWxsIGJlIGNhbGxlZCwgd2l0aCBpdHMgcmV0dXJuIHZhbHVlIGNhY2hlZC4gVGhlIGZ1bmN0aW9uIG11c3QgcmV0dXJuIGEgdmFsdWVcbiAgICAgKiBvdGhlciB0aGFuIGB1bmRlZmluZWRgIC0gdGFrZSBhIGxvb2sgYXQgU2luZ2xlZmxpZ2h0LlZvaWQgaWYgeW91IGRvbid0IGhhdmUgYSByZXR1cm5cbiAgICAgKiB0byBtYWtlLlxuICAgICAqXG4gICAgICogTm90ZSB0aGF0IHRoaXMgdGVjaG5pY2FsbHkgYWxsb3dzIHRoZSBjYWxsZXIgdG8gcHJvdmlkZSBhIGRpZmZlcmVudCBmdW5jdGlvbiBlYWNoIHRpbWU6XG4gICAgICogdGhpcyBpcyBsYXJnZWx5IGNvbnNpZGVyZWQgYSBiYWQgaWRlYSBhbmQgc2hvdWxkIG5vdCBiZSBkb25lLiBTaW5nbGVmbGlnaHRzIHdvcmsgb2ZmIHRoZVxuICAgICAqIHByZW1pc2UgdGhhdCBzb21ldGhpbmcgbmVlZHMgdG8gaGFwcGVuIG9uY2UsIHNvIGR1cGxpY2F0ZSBleGVjdXRpb25zIHdpbGwgYmUgaWdub3JlZC5cbiAgICAgKlxuICAgICAqIEZvciBpZGVhbCBwZXJmb3JtYW5jZSBhbmQgYmVoYXZpb3VyLCBmdW5jdGlvbnMgd2hpY2ggcmV0dXJuIHByb21pc2VzIGFyZSBwcmVmZXJyZWQuIElmXG4gICAgICogYSBmdW5jdGlvbiBpcyBub3QgcmV0dXJuaW5nIGEgcHJvbWlzZSwgaXQgc2hvdWxkIHJldHVybiBhcyBzb29uIGFzIHBvc3NpYmxlIHRvIGF2b2lkIGFcbiAgICAgKiBzZWNvbmQgY2FsbCBwb3RlbnRpYWxseSByYWNpbmcgaXQuIFRoZSBwcm9taXNlIHJldHVybmVkIGJ5IHRoaXMgZnVuY3Rpb24gd2lsbCBiZSB0aGF0XG4gICAgICogb2YgdGhlIGZpcnN0IGV4ZWN1dGlvbiBvZiB0aGUgZnVuY3Rpb24sIGV2ZW4gb24gZHVwbGljYXRlIGNhbGxzLlxuICAgICAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIFRoZSBmdW5jdGlvbiB0byBleGVjdXRlLlxuICAgICAqIEByZXR1cm5zIFRoZSByZWNvcmRlZCB2YWx1ZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgZG88VD4oZm46ICgpID0+IFQpOiBUIHtcbiAgICAgICAgY29uc3QgbWFwID0ga2V5TWFwLmdldE9yQ3JlYXRlKHRoaXMuaW5zdGFuY2UsIG5ldyBFbmhhbmNlZE1hcDxzdHJpbmcsIHVua25vd24+KCkpO1xuXG4gICAgICAgIC8vIFdlIGhhdmUgdG8gbWFudWFsbHkgZ2V0T3JDcmVhdGUoKSBiZWNhdXNlIHdlIG5lZWQgdG8gZXhlY3V0ZSB0aGUgZm5cbiAgICAgICAgbGV0IHZhbCA9IDxUPm1hcC5nZXQodGhpcy5rZXkpO1xuICAgICAgICBpZiAodmFsID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHZhbCA9IGZuKCk7XG4gICAgICAgICAgICBtYXAuc2V0KHRoaXMua2V5LCB2YWwpO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHZhbDtcbiAgICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBUUEsSUFBQUEsS0FBQSxHQUFBQyxPQUFBO0FBUkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBSUE7O0FBRUEsTUFBTUMsTUFBTSxHQUFHLElBQUlDLGlCQUFXLENBQXVDLENBQUM7O0FBRXRFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDTyxNQUFNQyxZQUFZLENBQUM7RUFDZEMsV0FBV0EsQ0FBQSxFQUFHLENBQUM7O0VBRXZCO0FBQ0o7QUFDQTtBQUNBOztFQUdJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtFQUNJLE9BQWNDLEdBQUdBLENBQUNDLFFBQXdCLEVBQUVDLEdBQW1CLEVBQXVCO0lBQ2xGLElBQUksQ0FBQ0QsUUFBUSxJQUFJLENBQUNDLEdBQUcsRUFBRSxNQUFNLElBQUlDLEtBQUssQ0FBQyxzQ0FBc0MsQ0FBQztJQUM5RSxPQUFPLElBQUlDLG1CQUFtQixDQUFDSCxRQUFRLEVBQUVDLEdBQUcsQ0FBQztFQUNqRDs7RUFFQTtBQUNKO0FBQ0E7QUFDQTtFQUNJLE9BQWNHLFlBQVlBLENBQUNKLFFBQWdCLEVBQVE7SUFDL0NMLE1BQU0sQ0FBQ1UsTUFBTSxDQUFDTCxRQUFRLENBQUM7RUFDM0I7O0VBRUE7QUFDSjtBQUNBO0VBQ0ksT0FBY00sU0FBU0EsQ0FBQSxFQUFTO0lBQzVCLEtBQUssTUFBTUMsQ0FBQyxJQUFJWixNQUFNLENBQUNhLElBQUksQ0FBQyxDQUFDLEVBQUU7TUFDM0JiLE1BQU0sQ0FBQ2MsTUFBTSxDQUFDRixDQUFDLENBQUM7SUFDcEI7RUFDSjtBQUNKO0FBQUNHLE9BQUEsQ0FBQWIsWUFBQSxHQUFBQSxZQUFBO0FBQUEsSUFBQWMsZ0JBQUEsQ0FBQUMsT0FBQSxFQXBDWWYsWUFBWSxVQU9BZ0IsTUFBTSxDQUFDLE1BQU0sQ0FBQztBQStCdkMsTUFBTVYsbUJBQW1CLENBQUM7RUFDZkwsV0FBV0EsQ0FDTkUsUUFBZ0IsRUFDaEJDLEdBQVcsRUFDckI7SUFBQSxLQUZVRCxRQUFnQixHQUFoQkEsUUFBZ0I7SUFBQSxLQUNoQkMsR0FBVyxHQUFYQSxHQUFXO0VBQ3BCOztFQUVIO0FBQ0o7QUFDQTtFQUNXYSxNQUFNQSxDQUFBLEVBQVM7SUFDbEIsTUFBTUMsR0FBRyxHQUFHcEIsTUFBTSxDQUFDcUIsR0FBRyxDQUFDLElBQUksQ0FBQ2hCLFFBQVEsQ0FBQztJQUNyQyxJQUFJLENBQUNlLEdBQUcsRUFBRTtJQUNWQSxHQUFHLENBQUNOLE1BQU0sQ0FBQyxJQUFJLENBQUNSLEdBQUcsQ0FBQztJQUNwQixJQUFJLENBQUNjLEdBQUcsQ0FBQ0UsSUFBSSxFQUFFdEIsTUFBTSxDQUFDYyxNQUFNLENBQUMsSUFBSSxDQUFDVCxRQUFRLENBQUM7RUFDL0M7O0VBRUE7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0VBQ1drQixFQUFFQSxDQUFJQyxFQUFXLEVBQUs7SUFDekIsTUFBTUosR0FBRyxHQUFHcEIsTUFBTSxDQUFDeUIsV0FBVyxDQUFDLElBQUksQ0FBQ3BCLFFBQVEsRUFBRSxJQUFJSixpQkFBVyxDQUFrQixDQUFDLENBQUM7O0lBRWpGO0lBQ0EsSUFBSXlCLEdBQUcsR0FBTU4sR0FBRyxDQUFDQyxHQUFHLENBQUMsSUFBSSxDQUFDZixHQUFHLENBQUM7SUFDOUIsSUFBSW9CLEdBQUcsS0FBS0MsU0FBUyxFQUFFO01BQ25CRCxHQUFHLEdBQUdGLEVBQUUsQ0FBQyxDQUFDO01BQ1ZKLEdBQUcsQ0FBQ1EsR0FBRyxDQUFDLElBQUksQ0FBQ3RCLEdBQUcsRUFBRW9CLEdBQUcsQ0FBQztJQUMxQjtJQUVBLE9BQU9BLEdBQUc7RUFDZDtBQUNKIiwiaWdub3JlTGlzdCI6W119