fission-suite/webnative

View on GitHub
src/repository.ts

Summary

Maintainability
A
0 mins
Test Coverage
import * as Storage from "./components/storage/implementation"
import * as TypeChecks from "./common/type-checks.js"


export type RepositoryOptions = {
  storage: Storage.Implementation
  storageName: string
}


export default abstract class Repository<T> {

  dictionary: Record<string, T>
  memoryCache: T[]
  storage: Storage.Implementation
  storageName: string


  constructor({ storage, storageName }: RepositoryOptions) {
    this.memoryCache = []
    this.dictionary = {}
    this.storage = storage
    this.storageName = storageName
  }

  static async create<T>(options: RepositoryOptions) {
    // @ts-ignore
    const repo = new this.prototype.constructor(options)

    repo.memoryCache = await repo.getAll()
    repo.dictionary = repo.toDictionary(repo.memoryCache)

    return repo
  }

  async add(itemOrItems: T | T[]): Promise<void> {
    const items = Array.isArray(itemOrItems) ? itemOrItems : [ itemOrItems ]

    this.memoryCache = [ ...this.memoryCache, ...items ]
    this.dictionary = this.toDictionary(this.memoryCache)

    await this.storage.setItem(
      this.storageName,
      // TODO: JSON.stringify(this.memoryCache.map(this.toJSON))
      this.memoryCache.map(this.toJSON).join("|||")
    )
  }

  clear(): Promise<void> {
    this.memoryCache = []
    this.dictionary = {}

    return this.storage.removeItem(this.storageName)
  }

  find(predicate: (value: T, index: number) => boolean): T | null {
    return this.memoryCache.find(predicate) || null
  }

  getByIndex(idx: number): T | null {
    return this.memoryCache[ idx ]
  }

  async getAll(): Promise<T[]> {
    const storage = await this.storage.getItem(this.storageName)
    const storedItems = TypeChecks.isString(storage)
      // TODO: ? - Need partial JSON decoding for this
      ? storage.split("|||").map(this.fromJSON)
      : []

    return storedItems
  }

  indexOf(item: T): number {
    return this.memoryCache.indexOf(item)
  }

  length(): number {
    return this.memoryCache.length
  }


  // ENCODING

  fromJSON(a: string): T {
    return JSON.parse(a)
  }

  toJSON(a: T): string {
    return JSON.stringify(a)
  }


  // DICTIONARY

  getByKey(key: string): T | null {
    return this.dictionary[ key ]
  }

  toDictionary(items: T[]): Record<string, T> {
    return items.reduce(
      (acc, value, idx) => ({ ...acc, [ idx.toString() ]: value }),
      {}
    )
  }

}