@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 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 each cached file.
65
- // Currently, all files share the same extension (this may change in the future).
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
 
@@ -18,7 +18,7 @@ describe("Cache Expiration", async () => {
18
18
  filePath,
19
19
  );
20
20
  },
21
- ext: "pdf",
21
+ ext: () => ".pdf",
22
22
  ttl: Duration.toMilliseconds({ seconds: 1 }),
23
23
  cleanupInterval: Duration.toMilliseconds({ seconds: 2 }),
24
24
  });
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, context: T, cache: FileCache<T>) => Promise<void>;
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
- constructor({ cachePath, autoCreateCachePath, cb, ext, ttl, cleanupInterval }) {
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 baseName = sha1(JSON.stringify(args));
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: baseName,
133
- ext: this._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":"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"}
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.4",
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/promise-semaphore": "^2.0.11",
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, context: T, cache: FileCache<T>) => Promise<void>;
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
- private _ext: FileCacheOptions<T>["ext"];
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
- { cachePath, autoCreateCachePath, cb, ext, ttl, cleanupInterval }:
41
- FileCacheOptions<T>,
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 baseName = sha1(JSON.stringify(args));
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: baseName,
166
- ext: this._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,