All files / src/apps/gbs-music-web/lib songBackup.ts

0% Statements 0/40
0% Branches 0/19
0% Functions 0/6
0% Lines 0/35

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70                                                                                                                                           
import { isSong, type Song } from "shared/lib/uge/types";
 
export const BACKUP_SONG_KEY = "gbsMusicWeb:song.bck";
export const BACKUP_TIMESTAMP_KEY = "gbsMusicWeb:song.bck.timestamp";
 
const UINT8_TAG = "__uint8array";
 
// JSON replacer that converts Uint8Array to a tagged plain object so it can
// survive a JSON round-trip without being silently mangled into {0:…, 1:…}.
const songReplacer = (_key: string, value: unknown): unknown => {
  Iif (value instanceof Uint8Array) {
    return { [UINT8_TAG]: Array.from(value) };
  }
  return value;
};
 
// Paired JSON reviver that reconstructs Uint8Arrays from the tagged form.
const songReviver = (_key: string, value: unknown): unknown => {
  Iif (
    value !== null &&
    typeof value === "object" &&
    UINT8_TAG in (value as Record<string, unknown>)
  ) {
    return new Uint8Array((value as Record<string, number[]>)[UINT8_TAG]);
  }
  return value;
};
 
export const serializeSong = (song: Song): string =>
  JSON.stringify(song, songReplacer);
 
export const deserializeSong = (json: string): Song | null => {
  try {
    const data: unknown = JSON.parse(json, songReviver);
    return isSong(data) ? data : null;
  } catch {
    return null;
  }
};
 
interface BackupInfo {
  name: string;
  filename: string;
  timestamp: number;
}
 
/** Returns metadata about the stored backup without fully deserialising the song. */
export const getBackupInfo = (): BackupInfo | null => {
  try {
    const json = localStorage.getItem(BACKUP_SONG_KEY);
    Iif (!json) return null;
    const parsed = deserializeSong(json);
    Iif (!parsed) {
      return null;
    }
    const name = parsed.name ?? "Backup";
    const filename = parsed.filename ?? "backup.uge";
    const rawTimestamp = localStorage.getItem(BACKUP_TIMESTAMP_KEY);
    const timestamp = rawTimestamp ? parseInt(rawTimestamp, 10) : Date.now();
    return { name, filename, timestamp };
  } catch {
    return null;
  }
};
 
export const clearBackup = (): void => {
  localStorage.removeItem(BACKUP_SONG_KEY);
  localStorage.removeItem(BACKUP_TIMESTAMP_KEY);
};