Cache

WebStorageCacheStrategy

Classe base astratta condivisa da LocalStorageCacheStrategy e SessionStorageCacheStrategy. Implementa la logica comune per Web Storage API con prefix, TTL e quota handling.

Cosa fa

WebStorageCacheStrategy e una classe astratta che incapsula la logica condivisa tra LocalStorageCacheStrategy e SessionStorageCacheStrategy. Le due classi concrete differiscono solo per:

  • Tipo di Storage iniettato (localStorage vs sessionStorage)
  • Discriminator type (LOCALSTORAGE vs SESSIONSTORAGE)
  • Implementazione di isPersistent() (true vs false)
  • Prefix default

Tutto il resto della logica e qui.

Non istanziare direttamente — usa le sottoclassi concrete o, ancora meglio, CacheStrategyFactory. Questa pagina e documentazione di riferimento per chi vuole capire il comportamento condiviso o estenderlo (es. cifratura, compressione, storage custom).

Costruttore (protected)

abstract class WebStorageCacheStrategy implements ICacheStrategy {
  abstract readonly type: CacheStrategyType
  protected constructor(storage: Storage, prefix: string)
  abstract isPersistent(): boolean
}
ParametroTipoDescrizione
storageStoragelocalStorage o sessionStorage (interfaccia Web Storage API)
prefixstringPrefisso usato per evitare collisioni con altre chiavi dell'app

Le sottoclassi devono dichiarare:

  • readonly type: CacheStrategyType
  • isPersistent(): boolean

Logica condivisa

Prefix scoping

Tutte le chiavi sono prefissate. Le query rispettano questo prefix:

private getKey(key: string): string {
  return `${this.prefix}${key}`
}

getAllKeys() itera storage.length e filtra per key.startsWith(this.prefix), restituendo i nomi senza prefix.

Schema entry

interface CacheItem<T> {
  data: T
  timestamp: number
  expiry: number | null
}

Serializzato con JSON.stringify in scrittura, JSON.parse in lettura.

Quota handling

async setItem<T>(key: string, value: T, ttl?: number): Promise<void> {
  try {
    this.storage.setItem(this.getKey(key), JSON.stringify(item))
  } catch (error) {
    if ((error as Error).name === 'QuotaExceededError') {
      await this.cleanExpired()
      try {
        this.storage.setItem(this.getKey(key), JSON.stringify(item))
      } catch {
        console.warn(`${this.type} quota exceeded, cache item not saved`)
      }
    }
  }
}

In caso di QuotaExceededError:

  1. Esegue cleanExpired() per liberare spazio
  2. Ritenta la setItem
  3. Se anche il retry fallisce, silenzia l'errore con console.warn (la mancata persistenza non deve far fallire la request applicativa)

Recupero robusto

getItem gestisce due edge case:

  • Entry scaduta → removeItem + ritorno null
  • Entry corrotta (JSON non parsabile) → removeItem + ritorno null

cleanExpired rimuove anche le entry corrotte (defensive cleanup).

API ereditata

Tutti i metodi di ICacheStrategy sono implementati nella classe base:

  • setItem, getItem, removeItem
  • clear(prefix?) — filtra ulteriormente le chiavi del prefix interno per keyPrefix se passato
  • hasItem, getAllValues, getAllKeys
  • cleanExpired, getStats
  • supportsTTL()true (Web Storage supporta TTL via timestamp + expiry serializzati)

Estendere la classe

Esempio: strategy con compressione LZ-string sopra localStorage.

import { WebStorageCacheStrategy } from '@pzeta/postgrest'
import { CacheStrategyType } from '@pzeta/postgrest'

class CompressedLocalStorageStrategy extends WebStorageCacheStrategy {
  readonly type = CacheStrategyType.LOCALSTORAGE

  constructor() {
    super(localStorage, 'pzeta_compressed_')
  }

  isPersistent() {
    return true
  }
  // ... override setItem/getItem con compressione
}

Vedi anche