@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 +1 -2
- package/lib/index.js +15 -25
- package/lib/index.js.map +1 -7
- package/package.json +3 -4
- package/src/index.ts +21 -29
- package/tsconfig.json +3 -7
- package/esbuild.mjs +0 -19
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
|
|
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
|
-
|
|
109
|
-
await
|
|
110
|
-
|
|
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
|
-
|
|
113
|
-
|
|
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": ";AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,SAAS,YAAY,sBAAsB;AAC3C,OAAO,UAAU;AACjB,OAAO,eAAe;AACtB,OAAO,WAAW;AAClB,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AACzB,SAAS,cAAc;AAEvB,IAAM,MAAM,GAAG;AAgBf,IAAM,YAAN,MAA+C;AAAA,EACrC;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,cAAqD;AAAA,EAErD,aAAwB,IAAI,UAAU;AAAA,EACtC,oBAA+B,IAAI,UAAU;AAAA,EAErD,YACE,EAAE,WAAW,qBAAqB,IAAI,KAAK,KAAK,gBAAgB,GAEhE;AACA,SAAK,aAAa;AAElB,SAAK,MAAM;AACX,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,mBAAmB,mBACtB,SAAS,eAAe,EAAE,MAAM,EAAE,CAAC;AAErC,KAAC,YAAY;AACX,UAAI,CAAE,MAAM,WAAW,SAAS,GAAI;AAClC,cAAM,IAAI,MAAM,sDAA+C;AAAA,MAEjE;AAAA,IACF,GAAG;AAEH,QAAK,eAAe,SAAS,GAAI;AAAA,IACjC,WAAW,qBAAqB;AAC9B,SAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,IAC7C,OAAO;AACL,YAAM,IAAI,MAAM,sDAA+C;AAAA,IAEjE;AAGA,SAAK,QAAQ;AAEb,SAAK,cAAc;AAAA,MACjB,KAAK,QAAQ,KAAK,IAAI;AAAA,MACtB,KAAK;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UAAU;AACd,QAAI;AACF,YAAM,KAAK,kBAAkB,QAAQ;AACrC,YAAM,SAAS,KAAK,YAAY;AAAA,QAC9B,WAAW,KAAK;AAAA,QAChB,SAAS;AAAA,QACT,wBAAwB;AAAA,MAC1B,CAAC;AAAA,IACH,UAAE;AACA,WAAK,kBAAkB,QAAQ;AAAA,IACjC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,QAAQ,MAA4B;AACxC,UAAM,WAAW,KAAK,gBAAgB,IAAI;AAE1C,QAAI;AACF,YAAM,QAAQ,IAAI;AAAA,QAChB,KAAK,WAAW,QAAQ,QAAQ;AAAA,QAChC,KAAK,kBAAkB,KAAK;AAAA,MAC9B,CAAC;AAED,UAAI,MAAM,WAAW,QAAQ,GAAG;AAE9B,cAAM,QAAQ;AAAA,MAChB,OAAO;AACL,cAAM,GAAG,UAAU,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9D,cAAM,KAAK,IAAI,UAAU,MAAM,IAAI;AAAA,MACrC;AAAA,IACF,UAAE;AACA,WAAK,WAAW,QAAQ,QAAQ;AAAA,IAClC;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,IAAI,MAA2B;AACnC,UAAM,WAAW,KAAK,gBAAgB,IAAI;AAC1C,WAAO,MAAM,WAAW,QAAQ;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,MAA2B;AACtC,UAAM,WAAW,KAAK,gBAAgB,IAAI;AAE1C,QAAI,MAAM,WAAW,QAAQ,GAAG;AAC9B,YAAM,OAAO,QAAQ;AACrB,aAAO;AAAA,IACT,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU;AACR,QAAI,KAAK,aAAa;AACpB,oBAAc,KAAK,WAAW;AAC9B,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,gBAAgB,MAAiB;AACvC,UAAM,WAAW,KAAK,KAAK,UAAU,IAAI,CAAC;AAE1C,WAAO,KAAK,OAAO;AAAA;AAAA,MAEjB,MAAM;AAAA,MACN,KAAK,KAAK;AAAA,IACZ,CAAC;AAAA,EAKH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,gBAAgB,MAAmB;AACjC,UAAM,WAAW,KAAK,gBAAgB,IAAI;AAE1C,UAAM,WAAW,KAAK;AAAA,MACpB,KAAK;AAAA,MACL,SAAS,CAAC;AAAA,MACV,SAAS,CAAC;AAAA,MACV,SAAS,CAAC;AAAA;AAAA,MAEV;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,sBAAsB,MAA4B;AACtD,UAAM,WAAW,KAAK,gBAAgB,IAAI;AAE1C,UAAM,IAAI,MAAM,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAE3D,WAAO;AAAA,EACT;AACF;",
|
|
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
|
+
"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/ &&
|
|
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
|
-
|
|
23
|
+
cleanupInterval?: Milliseconds;
|
|
25
24
|
};
|
|
26
25
|
|
|
27
26
|
class FileCache<T extends Record<string, any>> {
|
|
28
27
|
private _cachePath: FileCacheOptions<T>["cachePath"];
|
|
29
|
-
|
|
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
|
|
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
|
-
|
|
141
|
-
await
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
191
|
+
// async resolveCreateFilePath(args: T): Promise<FilePath> {
|
|
192
|
+
// const filePath = this.resolveFilePath(args);
|
|
201
193
|
|
|
202
|
-
|
|
194
|
+
// await fsp.mkdir(path.dirname(filePath), { recursive: true });
|
|
203
195
|
|
|
204
|
-
|
|
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,
|
|
6
|
+
"declaration": true,
|
|
11
7
|
"declarationDir": "./lib",
|
|
12
|
-
"
|
|
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));
|