/**
* Сохранения в JSON-слотах. Хранилище инъекцией (localStorage или любой
* Storage-подобный объект) — чтобы работать и в тестах без браузера.
*/
export interface StorageLike {
getItem(key: string): string | null;
setItem(key: string, value: string): void;
removeItem(key: string): void;
key(index: number): string | null;
readonly length: number;
}
export class SaveManager {
constructor(
private storage: StorageLike,
private prefix = 'save:'
) {}
save(slot: string, data: unknown): void {
this.storage.setItem(this.prefix + slot, JSON.stringify(data));
}
load<T = unknown>(slot: string): T | null {
const raw = this.storage.getItem(this.prefix + slot);
if (raw === null) return null;
try {
return JSON.parse(raw) as T;
} catch {
return null;
}
}
delete(slot: string): void {
this.storage.removeItem(this.prefix + slot);
}
/** Есть ли сохранение в слоте. */
has(slot: string): boolean {
return this.storage.getItem(this.prefix + slot) !== null;
}
/** Список занятых слотов (без префикса). */
listSlots(): string[] {
const out: string[] = [];
for (let i = 0; i < this.storage.length; i++) {
const key = this.storage.key(i);
if (key && key.startsWith(this.prefix)) {
out.push(key.slice(this.prefix.length));
}
}
return out;
}
}