{"version":3,"file":"scion-toolkit-storage.mjs","sources":["../../../../projects/scion/toolkit/storage/src/web-storage.ts","../../../../projects/scion/toolkit/storage/src/public_api.ts","../../../../projects/scion/toolkit/storage/src/scion-toolkit-storage.ts"],"sourcesContent":["/*\n * Copyright (c) 2018-2019 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n *  SPDX-License-Identifier: EPL-2.0\n */\n\nimport {EMPTY, fromEvent, merge, Observable, Observer, of, Subject, TeardownLogic} from 'rxjs';\nimport {distinctUntilChanged, filter, map, mergeMap, startWith, takeUntil} from 'rxjs/operators';\n\n/**\n * Allows observing items contained in web storage (local storage or session storage).\n *\n * Items are added in the JSON format to the storage.\n *\n * Because web storage does not notify about same document storage changes, always use {@link WebStorage#put}\n * to add items to the storage, or {@link WebStorage#remove} to remove items from the storage.\n *\n * #### Storage implementors:\n * - Local storage:\n *   Maintains a persistent storage area per origin.\n * - Session storage:\n *   Maintains a separate storage area per origin that is available for the duration of the page session\n *   (as long as the browser is open, including page reloads and restores).\n */\nexport class WebStorage {\n\n  private _currentDocumentChange$ = new Subject<string>();\n\n  /**\n   * Provide the storage implementor: {@link window.localStorage} or {@link window.sessionStorage}.\n   */\n  constructor(private _storage: Storage) {\n  }\n\n  /**\n   * Puts the given item into storage. The item is serialized to JSON.\n   */\n  public put(key: string, value: unknown): void {\n    if (value === undefined || value === null) {\n      this._storage.setItem(key, `${value}`); // preserve `null` and `undefined`\n    }\n    else {\n      this._storage.setItem(key, JSON.stringify(value));\n    }\n    this._currentDocumentChange$.next(key);\n  }\n\n  /**\n   * Puts the given item into storage, but only if not present. The item is serialized to JSON.\n   *\n   * Instead of an item you can pass a provider function to produce the item.\n   */\n  public putIfAbsent(key: string, value: unknown | (() => unknown)): void {\n    if (!this.isPresent(key)) {\n      this.put(key, typeof value === 'function' ? value() : value);\n    }\n  }\n\n  /**\n   * Returns the item associated with the given key, or `undefined` if not found.\n   *\n   * Expects the value to be stored in JSON format. Throws an error if the value cannot be parsed.\n   */\n  public get<T>(key: string): T | null | undefined {\n    const item = this._storage.getItem(key);\n\n    // Web storage returns `null` if there is no item associated with the key.\n    if (item === null) {\n      return undefined;\n    }\n\n    // If the value `undefined` is associated with the key, web storage returns 'undefined' as string.\n    if (item === 'undefined') {\n      return undefined;\n    }\n\n    // If the value `null` is associated with the key, web storage returns 'null' as string.\n    if (item === 'null') {\n      return null;\n    }\n\n    return JSON.parse(item) as T;\n  }\n\n  /**\n   * Removes the item associated with the given key.\n   */\n  public remove(key: string): void {\n    this._storage.removeItem(key);\n    this._currentDocumentChange$.next(key);\n  }\n\n  /**\n   * Checks if an item is present in the storage. Present also includes `null` and `undefined` items.\n   */\n  public isPresent(key: string): boolean {\n    return this._storage.getItem(key) !== null; // storage returns `null` if not present\n  }\n\n  /**\n   * Observes the item associated with the given key.\n   *\n   * Upon subscription, it emits the current item from the storage, but, by default, only if present,\n   * and then continuously emits when the item associated with the given key changes. It never completes.\n   *\n   * When removing the item from the storage, by default, the Observable does not emit.\n   *\n   * Set `emitIfAbsent` to `true` if to emit `undefined` when removing the item, or if there is no item associated\n   * with the given key upon subscription. By default, `emitIfAbsent` is set to `false`.\n   */\n  public observe$<T>(key: string, options?: {emitIfAbsent?: boolean}): Observable<T> {\n    const emitIfAbsent = options?.emitIfAbsent ?? false;\n    const otherDocumentChange$ = fromEvent<StorageEvent>(window, 'storage')\n      .pipe(\n        filter(event => event.storageArea === this._storage),\n        mergeMap(event => event.key !== null ? of(event.key) : EMPTY),\n      );\n\n    return new Observable((observer: Observer<T>): TeardownLogic => {\n      const unsubscribe$ = new Subject<void>();\n\n      merge(this._currentDocumentChange$, otherDocumentChange$)\n        .pipe(\n          filter(itemKey => itemKey === key),\n          startWith(key),\n          mergeMap(itemKey => {\n            if (!this.isPresent(itemKey)) {\n              return emitIfAbsent ? of(undefined) : EMPTY;\n            }\n\n            try {\n              return of(this.get<any>(itemKey));\n            }\n            catch (error) {\n              console.warn(`Failed to parse item from storage. Invalid JSON. [key=${itemKey}]`, error);\n              return EMPTY;\n            }\n          }),\n          distinctUntilChanged(),\n          takeUntil(unsubscribe$),\n        )\n        .subscribe(observer);\n\n      return (): void => {\n        unsubscribe$.next();\n      };\n    });\n  }\n\n  /**\n   * Notifies when no item is present for the given key. The Observable never completes.\n   */\n  public absent$(key: string): Observable<void> {\n    return this.observe$(key, {emitIfAbsent: true})\n      .pipe(\n        filter(() => !this.isPresent(key)),\n        map(() => undefined),\n      );\n  }\n}\n","/*\n * Copyright (c) 2018-2019 Swiss Federal Railways\n *\n * This program and the accompanying materials are made\n * available under the terms of the Eclipse Public License 2.0\n * which is available at https://www.eclipse.org/legal/epl-2.0/\n *\n *  SPDX-License-Identifier: EPL-2.0\n */\n\n/*\n * Secondary entrypoint: '@scion/toolkit/storage'\n *\n * @see https://github.com/ng-packagr/ng-packagr/blob/master/docs/secondary-entrypoints.md\n */\nexport * from './web-storage';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}