@chriscdn/file-cache 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/__tests__/file-cache.test.ts +1 -1
- package/lib/index.d.ts +7 -5
- package/lib/index.js +21 -11
- package/lib/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +39 -15
package/README.md
CHANGED
|
@@ -53,6 +53,9 @@ const options: FileCacheOptions<MyCallbackArgs> = {
|
|
|
53
53
|
// anything else in this directory.
|
|
54
54
|
cachePath: "/some/path/on/your/filesystem/",
|
|
55
55
|
|
|
56
|
+
// optional, automatically create cachePath if it doesn't exist
|
|
57
|
+
autoCreateCachePath: false,
|
|
58
|
+
|
|
56
59
|
// An asynchronous callback function, which is executed if no matching cached
|
|
57
60
|
// file is found. It is the responsibility of this function to use `context`
|
|
58
61
|
// to write a file to `filePath`.
|
|
@@ -61,9 +64,8 @@ const options: FileCacheOptions<MyCallbackArgs> = {
|
|
|
61
64
|
await downloadAndConvertToJPG(url, filePath);
|
|
62
65
|
},
|
|
63
66
|
|
|
64
|
-
// Specifies the file extension for
|
|
65
|
-
|
|
66
|
-
ext: ".jpg",
|
|
67
|
+
// Specifies the file extension for the cached file. Can be synchronous or asynchronous.
|
|
68
|
+
ext: (context: MyCallbackArgs) => ".jpg",
|
|
67
69
|
|
|
68
70
|
// Determines the time-to-live (TTL) of a cached file, in milliseconds,
|
|
69
71
|
// based on when it was last accessed.
|
|
@@ -84,6 +86,7 @@ const imageFilePath = await imageCache({
|
|
|
84
86
|
## Cleanup
|
|
85
87
|
|
|
86
88
|
`FileCache` instances are intended to be instantiated as singletons, persisting throughout the lifecycle of your app. If you need to deallocate an instance, be sure to call the `destroy()` method to prevent a memory leak.
|
|
89
|
+
This does not cleanup the cache directory.
|
|
87
90
|
|
|
88
91
|
## Tests
|
|
89
92
|
|
package/lib/index.d.ts
CHANGED
|
@@ -4,21 +4,23 @@ type Milliseconds = number;
|
|
|
4
4
|
export type FileCacheOptions<T extends Record<string, any>> = {
|
|
5
5
|
cachePath: DirectoryPath;
|
|
6
6
|
autoCreateCachePath?: boolean;
|
|
7
|
-
cb: (filePath: FilePath,
|
|
8
|
-
ext: string
|
|
7
|
+
cb: (filePath: FilePath, args: T, cache: FileCache<T>) => Promise<void>;
|
|
8
|
+
ext: (args: T) => string | Promise<string>;
|
|
9
|
+
resolveCacheFileKey?: (args: T) => string | Promise<string>;
|
|
9
10
|
ttl: Milliseconds;
|
|
10
11
|
cleanupInterval?: Milliseconds;
|
|
11
12
|
};
|
|
12
13
|
declare class FileCache<T extends Record<string, any>> {
|
|
13
14
|
private _cachePath;
|
|
14
15
|
private _cb;
|
|
15
|
-
private _ext;
|
|
16
16
|
private _ttl;
|
|
17
17
|
private _cleanupInterval;
|
|
18
|
+
private _resolveCacheFileKey;
|
|
19
|
+
private _ext;
|
|
18
20
|
private _intervalId;
|
|
19
21
|
private _semaphore;
|
|
20
22
|
private _cleanupSemaphore;
|
|
21
|
-
constructor({ cachePath, autoCreateCachePath, cb, ext, ttl, cleanupInterval }: FileCacheOptions<T>);
|
|
23
|
+
constructor({ cachePath, autoCreateCachePath, cb, ext, ttl, cleanupInterval, resolveCacheFileKey, }: FileCacheOptions<T>);
|
|
22
24
|
/**
|
|
23
25
|
* Runs a cleanup pass to remove files older than 1 day from the cache.
|
|
24
26
|
* This is called periodically, but can be triggered manually.
|
|
@@ -60,6 +62,6 @@ declare class FileCache<T extends Record<string, any>> {
|
|
|
60
62
|
* @param args The arguments used to resolve the file path
|
|
61
63
|
* @returns The full path to the cached file
|
|
62
64
|
*/
|
|
63
|
-
resolveFilePath(args: T): FilePath
|
|
65
|
+
resolveFilePath(args: T): Promise<FilePath>;
|
|
64
66
|
}
|
|
65
67
|
export { type DirectoryPath, FileCache, type FilePath };
|
package/lib/index.js
CHANGED
|
@@ -7,23 +7,28 @@ import touch from "touch";
|
|
|
7
7
|
import { findNuke } from "@chriscdn/find-nuke";
|
|
8
8
|
import { Duration } from "@chriscdn/duration";
|
|
9
9
|
import { rimraf } from "rimraf";
|
|
10
|
+
import { Memoize } from "@chriscdn/memoize";
|
|
10
11
|
const fsp = fs.promises;
|
|
11
12
|
class FileCache {
|
|
12
13
|
_cachePath;
|
|
13
14
|
_cb;
|
|
14
|
-
_ext;
|
|
15
15
|
_ttl;
|
|
16
16
|
_cleanupInterval;
|
|
17
|
+
_resolveCacheFileKey;
|
|
18
|
+
_ext;
|
|
17
19
|
_intervalId = null;
|
|
18
20
|
_semaphore = new Semaphore();
|
|
19
21
|
_cleanupSemaphore = new Semaphore();
|
|
20
|
-
|
|
22
|
+
// private _fileNameResover(args:T) =>
|
|
23
|
+
constructor({ cachePath, autoCreateCachePath, cb, ext, ttl, cleanupInterval, resolveCacheFileKey, }) {
|
|
21
24
|
this._cachePath = cachePath;
|
|
22
25
|
this._cb = cb;
|
|
23
26
|
this._ext = ext;
|
|
24
27
|
this._ttl = ttl;
|
|
25
28
|
this._cleanupInterval = cleanupInterval ??
|
|
26
29
|
Duration.toMilliseconds({ days: 1 });
|
|
30
|
+
this._resolveCacheFileKey = resolveCacheFileKey ??
|
|
31
|
+
((args) => sha1(JSON.stringify(args)));
|
|
27
32
|
if ((pathExistsSync(cachePath))) {
|
|
28
33
|
// we're good
|
|
29
34
|
}
|
|
@@ -36,6 +41,8 @@ class FileCache {
|
|
|
36
41
|
// fire and forget
|
|
37
42
|
this.cleanup();
|
|
38
43
|
this._intervalId = setInterval(this.cleanup.bind(this), this._cleanupInterval);
|
|
44
|
+
this.resolveFileName = Memoize(this.resolveFileName.bind(this));
|
|
45
|
+
this.resolveFilePath = Memoize(this.resolveFilePath.bind(this));
|
|
39
46
|
}
|
|
40
47
|
/**
|
|
41
48
|
* Runs a cleanup pass to remove files older than 1 day from the cache.
|
|
@@ -65,7 +72,7 @@ class FileCache {
|
|
|
65
72
|
* @returns The path to the cached or newly created file
|
|
66
73
|
*/
|
|
67
74
|
async getFile(args) {
|
|
68
|
-
const filePath = this.resolveFilePath(args);
|
|
75
|
+
const filePath = await this.resolveFilePath(args);
|
|
69
76
|
try {
|
|
70
77
|
await Promise.all([
|
|
71
78
|
this._semaphore.acquire(filePath),
|
|
@@ -86,7 +93,7 @@ class FileCache {
|
|
|
86
93
|
return filePath;
|
|
87
94
|
}
|
|
88
95
|
async has(args) {
|
|
89
|
-
const filePath = this.resolveFilePath(args);
|
|
96
|
+
const filePath = await this.resolveFilePath(args);
|
|
90
97
|
return await pathExists(filePath);
|
|
91
98
|
}
|
|
92
99
|
/**
|
|
@@ -96,7 +103,7 @@ class FileCache {
|
|
|
96
103
|
* @returns True if the file was deleted, false if it didn't exist
|
|
97
104
|
*/
|
|
98
105
|
async expire(args) {
|
|
99
|
-
const filePath = this.resolveFilePath(args);
|
|
106
|
+
const filePath = await this.resolveFilePath(args);
|
|
100
107
|
try {
|
|
101
108
|
await this._semaphore.acquire(filePath);
|
|
102
109
|
if (await pathExists(filePath)) {
|
|
@@ -126,11 +133,14 @@ class FileCache {
|
|
|
126
133
|
* Uses the provided `resolveFileName` function if available, or falls back to
|
|
127
134
|
* generating a hash from the arguments to create a unique file name.
|
|
128
135
|
*/
|
|
129
|
-
resolveFileName(args) {
|
|
130
|
-
const
|
|
136
|
+
async resolveFileName(args) {
|
|
137
|
+
const [name, ext] = await Promise.all([
|
|
138
|
+
this._resolveCacheFileKey(args),
|
|
139
|
+
this._ext(args),
|
|
140
|
+
]);
|
|
131
141
|
return path.format({
|
|
132
|
-
name
|
|
133
|
-
ext
|
|
142
|
+
name,
|
|
143
|
+
ext,
|
|
134
144
|
});
|
|
135
145
|
}
|
|
136
146
|
/**
|
|
@@ -139,8 +149,8 @@ class FileCache {
|
|
|
139
149
|
* @param args The arguments used to resolve the file path
|
|
140
150
|
* @returns The full path to the cached file
|
|
141
151
|
*/
|
|
142
|
-
resolveFilePath(args) {
|
|
143
|
-
const fileName = this.resolveFileName(args);
|
|
152
|
+
async resolveFilePath(args) {
|
|
153
|
+
const fileName = await this.resolveFileName(args);
|
|
144
154
|
const filePath = path.resolve(this._cachePath, fileName[0], fileName[1], fileName[2],
|
|
145
155
|
// fileName[3],
|
|
146
156
|
fileName);
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,SAAS,MAAM,6BAA6B,CAAC;AACpD,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chriscdn/file-cache",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "File cache ",
|
|
5
5
|
"repository": "https://github.com/chriscdn/file-cache",
|
|
6
6
|
"author": "Christopher Meyer <chris@schwiiz.org>",
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
"module": "./lib/index.js",
|
|
11
11
|
"types": "./lib/index.d.ts",
|
|
12
12
|
"exports": {
|
|
13
|
-
"types": "./lib/index.d.ts",
|
|
14
13
|
"default": "./lib/index.js"
|
|
15
14
|
},
|
|
16
15
|
"scripts": {
|
|
@@ -20,7 +19,8 @@
|
|
|
20
19
|
"dependencies": {
|
|
21
20
|
"@chriscdn/duration": "^1.0.3",
|
|
22
21
|
"@chriscdn/find-nuke": "^0.0.4",
|
|
23
|
-
"@chriscdn/
|
|
22
|
+
"@chriscdn/memoize": "^1.0.9",
|
|
23
|
+
"@chriscdn/promise-semaphore": "^2.0.12",
|
|
24
24
|
"path-exists": "^5.0.0",
|
|
25
25
|
"rimraf": "^6.0.1",
|
|
26
26
|
"sha1": "^1.1.1",
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import touch from "touch";
|
|
|
7
7
|
import { findNuke } from "@chriscdn/find-nuke";
|
|
8
8
|
import { Duration } from "@chriscdn/duration";
|
|
9
9
|
import { rimraf } from "rimraf";
|
|
10
|
-
|
|
10
|
+
import { Memoize } from "@chriscdn/memoize";
|
|
11
11
|
const fsp = fs.promises;
|
|
12
12
|
|
|
13
13
|
type DirectoryPath = string;
|
|
@@ -17,8 +17,9 @@ type Milliseconds = number;
|
|
|
17
17
|
export type FileCacheOptions<T extends Record<string, any>> = {
|
|
18
18
|
cachePath: DirectoryPath;
|
|
19
19
|
autoCreateCachePath?: boolean;
|
|
20
|
-
cb: (filePath: FilePath,
|
|
21
|
-
ext: string
|
|
20
|
+
cb: (filePath: FilePath, args: T, cache: FileCache<T>) => Promise<void>;
|
|
21
|
+
ext: (args: T) => string | Promise<string>;
|
|
22
|
+
resolveCacheFileKey?: (args: T) => string | Promise<string>;
|
|
22
23
|
ttl: Milliseconds;
|
|
23
24
|
cleanupInterval?: Milliseconds;
|
|
24
25
|
};
|
|
@@ -27,18 +28,32 @@ class FileCache<T extends Record<string, any>> {
|
|
|
27
28
|
private _cachePath: FileCacheOptions<T>["cachePath"];
|
|
28
29
|
|
|
29
30
|
private _cb: FileCacheOptions<T>["cb"];
|
|
30
|
-
|
|
31
|
+
|
|
31
32
|
private _ttl: FileCacheOptions<T>["ttl"];
|
|
32
33
|
private _cleanupInterval: FileCacheOptions<T>["cleanupInterval"];
|
|
33
34
|
|
|
35
|
+
private _resolveCacheFileKey: Required<
|
|
36
|
+
FileCacheOptions<T>
|
|
37
|
+
>["resolveCacheFileKey"];
|
|
38
|
+
private _ext: FileCacheOptions<T>["ext"];
|
|
39
|
+
|
|
34
40
|
private _intervalId: ReturnType<typeof setInterval> | null = null;
|
|
35
41
|
|
|
36
42
|
private _semaphore: Semaphore = new Semaphore();
|
|
37
43
|
private _cleanupSemaphore: Semaphore = new Semaphore();
|
|
38
44
|
|
|
45
|
+
// private _fileNameResover(args:T) =>
|
|
46
|
+
|
|
39
47
|
constructor(
|
|
40
|
-
{
|
|
41
|
-
|
|
48
|
+
{
|
|
49
|
+
cachePath,
|
|
50
|
+
autoCreateCachePath,
|
|
51
|
+
cb,
|
|
52
|
+
ext,
|
|
53
|
+
ttl,
|
|
54
|
+
cleanupInterval,
|
|
55
|
+
resolveCacheFileKey,
|
|
56
|
+
}: FileCacheOptions<T>,
|
|
42
57
|
) {
|
|
43
58
|
this._cachePath = cachePath;
|
|
44
59
|
this._cb = cb;
|
|
@@ -47,6 +62,9 @@ class FileCache<T extends Record<string, any>> {
|
|
|
47
62
|
this._cleanupInterval = cleanupInterval ??
|
|
48
63
|
Duration.toMilliseconds({ days: 1 });
|
|
49
64
|
|
|
65
|
+
this._resolveCacheFileKey = resolveCacheFileKey ??
|
|
66
|
+
((args: T) => sha1(JSON.stringify(args)));
|
|
67
|
+
|
|
50
68
|
if ((pathExistsSync(cachePath))) {
|
|
51
69
|
// we're good
|
|
52
70
|
} else if (autoCreateCachePath) {
|
|
@@ -62,6 +80,9 @@ class FileCache<T extends Record<string, any>> {
|
|
|
62
80
|
this.cleanup.bind(this),
|
|
63
81
|
this._cleanupInterval,
|
|
64
82
|
);
|
|
83
|
+
|
|
84
|
+
this.resolveFileName = Memoize(this.resolveFileName.bind(this));
|
|
85
|
+
this.resolveFilePath = Memoize(this.resolveFilePath.bind(this));
|
|
65
86
|
}
|
|
66
87
|
|
|
67
88
|
/**
|
|
@@ -92,7 +113,7 @@ class FileCache<T extends Record<string, any>> {
|
|
|
92
113
|
* @returns The path to the cached or newly created file
|
|
93
114
|
*/
|
|
94
115
|
async getFile(args: T): Promise<FilePath> {
|
|
95
|
-
const filePath = this.resolveFilePath(args);
|
|
116
|
+
const filePath = await this.resolveFilePath(args);
|
|
96
117
|
|
|
97
118
|
try {
|
|
98
119
|
await Promise.all([
|
|
@@ -115,7 +136,7 @@ class FileCache<T extends Record<string, any>> {
|
|
|
115
136
|
}
|
|
116
137
|
|
|
117
138
|
async has(args: T): Promise<boolean> {
|
|
118
|
-
const filePath = this.resolveFilePath(args);
|
|
139
|
+
const filePath = await this.resolveFilePath(args);
|
|
119
140
|
return await pathExists(filePath);
|
|
120
141
|
}
|
|
121
142
|
|
|
@@ -126,7 +147,7 @@ class FileCache<T extends Record<string, any>> {
|
|
|
126
147
|
* @returns True if the file was deleted, false if it didn't exist
|
|
127
148
|
*/
|
|
128
149
|
async expire(args: T): Promise<boolean> {
|
|
129
|
-
const filePath = this.resolveFilePath(args);
|
|
150
|
+
const filePath = await this.resolveFilePath(args);
|
|
130
151
|
|
|
131
152
|
try {
|
|
132
153
|
await this._semaphore.acquire(filePath);
|
|
@@ -158,12 +179,15 @@ class FileCache<T extends Record<string, any>> {
|
|
|
158
179
|
* Uses the provided `resolveFileName` function if available, or falls back to
|
|
159
180
|
* generating a hash from the arguments to create a unique file name.
|
|
160
181
|
*/
|
|
161
|
-
private resolveFileName(args: T): string {
|
|
162
|
-
const
|
|
182
|
+
private async resolveFileName(args: T): Promise<string> {
|
|
183
|
+
const [name, ext] = await Promise.all([
|
|
184
|
+
this._resolveCacheFileKey(args),
|
|
185
|
+
this._ext(args),
|
|
186
|
+
]);
|
|
163
187
|
|
|
164
188
|
return path.format({
|
|
165
|
-
name
|
|
166
|
-
ext
|
|
189
|
+
name,
|
|
190
|
+
ext,
|
|
167
191
|
});
|
|
168
192
|
}
|
|
169
193
|
|
|
@@ -173,8 +197,8 @@ class FileCache<T extends Record<string, any>> {
|
|
|
173
197
|
* @param args The arguments used to resolve the file path
|
|
174
198
|
* @returns The full path to the cached file
|
|
175
199
|
*/
|
|
176
|
-
resolveFilePath(args: T): FilePath {
|
|
177
|
-
const fileName = this.resolveFileName(args);
|
|
200
|
+
async resolveFilePath(args: T): Promise<FilePath> {
|
|
201
|
+
const fileName = await this.resolveFileName(args);
|
|
178
202
|
|
|
179
203
|
const filePath = path.resolve(
|
|
180
204
|
this._cachePath,
|