text: /*---------------------------------------------------------------------------------------------
   *  Copyright (c) Microsoft Corporation. All rights reserved.
   *  Licensed under the MIT License. See License.txt in the project root for license information.
   *--------------------------------------------------------------------------------------------*/
  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());
      });
  };
  import { Disposable } from '../../../common/lifecycle.js';
  import { Emitter, Event } from '../../../common/event.js';
  import { ThrottledDelayer } from '../../../common/async.js';
  import { isUndefinedOrNull } from '../../../common/types.js';
  var StorageState;
  (function (StorageState) {
      StorageState[StorageState["None"] = 0] = "None";
      StorageState[StorageState["Initialized"] = 1] = "Initialized";
      StorageState[StorageState["Closed"] = 2] = "Closed";
  })(StorageState || (StorageState = {}));
  export class Storage extends Disposable {
      constructor(database, options = Object.create(null)) {
          super();
          this.database = database;
          this.options = options;
          this._onDidChangeStorage = this._register(new Emitter());
          this.onDidChangeStorage = this._onDidChangeStorage.event;
          this.state = StorageState.None;
          this.cache = new Map();
          this.flushDelayer = new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY);
          this.pendingDeletes = new Set();
          this.pendingInserts = new Map();
          this.whenFlushedCallbacks = [];
          this.registerListeners();
      }
      registerListeners() {
          this._register(this.database.onDidChangeItemsExternal(e => this.onDidChangeItemsExternal(e)));
      }
      onDidChangeItemsExternal(e) {
          var _a, _b;
          // items that change external require us to update our
          // caches with the values. we just accept the value and
          // emit an event if there is a change.
          (_a = e.changed) === null || _a === void 0 ? void 0 : _a.forEach((value, key) => this.accept(key, value));
          (_b = e.deleted) === null || _b === void 0 ? void 0 : _b.forEach(key => this.accept(key, undefined));
      }
      accept(key, value) {
          if (this.state === StorageState.Closed) {
              return; // Return early if we are already closed
          }
          let changed = false;
          // Item got removed, check for deletion
          if (isUndefinedOrNull(value)) {
              changed = this.cache.delete(key);
          }
          // Item got updated, check for change
          else {
              const currentValue = this.cache.get(key);
              if (currentValue !== value) {
                  this.cache.set(key, value);
                  changed = true;
              }
          }
          // Signal to outside listeners
          if (changed) {
              this._onDidChangeStorage.fire(key);
          }
      }
      get(key, fallbackValue) {
          const value = this.cache.get(key);
          if (isUndefinedOrNull(value)) {
              return fallbackValue;
          }
          return value;
      }
      getBoolean(key, fallbackValue) {
          const value = this.get(key);
          if (isUndefinedOrNull(value)) {
              return fallbackValue;
          }
          return value === 'true';
      }
      getNumber(key, fallbackValue) {
          const value = this.get(key);
          if (isUndefinedOrNull(value)) {
              return fallbackValue;
          }
          return parseInt(value, 10);
      }
      set(key, value) {
          return __awaiter(this, void 0, void 0, function* () {
              if (this.state === StorageState.Closed) {
                  return; // Return early if we are already closed
              }
              // We remove the key for undefined/null values
              if (isUndefinedOrNull(value)) {
                  return this.delete(key);
              }
              // Otherwise, convert to String and store
              const valueStr = String(value);
              // Return early if value already set
              const currentValue = this.cache.get(key);
              if (currentValue === valueStr) {
                  return;
              }
              // Update in cache and pending
              this.cache.set(key, valueStr);
              this.pendingInserts.set(key, valueStr);
              this.pendingDeletes.delete(key);
              // Event
              this._onDidChangeStorage.fire(key);
              // Accumulate work by scheduling after timeout
              return this.flushDelayer.trigger(() => this.flushPending());
          });
      }
      delete(key) {
          return __awaiter(this, void 0, void 0, function* () {
              if (this.state === StorageState.Closed) {
                  return; // Return early if we are already closed
              }
              // Remove from cache and add to pending
              const wasDeleted = this.cache.delete(key);
              if (!wasDeleted) {
                  return; // Return early if value already deleted
              }
              if (!this.pendingDeletes.has(key)) {
                  this.pendingDeletes.add(key);
              }
              this.pendingInserts.delete(key);
              // Event
              this._onDidChangeStorage.fire(key);
              // Accumulate work by scheduling after timeout
              return this.flushDelayer.trigger(() => this.flushPending());
          });
      }
      get hasPending() {
          return this.pendingInserts.size > 0 || this.pendingDeletes.size > 0;
      }
      flushPending() {
          return __awaiter(this, void 0, void 0, function* () {
              if (!this.hasPending) {
                  return; // return early if nothing to do
              }
              // Get pending data
              const updateRequest = { insert: this.pendingInserts, delete: this.pendingDeletes };
              // Reset pending data for next run
              this.pendingDeletes = new Set();
              this.pendingInserts = new Map();
              // Update in storage and release any
              // waiters we have once done
              return this.database.updateItems(updateRequest).finally(() => {
                  var _a;
                  if (!this.hasPending) {
                      while (this.whenFlushedCallbacks.length) {
                          (_a = this.whenFlushedCallbacks.pop()) === null || _a === void 0 ? void 0 : _a();
                      }
                  }
              });
          });
      }
      dispose() {
          this.flushDelayer.dispose();
          super.dispose();
      }
  }
  Storage.DEFAULT_FLUSH_DELAY = 100;
  export class InMemoryStorageDatabase {
      constructor() {
          this.onDidChangeItemsExternal = Event.None;
          this.items = new Map();
      }
      updateItems(request) {
          return __awaiter(this, void 0, void 0, function* () {
              if (request.insert) {
                  request.insert.forEach((value, key) => this.items.set(key, value));
              }
              if (request.delete) {
                  request.delete.forEach(key => this.items.delete(key));
              }
          });
      }
  }
  