@chriscdn/file-cache 0.0.3 → 0.0.4

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/lib/index.d.ts CHANGED
@@ -6,8 +6,8 @@ export type FileCacheOptions<T extends Record<string, any>> = {
6
6
  autoCreateCachePath?: boolean;
7
7
  cb: (filePath: FilePath, context: T, cache: FileCache<T>) => Promise<void>;
8
8
  ext: string;
9
- cleanupInterval?: Milliseconds;
10
9
  ttl: Milliseconds;
10
+ cleanupInterval?: Milliseconds;
11
11
  };
12
12
  declare class FileCache<T extends Record<string, any>> {
13
13
  private _cachePath;
@@ -61,6 +61,5 @@ declare class FileCache<T extends Record<string, any>> {
61
61
  * @returns The full path to the cached file
62
62
  */
63
63
  resolveFilePath(args: T): FilePath;
64
- resolveCreateFilePath(args: T): Promise<FilePath>;
65
64
  }
66
65
  export { type DirectoryPath, FileCache, type FilePath };
package/lib/index.js CHANGED
@@ -10,7 +10,6 @@ import { rimraf } from "rimraf";
10
10
  const fsp = fs.promises;
11
11
  class FileCache {
12
12
  _cachePath;
13
- // private _resolveFileName: FileCacheOptions<T>["resolveFileName"];
14
13
  _cb;
15
14
  _ext;
16
15
  _ttl;
@@ -20,26 +19,19 @@ class FileCache {
20
19
  _cleanupSemaphore = new Semaphore();
21
20
  constructor({ cachePath, autoCreateCachePath, cb, ext, ttl, cleanupInterval }) {
22
21
  this._cachePath = cachePath;
23
- // this._resolveFileName = resolveFileName;
24
22
  this._cb = cb;
25
23
  this._ext = ext;
26
24
  this._ttl = ttl;
27
25
  this._cleanupInterval = cleanupInterval ??
28
26
  Duration.toMilliseconds({ days: 1 });
29
- (async () => {
30
- if (!(await pathExists(cachePath))) {
31
- throw new Error("💥 FileCache error: cachePath does not exist.");
32
- // process.exit(1); // or throw new Error() if you want to let it bubble
33
- }
34
- })();
35
27
  if ((pathExistsSync(cachePath))) {
28
+ // we're good
36
29
  }
37
30
  else if (autoCreateCachePath) {
38
31
  fs.mkdirSync(cachePath, { recursive: true });
39
32
  }
40
33
  else {
41
34
  throw new Error("💥 FileCache error: cachePath does not exist.");
42
- // process.exit(1); // or throw new Error() if you want to let it bubble
43
35
  }
44
36
  // fire and forget
45
37
  this.cleanup();
@@ -77,14 +69,14 @@ class FileCache {
77
69
  try {
78
70
  await Promise.all([
79
71
  this._semaphore.acquire(filePath),
80
- this._cleanupSemaphore.wait(),
72
+ this._cleanupSemaphore.wait(), // wait for any cleanup task to end
81
73
  ]);
82
74
  if (await pathExists(filePath)) {
83
75
  // fire and forget
84
76
  touch(filePath);
85
77
  }
86
78
  else {
87
- await fs.mkdirSync(path.dirname(filePath), { recursive: true });
79
+ await fsp.mkdir(path.dirname(filePath), { recursive: true });
88
80
  await this._cb(filePath, args, this);
89
81
  }
90
82
  }
@@ -105,12 +97,18 @@ class FileCache {
105
97
  */
106
98
  async expire(args) {
107
99
  const filePath = this.resolveFilePath(args);
108
- if (await pathExists(filePath)) {
109
- await rimraf(filePath);
110
- return true;
100
+ try {
101
+ await this._semaphore.acquire(filePath);
102
+ if (await pathExists(filePath)) {
103
+ await rimraf(filePath);
104
+ return true;
105
+ }
106
+ else {
107
+ return false;
108
+ }
111
109
  }
112
- else {
113
- return false;
110
+ finally {
111
+ this._semaphore.release(filePath);
114
112
  }
115
113
  }
116
114
  /**
@@ -131,13 +129,9 @@ class FileCache {
131
129
  resolveFileName(args) {
132
130
  const baseName = sha1(JSON.stringify(args));
133
131
  return path.format({
134
- // dir: path.dirname(filePath),
135
132
  name: baseName,
136
133
  ext: this._ext,
137
134
  });
138
- // return this._resolveFileName
139
- // ? this._resolveFileName(args)
140
- // : `${sha1(JSON.stringify(args, Object.keys(args).sort()))}${this._ext}`;
141
135
  }
142
136
  /**
143
137
  * Resolves the full file path for the cached file based on the provided arguments.
@@ -152,10 +146,6 @@ class FileCache {
152
146
  fileName);
153
147
  return filePath;
154
148
  }
155
- async resolveCreateFilePath(args) {
156
- const filePath = this.resolveFilePath(args);
157
- await fsp.mkdir(path.dirname(filePath), { recursive: true });
158
- return filePath;
159
- }
160
149
  }
161
150
  export { FileCache };
151
+ //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1,7 +1 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/index.ts"],
4
- "sourcesContent": ["import path from \"path\";\nimport fs from \"fs\";\nimport { pathExists, pathExistsSync } from \"path-exists\";\nimport sha1 from \"sha1\";\nimport Semaphore from \"@chriscdn/promise-semaphore\";\nimport touch from \"touch\";\nimport { findNuke } from \"@chriscdn/find-nuke\";\nimport { Duration } from \"@chriscdn/duration\";\nimport { rimraf } from \"rimraf\";\n\nconst fsp = fs.promises;\n\ntype DirectoryPath = string;\ntype FilePath = string;\ntype Milliseconds = number;\n\nexport type FileCacheOptions<T extends Record<string, any>> = {\n cachePath: DirectoryPath;\n autoCreateCachePath?: boolean;\n cb: (filePath: FilePath, context: T, cache: FileCache<T>) => Promise<void>;\n ext: string;\n cleanupInterval?: Milliseconds;\n ttl: Milliseconds;\n // resolveFileName?: (args: T) => FilePath;\n};\n\nclass FileCache<T extends Record<string, any>> {\n private _cachePath: FileCacheOptions<T>[\"cachePath\"];\n // private _resolveFileName: FileCacheOptions<T>[\"resolveFileName\"];\n private _cb: FileCacheOptions<T>[\"cb\"];\n private _ext: FileCacheOptions<T>[\"ext\"];\n private _ttl: FileCacheOptions<T>[\"ttl\"];\n private _cleanupInterval: FileCacheOptions<T>[\"cleanupInterval\"];\n\n private _intervalId: ReturnType<typeof setInterval> | null = null;\n\n private _semaphore: Semaphore = new Semaphore();\n private _cleanupSemaphore: Semaphore = new Semaphore();\n\n constructor(\n { cachePath, autoCreateCachePath, cb, ext, ttl, cleanupInterval }:\n FileCacheOptions<T>,\n ) {\n this._cachePath = cachePath;\n // this._resolveFileName = resolveFileName;\n this._cb = cb;\n this._ext = ext;\n this._ttl = ttl;\n this._cleanupInterval = cleanupInterval ??\n Duration.toMilliseconds({ days: 1 });\n\n (async () => {\n if (!(await pathExists(cachePath))) {\n throw new Error(\"\uD83D\uDCA5 FileCache error: cachePath does not exist.\");\n // process.exit(1); // or throw new Error() if you want to let it bubble\n }\n })();\n\n if ((pathExistsSync(cachePath))) {\n } else if (autoCreateCachePath) {\n fs.mkdirSync(cachePath, { recursive: true });\n } else {\n throw new Error(\"\uD83D\uDCA5 FileCache error: cachePath does not exist.\");\n // process.exit(1); // or throw new Error() if you want to let it bubble\n }\n\n // fire and forget\n this.cleanup();\n\n this._intervalId = setInterval(\n this.cleanup.bind(this),\n this._cleanupInterval,\n );\n }\n\n /**\n * Runs a cleanup pass to remove files older than 1 day from the cache.\n * This is called periodically, but can be triggered manually.\n */\n async cleanup() {\n try {\n await this._cleanupSemaphore.acquire();\n await findNuke(this._cachePath, {\n olderThan: this._ttl,\n verbose: true,\n deleteEmptyDirectories: true,\n });\n } finally {\n this._cleanupSemaphore.release();\n }\n }\n\n /**\n * Retrieves a cached file for the given arguments.\n * If the file doesn't exist, it is generated by the callback and stored.\n *\n * Concurrency-safe, ensuring no conflicts when multiple requests are made\n * for the same file.\n *\n * @param args The arguments used to determine the file's identity and generation\n * @returns The path to the cached or newly created file\n */\n async getFile(args: T): Promise<FilePath> {\n const filePath = this.resolveFilePath(args);\n\n try {\n await Promise.all([\n this._semaphore.acquire(filePath),\n this._cleanupSemaphore.wait(),\n ]);\n\n if (await pathExists(filePath)) {\n // fire and forget\n touch(filePath);\n } else {\n await fs.mkdirSync(path.dirname(filePath), { recursive: true });\n await this._cb(filePath, args, this);\n }\n } finally {\n this._semaphore.release(filePath);\n }\n\n return filePath;\n }\n\n async has(args: T): Promise<boolean> {\n const filePath = this.resolveFilePath(args);\n return await pathExists(filePath);\n }\n\n /**\n * Deletes a file from the cache if it exists.\n *\n * @param args The arguments used to determine the file's identity\n * @returns True if the file was deleted, false if it didn't exist\n */\n async expire(args: T): Promise<boolean> {\n const filePath = this.resolveFilePath(args);\n\n if (await pathExists(filePath)) {\n await rimraf(filePath);\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * Stops the background cleanup process, but does not delete any cached files.\n */\n destroy() {\n if (this._intervalId) {\n clearInterval(this._intervalId);\n this._intervalId = null;\n }\n }\n\n /**\n * Resolves the filename for the cache based on the provided arguments.\n *\n * Uses the provided `resolveFileName` function if available, or falls back to\n * generating a hash from the arguments to create a unique file name.\n */\n private resolveFileName(args: T): string {\n const baseName = sha1(JSON.stringify(args));\n\n return path.format({\n // dir: path.dirname(filePath),\n name: baseName,\n ext: this._ext,\n });\n\n // return this._resolveFileName\n // ? this._resolveFileName(args)\n // : `${sha1(JSON.stringify(args, Object.keys(args).sort()))}${this._ext}`;\n }\n\n /**\n * Resolves the full file path for the cached file based on the provided arguments.\n *\n * @param args The arguments used to resolve the file path\n * @returns The full path to the cached file\n */\n resolveFilePath(args: T): FilePath {\n const fileName = this.resolveFileName(args);\n\n const filePath = path.resolve(\n this._cachePath,\n fileName[0],\n fileName[1],\n fileName[2],\n // fileName[3],\n fileName,\n );\n\n return filePath;\n }\n\n async resolveCreateFilePath(args: T): Promise<FilePath> {\n const filePath = this.resolveFilePath(args);\n\n await fsp.mkdir(path.dirname(filePath), { recursive: true });\n\n return filePath;\n }\n}\n\nexport { type DirectoryPath, FileCache, type FilePath };\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
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",
3
+ "version": "0.0.4",
4
4
  "description": "File cache ",
5
5
  "repository": "https://github.com/chriscdn/file-cache",
6
6
  "author": "Christopher Meyer <chris@schwiiz.org>",
@@ -14,8 +14,7 @@
14
14
  "default": "./lib/index.js"
15
15
  },
16
16
  "scripts": {
17
- "build": "rimraf ./lib/ && node ./esbuild.mjs && tsc --declaration",
18
- "dev": "microbundle watch",
17
+ "build": "rimraf ./lib/ && tsc",
19
18
  "test": "vitest"
20
19
  },
21
20
  "dependencies": {
@@ -29,9 +28,9 @@
29
28
  "touch": "^3.1.1"
30
29
  },
31
30
  "devDependencies": {
31
+ "@tsconfig/node22": "^22.0.1",
32
32
  "@types/sha1": "^1.1.5",
33
33
  "@types/touch": "^3.1.5",
34
- "esbuild": "^0.25.3",
35
34
  "typescript": "^5.8.3",
36
35
  "vitest": "^3.1.2"
37
36
  }
package/src/index.ts CHANGED
@@ -19,14 +19,13 @@ export type FileCacheOptions<T extends Record<string, any>> = {
19
19
  autoCreateCachePath?: boolean;
20
20
  cb: (filePath: FilePath, context: T, cache: FileCache<T>) => Promise<void>;
21
21
  ext: string;
22
- cleanupInterval?: Milliseconds;
23
22
  ttl: Milliseconds;
24
- // resolveFileName?: (args: T) => FilePath;
23
+ cleanupInterval?: Milliseconds;
25
24
  };
26
25
 
27
26
  class FileCache<T extends Record<string, any>> {
28
27
  private _cachePath: FileCacheOptions<T>["cachePath"];
29
- // private _resolveFileName: FileCacheOptions<T>["resolveFileName"];
28
+
30
29
  private _cb: FileCacheOptions<T>["cb"];
31
30
  private _ext: FileCacheOptions<T>["ext"];
32
31
  private _ttl: FileCacheOptions<T>["ttl"];
@@ -42,26 +41,18 @@ class FileCache<T extends Record<string, any>> {
42
41
  FileCacheOptions<T>,
43
42
  ) {
44
43
  this._cachePath = cachePath;
45
- // this._resolveFileName = resolveFileName;
46
44
  this._cb = cb;
47
45
  this._ext = ext;
48
46
  this._ttl = ttl;
49
47
  this._cleanupInterval = cleanupInterval ??
50
48
  Duration.toMilliseconds({ days: 1 });
51
49
 
52
- (async () => {
53
- if (!(await pathExists(cachePath))) {
54
- throw new Error("💥 FileCache error: cachePath does not exist.");
55
- // process.exit(1); // or throw new Error() if you want to let it bubble
56
- }
57
- })();
58
-
59
50
  if ((pathExistsSync(cachePath))) {
51
+ // we're good
60
52
  } else if (autoCreateCachePath) {
61
53
  fs.mkdirSync(cachePath, { recursive: true });
62
54
  } else {
63
55
  throw new Error("💥 FileCache error: cachePath does not exist.");
64
- // process.exit(1); // or throw new Error() if you want to let it bubble
65
56
  }
66
57
 
67
58
  // fire and forget
@@ -106,14 +97,14 @@ class FileCache<T extends Record<string, any>> {
106
97
  try {
107
98
  await Promise.all([
108
99
  this._semaphore.acquire(filePath),
109
- this._cleanupSemaphore.wait(),
100
+ this._cleanupSemaphore.wait(), // wait for any cleanup task to end
110
101
  ]);
111
102
 
112
103
  if (await pathExists(filePath)) {
113
104
  // fire and forget
114
105
  touch(filePath);
115
106
  } else {
116
- await fs.mkdirSync(path.dirname(filePath), { recursive: true });
107
+ await fsp.mkdir(path.dirname(filePath), { recursive: true });
117
108
  await this._cb(filePath, args, this);
118
109
  }
119
110
  } finally {
@@ -137,11 +128,17 @@ class FileCache<T extends Record<string, any>> {
137
128
  async expire(args: T): Promise<boolean> {
138
129
  const filePath = this.resolveFilePath(args);
139
130
 
140
- if (await pathExists(filePath)) {
141
- await rimraf(filePath);
142
- return true;
143
- } else {
144
- return false;
131
+ try {
132
+ await this._semaphore.acquire(filePath);
133
+
134
+ if (await pathExists(filePath)) {
135
+ await rimraf(filePath);
136
+ return true;
137
+ } else {
138
+ return false;
139
+ }
140
+ } finally {
141
+ this._semaphore.release(filePath);
145
142
  }
146
143
  }
147
144
 
@@ -165,14 +162,9 @@ class FileCache<T extends Record<string, any>> {
165
162
  const baseName = sha1(JSON.stringify(args));
166
163
 
167
164
  return path.format({
168
- // dir: path.dirname(filePath),
169
165
  name: baseName,
170
166
  ext: this._ext,
171
167
  });
172
-
173
- // return this._resolveFileName
174
- // ? this._resolveFileName(args)
175
- // : `${sha1(JSON.stringify(args, Object.keys(args).sort()))}${this._ext}`;
176
168
  }
177
169
 
178
170
  /**
@@ -196,13 +188,13 @@ class FileCache<T extends Record<string, any>> {
196
188
  return filePath;
197
189
  }
198
190
 
199
- async resolveCreateFilePath(args: T): Promise<FilePath> {
200
- const filePath = this.resolveFilePath(args);
191
+ // async resolveCreateFilePath(args: T): Promise<FilePath> {
192
+ // const filePath = this.resolveFilePath(args);
201
193
 
202
- await fsp.mkdir(path.dirname(filePath), { recursive: true });
194
+ // await fsp.mkdir(path.dirname(filePath), { recursive: true });
203
195
 
204
- return filePath;
205
- }
196
+ // return filePath;
197
+ // }
206
198
  }
207
199
 
208
200
  export { type DirectoryPath, FileCache, type FilePath };
package/tsconfig.json CHANGED
@@ -1,15 +1,11 @@
1
1
  {
2
+ "extends": "@tsconfig/node22/tsconfig.json",
2
3
  "compilerOptions": {
3
- "target": "ESNext",
4
- "lib": ["ESNext"],
5
- "module": "ESNext",
6
- "moduleResolution": "node",
7
- "strict": true,
8
4
  "allowSyntheticDefaultImports": true,
9
5
  "outDir": "./lib",
10
- "declaration": true, // Generate declaration files
6
+ "declaration": true,
11
7
  "declarationDir": "./lib",
12
- "skipLibCheck": true
8
+ "sourceMap": true
13
9
  },
14
10
  "include": ["./src/**/*"]
15
11
  }
package/esbuild.mjs DELETED
@@ -1,19 +0,0 @@
1
- import { build } from "esbuild";
2
-
3
- import pkg from "./package.json" with { type: "json" };
4
-
5
- const external = [
6
- ...Object.keys(pkg.dependencies || {}),
7
- ...Object.keys(pkg.devDependencies || {}),
8
- ];
9
-
10
- build({
11
- entryPoints: ["./src/index.ts"],
12
- bundle: true,
13
- platform: "node",
14
- format: "esm", // or 'esm' if you prefer
15
- outfile: "./lib/index.js",
16
- external,
17
- minify: false,
18
- sourcemap: true,
19
- }).catch(() => process.exit(1));