Last active
November 12, 2024 22:29
-
-
Save moritztim/de1a520513d7a4f01bbe5c4dc768a8c5 to your computer and use it in GitHub Desktop.
Deno TypeScript Cache Manager
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ReadWriteFileCacheManager } from "./FileCacheManager.ts"; | |
/** Cache manager for raw binary files | |
* | |
* @property {string} filePath Path to the file whose contents are to be cached | |
* @property {Promise<Uint8Array>} dataPromise Asynchronous getter for data | |
* @property {Uint8Array} data Synchronous getter for data | |
* @property {Promise<void>} set Asynchronous setter for data | |
* @property {Uint8Array} data Synchronous setter for data | |
*/ | |
export class BinaryFileCacheManager extends ReadWriteFileCacheManager<Uint8Array> { | |
protected get fileContentsPromise() { | |
return Deno.readFile(this.filePath); | |
} | |
protected get fileContents() { | |
return Deno.readFileSync(this.filePath); | |
} | |
protected set fileContents(data: Uint8Array) { | |
Deno.writeFileSync(this.filePath, data); | |
} | |
protected writeFileContents(data: Uint8Array) { | |
return Deno.writeFile(this.filePath, data); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Abstract class for handling caching of generic contents. | |
* | |
* Provides methods to retrieve cached data, as well as to invalidate the cache. | |
* | |
* @typeParam T Type of the data to be cached | |
*/ | |
export default abstract class CacheManager<T> { | |
/** Cached data */ | |
protected _cache?: T; | |
/** When the data was last fetched from the file */ | |
protected lastFetch?: Date; | |
public get valid(): boolean { | |
return !(this.lastFetch == undefined || this._cache == undefined); | |
} | |
/** | |
* Retrieves the data from cache or fetches it if necessary. | |
* @async | |
* @returns {Promise<T>} Promise for data from cache or source | |
*/ | |
public abstract get dataPromise(): Promise<T>; | |
/** | |
* Retrieves the data from cache or fetches it synchronously if necessary. | |
* @returns {T} Data from cache or source | |
*/ | |
public abstract get data(): T; | |
/** | |
* Invalidates the cache, causing data to be refreshed on next access. | |
*/ | |
public invalidate(): void { | |
this._cache = undefined; | |
this.lastFetch = undefined; | |
} | |
/** | |
* Caches the provided data and updates the last fetch timestamp. | |
* @param data The data to be cached. | |
*/ | |
protected cache(data: T): void { | |
this._cache = data; | |
this.lastFetch = new Date(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import CacheManager from "./CacheManager.ts"; | |
import ReadWriteCacheManager from "./ReadWriteCacheManager.ts"; | |
/** Generic class for handling caching of file contents. | |
* | |
* Provides methods to retrieve cached data, as well as to invalidate the cache. | |
* | |
* @typeParam T Type of the data to be cached | |
* @property {string} filePath Path to the file whose contents are to be cached | |
* @property {Promise<T>} dataPromise Asynchronous getter for data | |
* @property {T} data Synchronous getter for data | |
*/ | |
export abstract class FileCacheManager<T> extends CacheManager<T> { | |
constructor(protected filePath: string) { super() } | |
/** Promise for data from cache or, if file is newer, asynchronously retrieved from file | |
* @async | |
*/ | |
public get dataPromise(): Promise<T> { | |
this.#cachedFileStatsPromise = undefined; | |
// if not cached, cache | |
if (!super.valid) { | |
return this.fetchAndCache(); | |
} | |
// if cached, check if file modified | |
return this.fileStatsPromise.then((stats) => { | |
if (stats.mtime! > this.lastFetch!) | |
return this.fetchAndCache(); | |
else | |
return this._cache ?? this.fetchAndCache(); // cache may have been invalidated | |
}); | |
} | |
/** Data from cache or, if file is newer, from file */ | |
public get data(): T { | |
this.#cachedFileStats = undefined; | |
if (!super.valid || this.fileStats.mtime! > this.lastFetch!) // if valid, lastFetch is defined | |
this._cache = this.fileContents; | |
return this._cache!; // if valid, _cache is defined, otherwise _cache just got defined | |
} | |
/** Promise of asynchronously retrieved file contents | |
* @async | |
* @returns {Promise<T>} | |
*/ | |
protected abstract get fileContentsPromise(): Promise<T>; | |
/** @returns {T} */ | |
protected abstract get fileContents(): T; | |
/** Promise for file stats | |
* Stored in a sort of singleton, to avoid multiple calls to Deno.stat | |
*/ | |
#cachedFileStatsPromise?: ReturnType<typeof Deno.stat>; | |
private get fileStatsPromise() { | |
return this.#getCachedSimple<ReturnType<typeof Deno.stat>>(this.#cachedFileStatsPromise, Deno.stat.bind(Deno, this.filePath)); | |
} | |
/** File stats, synchronous version of {@link fileStatsPromise} */ | |
#cachedFileStats?: ReturnType<typeof Deno.statSync>; | |
private get fileStats(): ReturnType<typeof Deno.statSync> { | |
return this.#getCachedSimple<ReturnType<typeof Deno.statSync>>(this.#cachedFileStats, Deno.statSync.bind(Deno, this.filePath)); | |
} | |
#getCachedSimple<T>(cached: T | undefined, get: () => T): T { | |
if (!cached) cached = get(); | |
return cached; | |
} | |
private async fetchAndCache(): Promise<T> { | |
return await this.fileContentsPromise.then((data) => { | |
this.cache(data); | |
return data; | |
}); | |
} | |
} | |
/** | |
* Generic class for handling caching of file contents. | |
* | |
* Provides methods to set and retrieve cached data, as well as to invalidate the cache. | |
* | |
* @typeParam T Type of the data to be cached | |
* @property {string} filePath Path to the file whose contents are to be cached | |
* @property {Promise<T>} dataPromise Asynchronous getter for data | |
* @property {T} data Synchronous getter for data | |
* @property {Promise<void>} set Asynchronous setter for data | |
* @property {T} data Synchronous setter for data | |
*/ | |
export abstract class ReadWriteFileCacheManager<T> extends FileCacheManager<T> implements ReadWriteCacheManager<T> { | |
public set data(data: T) { | |
this.cache(data); | |
this.writeFileContents(data); | |
} | |
/** Asynchronously set the data in the cache and write it to file */ | |
public async set(data: T): Promise<void> { | |
this.cache(data); | |
await this.writeFileContents(data); | |
} | |
/** Asynchronously write data to file | |
* @async | |
* @param {T} data | |
* @returns {Promise<void>} | |
*/ | |
protected abstract writeFileContents(data: T): Promise<void>; | |
/** Synchronously sets data in file | |
* @param {T} data | |
*/ | |
protected abstract set fileContents(data: T); | |
} | |
/** Cache manager for text files | |
* | |
* @property {string} filePath Path to the file whose contents are to be cached | |
* @property {Promise<string>} dataPromise Asynchronous getter for data | |
* @property {string} data Synchronous getter for data | |
* @property {Promise<void>} set Asynchronous setter for data | |
* @property {string} data Synchronous setter for data | |
*/ | |
export class TextCacheManager extends ReadWriteFileCacheManager<string> { | |
protected get fileContentsPromise() { | |
return Deno.readTextFile(this.filePath); | |
} | |
protected get fileContents() { | |
return Deno.readTextFileSync(this.filePath); | |
} | |
protected set fileContents(data: string) { | |
Deno.writeTextFileSync(this.filePath, data); | |
} | |
protected writeFileContents(data: string) { | |
return Deno.writeTextFile(this.filePath, data); | |
} | |
} | |
/** Cache manager for JSON files | |
* | |
* @typeParam T Type of the data to be cached | |
* @property {string} filePath Path to the file whose contents are to be cached | |
* @property {Promise<T>} dataPromise Asynchronous getter for data | |
* @property {T} data Synchronous getter for data | |
* @property {Promise<void>} set Asynchronous setter for data | |
* @property {T} data Synchronous setter for data | |
*/ | |
export class JSONCacheManager<T extends object> extends ReadWriteFileCacheManager<T> { | |
protected get fileContentsPromise() { | |
return Deno.readTextFile(this.filePath).then((contents) => JSON.parse(contents)); | |
} | |
protected get fileContents() { | |
return JSON.parse(Deno.readTextFileSync(this.filePath)); | |
} | |
protected set fileContents(data: T) { | |
Deno.writeTextFileSync(this.filePath, JSON.stringify(data)); | |
} | |
protected writeFileContents(data: T) { | |
return Deno.writeTextFile(this.filePath, JSON.stringify(data)); | |
} | |
} | |
/** Cache manager for raw binary files | |
* | |
* @property {string} filePath Path to the file whose contents are to be cached | |
* @property {Promise<Uint8Array>} dataPromise Asynchronous getter for data | |
* @property {Uint8Array} data Synchronous getter for data | |
* @property {Promise<void>} set Asynchronous setter for data | |
* @property {Uint8Array} data Synchronous setter for data | |
*/ | |
export class BinaryCacheManager extends ReadWriteFileCacheManager<Uint8Array> { | |
protected get fileContentsPromise() { | |
return Deno.readFile(this.filePath); | |
} | |
protected get fileContents() { | |
return Deno.readFileSync(this.filePath); | |
} | |
protected set fileContents(data: Uint8Array) { | |
Deno.writeFileSync(this.filePath, data); | |
} | |
protected writeFileContents(data: Uint8Array) { | |
return Deno.writeFile(this.filePath, data); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ReadWriteFileCacheManager } from "./FileCacheManager.ts"; | |
/** Cache manager for JSON files | |
* | |
* @typeParam T Type of the data to be cached | |
* @property {string} filePath Path to the file whose contents are to be cached | |
* @property {Promise<T>} dataPromise Asynchronous getter for data | |
* @property {T} data Synchronous getter for data | |
* @property {Promise<void>} set Asynchronous setter for data | |
* @property {T} data Synchronous setter for data | |
*/ | |
export class JSONFileCacheManager<T extends object> extends ReadWriteFileCacheManager<T> { | |
protected get fileContentsPromise() { | |
return Deno.readTextFile(this.filePath).then((contents) => JSON.parse(contents)); | |
} | |
protected get fileContents() { | |
return JSON.parse(Deno.readTextFileSync(this.filePath)); | |
} | |
protected set fileContents(data: T) { | |
Deno.writeTextFileSync(this.filePath, JSON.stringify(data)); | |
} | |
protected writeFileContents(data: T) { | |
return Deno.writeTextFile(this.filePath, JSON.stringify(data)); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import CacheManager from './CacheManager.ts' | |
export default interface ReadWriteCacheManager<T> extends CacheManager<T> { | |
set data(data: T) | |
/** Asynchronously set the data in the cache and write it to file | |
* @async | |
*/ | |
set(data: T): Promise<void> | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ReadWriteFileCacheManager } from "./FileCacheManager.ts"; | |
/** Cache manager for text files | |
* | |
* @property {string} filePath Path to the file whose contents are to be cached | |
* @property {Promise<string>} dataPromise Asynchronous getter for data | |
* @property {string} data Synchronous getter for data | |
* @property {Promise<void>} set Asynchronous setter for data | |
* @property {string} data Synchronous setter for data | |
*/ | |
export class TextFileCacheManager extends ReadWriteFileCacheManager<string> { | |
protected get fileContentsPromise() { | |
return Deno.readTextFile(this.filePath); | |
} | |
protected get fileContents() { | |
return Deno.readTextFileSync(this.filePath); | |
} | |
protected set fileContents(data: string) { | |
Deno.writeTextFileSync(this.filePath, data); | |
} | |
protected writeFileContents(data: string) { | |
return Deno.writeTextFile(this.filePath, data); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment