import store from '../../utils/store'; /** * The root JSON object that contains the history records and is persisted to disk. */ interface History { /** * Used to track the version of the schema layout just in case we do any * breaking changes to this schema so that we can properly migrate old * search history data. It's also used to prevent older versions of * the frontend code from trying to use the newer incompatible schema they * know nothing about (extremely improbable, but just in case). */ schemaVersion: 1; /** * The list of history records sorted from the last recently used to the oldest unused. */ records: string[]; } /** * History store backend is responsible for parsing and serializing the data * to/from `localStorage`. It handles versioning of the schema, and transparently * disables writing to the storage if the schema version is unknown to prevent * data loss (extremely improbable, but just in case). */ export class HistoryStore { private writable: boolean = true; private readonly key: string; constructor(key: string) { this.key = key; } read(): string[] { return this.extractRecords(store.get(this.key)); } write(records: string[]): void { if (!this.writable) { return; } const history: History = { schemaVersion: 1, records, }; const start = performance.now(); store.set(this.key, history); const end = performance.now(); console.debug( `Writing ${records.length} history records to the localStorage took ${end - start}ms. ` + `Records: ${records.length}`, ); } /** * Extracts the records from the history. To do this, we first need to migrate * the history object to the latest schema version if necessary. */ private extractRecords(history: History | null): string[] { // `null` here means we are starting from the initial state (empty list of records). if (history === null) { return []; } // We have only one version at the time of this writing, so we don't need // to do any migration yet. Hopefully we never need to do a breaking change // and this stays at version `1` forever. const latestSchemaVersion = 1; switch (history.schemaVersion) { case latestSchemaVersion: return history.records; default: // It's very unlikely that we ever hit this branch. console.warn( `Unknown search history schema version: '${history.schemaVersion}'. ` + `This frontend code was built with the maximum supported schema version ` + `'${latestSchemaVersion}'. The search history will be disabled for this ` + `session to prevent potential history data loss. The cause of the version ` + `mismatch may be that a newer version of the frontend code is running in a ` + `separate tab, or you were mistakenly served with an older version of the ` + `frontend code.`, ); // Disallow writing to the storage to prevent data loss. this.writable = false; return []; } } }