Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ lib
node_modules
npm-debug.log
/.tmp
yarn.lock
package-lock.json
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
#### Changed
#### Deprecated
#### Removed
- Removed `fs-extra` dependency to fix dynamic require issues in ES modules
#### Fixed
- Fixed "Dynamic require of 'fs' is not supported" error when using the library in ES module environments
- Updated deprecated `fs.rmdir` calls to use `fs.rm` for better Node.js compatibility
#### Security


Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "file-system-cache",
"version": "3.0.0",
"name": "@Latermedia/file-system-cache",
"version": "3.2.0-later.0",
"description": "A super-fast, promise-based cache that reads and writes to the file-system.",
"keywords": [
"cache",
Expand All @@ -10,7 +10,10 @@
"homepage": "https://github.com/philcockfield/file-system-cache",
"repository": {
"type": "git",
"url": "https://github.com/philcockfield/file-system-cache"
"url": "git+https://github.com/Latermedia/file-system-cache"
},
"publishConfig": {
"registry": "https://npm.pkg.github.com"
},
"license": "MIT",
"author": {
Expand Down Expand Up @@ -41,10 +44,8 @@
"devDependencies": {
"@types/chai": "^4.3.16",
"@types/expect": "^24.3.0",
"@types/fs-extra": "^11.0.4",
"@types/ramda": "^0.30.1",
"chai": "^5.1.1",
"fs-extra": "^11.2.0",
"prettier": "^3.3.3",
"ramda": "^0.30.1",
"tsup": "^8.1.0",
Expand Down
119 changes: 105 additions & 14 deletions src/FileSystemCache.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as fs from 'node:fs';
import * as fse from 'fs-extra/esm';
import { R, Util, hashAlgorithms, type t } from './common/index';

/**
Expand Down Expand Up @@ -68,14 +67,14 @@ export class FileSystemCache {
* @param {string} key: The key of the cache item.
*/
public fileExists(key: string) {
return fse.pathExists(this.path(key));
return Util.pathExists(this.path(key));
}

/**
* Ensure that the base path exists.
*/
public async ensureBasePath() {
if (!this.basePathExists) await fse.ensureDir(this.basePath);
if (!this.basePathExists) await Util.ensureDir(this.basePath);
this.basePathExists = true;
}

Expand All @@ -86,7 +85,7 @@ export class FileSystemCache {
* @return File contents, or
* undefined if the file does not exist.
*/
public get(key: string, defaultValue?: any) {
public async get<T = any>(key: string, defaultValue?: T | (() => T) | (() => Promise<T>)): Promise<T | undefined> {
return Util.getValueP(this.path(key), defaultValue);
}

Expand All @@ -96,21 +95,41 @@ export class FileSystemCache {
* @param defaultValue: Optional. A default value to return if the value does not exist in cache.
* @return the cached value, or undefined.
*/
public getSync(key: string, defaultValue?: any) {
const path = this.path(key);
return fs.existsSync(path) ? Util.toGetValue(fse.readJsonSync(path)) : defaultValue;
public getSync<T = any>(key: string, defaultValue?: T): T | undefined {
const value = Util.storedValue(this.path(key));
const valueExpired = value && Util.isExpired(value);

if (value && !valueExpired) {
return Util.toGetValue(value);
}

if (valueExpired) {
Util.removeSync(this.path(key));
}

if (typeof defaultValue === 'function') {
return (defaultValue as Function)();
}
return defaultValue;
}

/**
* Writes the given value to the file-system.
* @param {string} key: The key of the cache item.
* @param value: The value to write (Primitive or Object).
*/
public async set(key: string, value: any, ttl?: number) {
public async set<T = any>(key: string, value: T | (() => T) | (() => Promise<T>), ttl?: number) {
const path = this.path(key);
ttl = typeof ttl === 'number' ? ttl : this.ttl;
await this.ensureBasePath();
await fse.outputFile(path, Util.toJson(value, ttl));
let valueToWrite;
if (typeof value === 'function') {
const result = (value as Function)();
valueToWrite = result instanceof Promise ? await result : result;
} else {
valueToWrite = value;
}
await Util.outputFile(path, Util.toJson(valueToWrite, ttl));
return { path };
}

Expand All @@ -120,32 +139,91 @@ export class FileSystemCache {
* @param value: The value to write (Primitive or Object).
* @return the cache.
*/
public setSync(key: string, value: any, ttl?: number) {
public setSync<T = any>(key: string, value: T | (() => T), ttl?: number) {
ttl = typeof ttl === 'number' ? ttl : this.ttl;
fse.outputFileSync(this.path(key), Util.toJson(value, ttl));
let valueToWrite;
if (typeof value === 'function') {
valueToWrite = (value as Function)();
} else {
valueToWrite = value;
}
Util.outputFileSync(this.path(key), Util.toJson(valueToWrite, ttl));
return this;
}

/**
* Returns the cached value or sets the default value and returns it.
* @param {string} key: The key of the cache item.
* @param defaultValue: The value to write (Primitive, Object, or function).
* @return the cached value or the default value.
*/
public async fetch<T = any>(key: string, defaultValue: T | (() => T) | (() => Promise<T>), ttl?: number): Promise<T | undefined> {
let value = await this.get(key);

// return cached value
if (value !== undefined) {
return value;
}

if (typeof defaultValue === 'function') {
const result = (defaultValue as Function)();
value = result instanceof Promise ? await result : result;
} else {
value = defaultValue;
}

// save value to cache
await this.set(key, value, ttl);

return value;
}

/**
* Returns the cached value or sets the default value and returns it.
* @param {string} key: The key of the cache item.
* @param defaultValue: The value to write (Primitive, Object, or function).
* @return the cached value or the default value.
*/
public fetchSync<T = any>(key: string, defaultValue: T | (() => T), ttl?: number): T | undefined {
let value = this.getSync(key);

// return cached value
if (value !== undefined) {
return value;
}

if (typeof defaultValue === 'function') {
value = (defaultValue as Function)();
} else {
value = defaultValue;
}

// save value to cache
this.setSync(key, value, ttl);

return value;
}

/**
* Removes the item from the file-system.
* @param {string} key: The key of the cache item.
*/
public remove(key: string) {
return fse.remove(this.path(key));
return Util.remove(this.path(key));
}

/**
* Removes all items from the cache.
*/
public async clear() {
const paths = await Util.filePathsP(this.basePath, this.ns);
await Promise.all(paths.map((path) => fse.remove(path)));
await Promise.all(paths.map((path) => Util.remove(path)));
console.groupEnd();
}

/**
* Saves several items to the cache in one operation.
* @param {array} items: An array of objects of the form { key, value }.
* @param {array} input: An array of objects of the form { key, value }.
*/
public async save(input: ({ key: string; value: any } | null | undefined)[]): Promise<{ paths: string[] }> {
type Item = { key: string; value: any };
Expand Down Expand Up @@ -180,6 +258,19 @@ export class FileSystemCache {
const files = await Promise.all(paths.map(async (path) => ({ path, value: await Util.getValueP(path) })));
return { files };
}

/**
* Optimizes the cache by removing all files that are older than the given time-to-live.
*/
public async optimize() {
const paths = await Util.filePathsP(this.basePath, this.ns);
await Promise.all(paths.map((path) => {
const value = Util.storedValue(path);
if (value && Util.isExpired(value)) {
return Util.remove(path);
}
}));
}
}

/**
Expand Down
Loading