file-entry-cache 11.1.3 → 11.1.5

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/dist/index.js DELETED
@@ -1,525 +0,0 @@
1
- // src/index.ts
2
- import crypto from "crypto";
3
- import fs from "fs";
4
- import path from "path";
5
- import {
6
- createFromFile as createFlatCacheFile,
7
- FlatCache
8
- } from "flat-cache";
9
- function createFromFile(filePath, options) {
10
- const fname = path.basename(filePath);
11
- const directory = path.dirname(filePath);
12
- return create(fname, directory, options);
13
- }
14
- function create(cacheId, cacheDirectory, options) {
15
- const opts = {
16
- ...options,
17
- cache: {
18
- cacheId,
19
- cacheDir: cacheDirectory
20
- }
21
- };
22
- const fileEntryCache = new FileEntryCache(opts);
23
- if (cacheDirectory) {
24
- const cachePath = `${cacheDirectory}/${cacheId}`;
25
- if (fs.existsSync(cachePath)) {
26
- fileEntryCache.cache = createFlatCacheFile(cachePath, opts.cache);
27
- }
28
- }
29
- return fileEntryCache;
30
- }
31
- var FileEntryDefault = class {
32
- static create = create;
33
- static createFromFile = createFromFile;
34
- };
35
- var FileEntryCache = class {
36
- _cache = new FlatCache({ useClone: false });
37
- _useCheckSum = false;
38
- _hashAlgorithm = "md5";
39
- _cwd = process.cwd();
40
- _restrictAccessToCwd = false;
41
- _logger;
42
- _useAbsolutePathAsKey = false;
43
- _useModifiedTime = true;
44
- /**
45
- * Create a new FileEntryCache instance
46
- * @param options - The options for the FileEntryCache (all properties are optional with defaults)
47
- */
48
- constructor(options) {
49
- if (options?.cache) {
50
- this._cache = new FlatCache(options.cache);
51
- }
52
- if (options?.useCheckSum) {
53
- this._useCheckSum = options.useCheckSum;
54
- }
55
- if (options?.hashAlgorithm) {
56
- this._hashAlgorithm = options.hashAlgorithm;
57
- }
58
- if (options?.cwd) {
59
- this._cwd = options.cwd;
60
- }
61
- if (options?.useModifiedTime !== void 0) {
62
- this._useModifiedTime = options.useModifiedTime;
63
- }
64
- if (options?.restrictAccessToCwd !== void 0) {
65
- this._restrictAccessToCwd = options.restrictAccessToCwd;
66
- }
67
- if (options?.useAbsolutePathAsKey !== void 0) {
68
- this._useAbsolutePathAsKey = options.useAbsolutePathAsKey;
69
- }
70
- if (options?.logger) {
71
- this._logger = options.logger;
72
- }
73
- }
74
- /**
75
- * Get the cache
76
- * @returns {FlatCache} The cache
77
- */
78
- get cache() {
79
- return this._cache;
80
- }
81
- /**
82
- * Set the cache
83
- * @param {FlatCache} cache - The cache to set
84
- */
85
- set cache(cache) {
86
- this._cache = cache;
87
- }
88
- /**
89
- * Get the logger
90
- * @returns {ILogger | undefined} The logger instance
91
- */
92
- get logger() {
93
- return this._logger;
94
- }
95
- /**
96
- * Set the logger
97
- * @param {ILogger | undefined} logger - The logger to set
98
- */
99
- set logger(logger) {
100
- this._logger = logger;
101
- }
102
- /**
103
- * Use the hash to check if the file has changed
104
- * @returns {boolean} if the hash is used to check if the file has changed (default: false)
105
- */
106
- get useCheckSum() {
107
- return this._useCheckSum;
108
- }
109
- /**
110
- * Set the useCheckSum value
111
- * @param {boolean} value - The value to set
112
- */
113
- set useCheckSum(value) {
114
- this._useCheckSum = value;
115
- }
116
- /**
117
- * Get the hash algorithm
118
- * @returns {string} The hash algorithm (default: 'md5')
119
- */
120
- get hashAlgorithm() {
121
- return this._hashAlgorithm;
122
- }
123
- /**
124
- * Set the hash algorithm
125
- * @param {string} value - The value to set
126
- */
127
- set hashAlgorithm(value) {
128
- this._hashAlgorithm = value;
129
- }
130
- /**
131
- * Get the current working directory
132
- * @returns {string} The current working directory (default: process.cwd())
133
- */
134
- get cwd() {
135
- return this._cwd;
136
- }
137
- /**
138
- * Set the current working directory
139
- * @param {string} value - The value to set
140
- */
141
- set cwd(value) {
142
- this._cwd = value;
143
- }
144
- /**
145
- * Get whether to use modified time for change detection
146
- * @returns {boolean} Whether modified time (mtime) is used for change detection (default: true)
147
- */
148
- get useModifiedTime() {
149
- return this._useModifiedTime;
150
- }
151
- /**
152
- * Set whether to use modified time for change detection
153
- * @param {boolean} value - The value to set
154
- */
155
- set useModifiedTime(value) {
156
- this._useModifiedTime = value;
157
- }
158
- /**
159
- * Get whether to restrict paths to cwd boundaries
160
- * @returns {boolean} Whether strict path checking is enabled (default: true)
161
- */
162
- get restrictAccessToCwd() {
163
- return this._restrictAccessToCwd;
164
- }
165
- /**
166
- * Set whether to restrict paths to cwd boundaries
167
- * @param {boolean} value - The value to set
168
- */
169
- set restrictAccessToCwd(value) {
170
- this._restrictAccessToCwd = value;
171
- }
172
- /**
173
- * Get whether to use absolute path as cache key
174
- * @returns {boolean} Whether cache keys use absolute paths (default: false)
175
- */
176
- get useAbsolutePathAsKey() {
177
- return this._useAbsolutePathAsKey;
178
- }
179
- /**
180
- * Set whether to use absolute path as cache key
181
- * @param {boolean} value - The value to set
182
- */
183
- set useAbsolutePathAsKey(value) {
184
- this._useAbsolutePathAsKey = value;
185
- }
186
- /**
187
- * Given a buffer, calculate md5 hash of its content.
188
- * @method getHash
189
- * @param {Buffer} buffer buffer to calculate hash on
190
- * @return {String} content hash digest
191
- */
192
- getHash(buffer) {
193
- return crypto.createHash(this._hashAlgorithm).update(buffer).digest("hex");
194
- }
195
- /**
196
- * Create the key for the file path used for caching.
197
- * @method createFileKey
198
- * @param {String} filePath
199
- * @return {String}
200
- */
201
- createFileKey(filePath) {
202
- let result = filePath;
203
- if (this._useAbsolutePathAsKey && this.isRelativePath(filePath)) {
204
- result = this.getAbsolutePathWithCwd(filePath, this._cwd);
205
- }
206
- return result;
207
- }
208
- /**
209
- * Check if the file path is a relative path
210
- * @method isRelativePath
211
- * @param filePath - The file path to check
212
- * @returns {boolean} if the file path is a relative path, false otherwise
213
- */
214
- isRelativePath(filePath) {
215
- return !path.isAbsolute(filePath);
216
- }
217
- /**
218
- * Delete the cache file from the disk
219
- * @method deleteCacheFile
220
- * @return {boolean} true if the file was deleted, false otherwise
221
- */
222
- deleteCacheFile() {
223
- return this._cache.removeCacheFile();
224
- }
225
- /**
226
- * Remove the cache from the file and clear the memory cache
227
- * @method destroy
228
- */
229
- destroy() {
230
- this._cache.destroy();
231
- }
232
- /**
233
- * Remove and Entry From the Cache
234
- * @method removeEntry
235
- * @param filePath - The file path to remove from the cache
236
- */
237
- removeEntry(filePath) {
238
- const key = this.createFileKey(filePath);
239
- this._cache.removeKey(key);
240
- }
241
- /**
242
- * Reconcile the cache
243
- * @method reconcile
244
- */
245
- reconcile() {
246
- const { items } = this._cache;
247
- for (const item of items) {
248
- const fileDescriptor = this.getFileDescriptor(item.key);
249
- if (fileDescriptor.notFound) {
250
- this._cache.removeKey(item.key);
251
- }
252
- }
253
- this._cache.save();
254
- }
255
- /**
256
- * Check if the file has changed
257
- * @method hasFileChanged
258
- * @param filePath - The file path to check
259
- * @returns {boolean} if the file has changed, false otherwise
260
- */
261
- hasFileChanged(filePath) {
262
- let result = false;
263
- const fileDescriptor = this.getFileDescriptor(filePath);
264
- if ((!fileDescriptor.err || !fileDescriptor.notFound) && fileDescriptor.changed) {
265
- result = true;
266
- }
267
- return result;
268
- }
269
- /**
270
- * Get the file descriptor for the file path
271
- * @method getFileDescriptor
272
- * @param filePath - The file path to get the file descriptor for
273
- * @param options - The options for getting the file descriptor
274
- * @returns The file descriptor
275
- */
276
- getFileDescriptor(filePath, options) {
277
- this._logger?.debug({ filePath, options }, "Getting file descriptor");
278
- let fstat;
279
- const result = {
280
- key: this.createFileKey(filePath),
281
- changed: false,
282
- meta: {}
283
- };
284
- this._logger?.trace({ key: result.key }, "Created file key");
285
- const metaCache = this._cache.getKey(result.key);
286
- if (metaCache) {
287
- this._logger?.trace({ metaCache }, "Found cached meta");
288
- } else {
289
- this._logger?.trace("No cached meta found");
290
- }
291
- result.meta = metaCache ? { ...metaCache } : {};
292
- const absolutePath = this.getAbsolutePath(filePath);
293
- this._logger?.trace({ absolutePath }, "Resolved absolute path");
294
- const useCheckSumValue = options?.useCheckSum ?? this._useCheckSum;
295
- this._logger?.debug(
296
- { useCheckSum: useCheckSumValue },
297
- "Using checksum setting"
298
- );
299
- const useModifiedTimeValue = options?.useModifiedTime ?? this.useModifiedTime;
300
- this._logger?.debug(
301
- { useModifiedTime: useModifiedTimeValue },
302
- "Using modified time (mtime) setting"
303
- );
304
- try {
305
- fstat = fs.statSync(absolutePath);
306
- result.meta.size = fstat.size;
307
- result.meta.mtime = fstat.mtime.getTime();
308
- this._logger?.trace(
309
- { size: result.meta.size, mtime: result.meta.mtime },
310
- "Read file stats"
311
- );
312
- if (useCheckSumValue) {
313
- const buffer = fs.readFileSync(absolutePath);
314
- result.meta.hash = this.getHash(buffer);
315
- this._logger?.trace({ hash: result.meta.hash }, "Calculated file hash");
316
- }
317
- } catch (error) {
318
- this._logger?.error({ filePath, error }, "Error reading file");
319
- this.removeEntry(filePath);
320
- let notFound = false;
321
- if (error.message.includes("ENOENT")) {
322
- notFound = true;
323
- this._logger?.debug({ filePath }, "File not found");
324
- }
325
- return {
326
- key: result.key,
327
- err: error,
328
- notFound,
329
- meta: {}
330
- };
331
- }
332
- if (!metaCache) {
333
- result.changed = true;
334
- this._cache.setKey(result.key, result.meta);
335
- this._logger?.debug({ filePath }, "File not in cache, marked as changed");
336
- return result;
337
- }
338
- if (useModifiedTimeValue && metaCache?.mtime !== result.meta?.mtime) {
339
- result.changed = true;
340
- this._logger?.debug(
341
- { filePath, oldMtime: metaCache.mtime, newMtime: result.meta.mtime },
342
- "File changed: mtime differs"
343
- );
344
- }
345
- if (metaCache?.size !== result.meta?.size) {
346
- result.changed = true;
347
- this._logger?.debug(
348
- { filePath, oldSize: metaCache.size, newSize: result.meta.size },
349
- "File changed: size differs"
350
- );
351
- }
352
- if (useCheckSumValue && metaCache?.hash !== result.meta?.hash) {
353
- result.changed = true;
354
- this._logger?.debug(
355
- { filePath, oldHash: metaCache.hash, newHash: result.meta.hash },
356
- "File changed: hash differs"
357
- );
358
- }
359
- this._cache.setKey(result.key, result.meta);
360
- if (result.changed) {
361
- this._logger?.info({ filePath }, "File has changed");
362
- } else {
363
- this._logger?.debug({ filePath }, "File unchanged");
364
- }
365
- return result;
366
- }
367
- /**
368
- * Get the file descriptors for the files
369
- * @method normalizeEntries
370
- * @param files?: string[] - The files to get the file descriptors for
371
- * @returns The file descriptors
372
- */
373
- normalizeEntries(files) {
374
- const result = [];
375
- if (files) {
376
- for (const file of files) {
377
- const fileDescriptor = this.getFileDescriptor(file);
378
- result.push(fileDescriptor);
379
- }
380
- return result;
381
- }
382
- const keys = this.cache.keys();
383
- for (const key of keys) {
384
- const fileDescriptor = this.getFileDescriptor(key);
385
- if (!fileDescriptor.notFound && !fileDescriptor.err) {
386
- result.push(fileDescriptor);
387
- }
388
- }
389
- return result;
390
- }
391
- /**
392
- * Analyze the files
393
- * @method analyzeFiles
394
- * @param files - The files to analyze
395
- * @returns {AnalyzedFiles} The analysis of the files
396
- */
397
- analyzeFiles(files) {
398
- const result = {
399
- changedFiles: [],
400
- notFoundFiles: [],
401
- notChangedFiles: []
402
- };
403
- const fileDescriptors = this.normalizeEntries(files);
404
- for (const fileDescriptor of fileDescriptors) {
405
- if (fileDescriptor.notFound) {
406
- result.notFoundFiles.push(fileDescriptor.key);
407
- } else if (fileDescriptor.changed) {
408
- result.changedFiles.push(fileDescriptor.key);
409
- } else {
410
- result.notChangedFiles.push(fileDescriptor.key);
411
- }
412
- }
413
- return result;
414
- }
415
- /**
416
- * Get the updated files
417
- * @method getUpdatedFiles
418
- * @param files - The files to get the updated files for
419
- * @returns {string[]} The updated files
420
- */
421
- getUpdatedFiles(files) {
422
- const result = [];
423
- const fileDescriptors = this.normalizeEntries(files);
424
- for (const fileDescriptor of fileDescriptors) {
425
- if (fileDescriptor.changed) {
426
- result.push(fileDescriptor.key);
427
- }
428
- }
429
- return result;
430
- }
431
- /**
432
- * Get the file descriptors by path prefix
433
- * @method getFileDescriptorsByPath
434
- * @param filePath - the path prefix to match
435
- * @returns {FileDescriptor[]} The file descriptors
436
- */
437
- getFileDescriptorsByPath(filePath) {
438
- const result = [];
439
- const keys = this._cache.keys();
440
- for (const key of keys) {
441
- if (key.startsWith(filePath)) {
442
- const fileDescriptor = this.getFileDescriptor(key);
443
- result.push(fileDescriptor);
444
- }
445
- }
446
- return result;
447
- }
448
- /**
449
- * Get the Absolute Path. If it is already absolute it will return the path as is.
450
- * When restrictAccessToCwd is enabled, ensures the resolved path stays within cwd boundaries.
451
- * @method getAbsolutePath
452
- * @param filePath - The file path to get the absolute path for
453
- * @returns {string}
454
- * @throws {Error} When restrictAccessToCwd is true and path would resolve outside cwd
455
- */
456
- getAbsolutePath(filePath) {
457
- if (this.isRelativePath(filePath)) {
458
- const sanitizedPath = filePath.replace(/\0/g, "");
459
- const resolved = path.resolve(this._cwd, sanitizedPath);
460
- if (this._restrictAccessToCwd) {
461
- const normalizedResolved = path.normalize(resolved);
462
- const normalizedCwd = path.normalize(this._cwd);
463
- const isWithinCwd = normalizedResolved === normalizedCwd || normalizedResolved.startsWith(normalizedCwd + path.sep);
464
- if (!isWithinCwd) {
465
- throw new Error(
466
- `Path traversal attempt blocked: "${filePath}" resolves outside of working directory "${this._cwd}"`
467
- );
468
- }
469
- }
470
- return resolved;
471
- }
472
- return filePath;
473
- }
474
- /**
475
- * Get the Absolute Path with a custom working directory. If it is already absolute it will return the path as is.
476
- * When restrictAccessToCwd is enabled, ensures the resolved path stays within the provided cwd boundaries.
477
- * @method getAbsolutePathWithCwd
478
- * @param filePath - The file path to get the absolute path for
479
- * @param cwd - The custom working directory to resolve relative paths from
480
- * @returns {string}
481
- * @throws {Error} When restrictAccessToCwd is true and path would resolve outside the provided cwd
482
- */
483
- getAbsolutePathWithCwd(filePath, cwd) {
484
- if (this.isRelativePath(filePath)) {
485
- const sanitizedPath = filePath.replace(/\0/g, "");
486
- const resolved = path.resolve(cwd, sanitizedPath);
487
- if (this._restrictAccessToCwd) {
488
- const normalizedResolved = path.normalize(resolved);
489
- const normalizedCwd = path.normalize(cwd);
490
- const isWithinCwd = normalizedResolved === normalizedCwd || normalizedResolved.startsWith(normalizedCwd + path.sep);
491
- if (!isWithinCwd) {
492
- throw new Error(
493
- `Path traversal attempt blocked: "${filePath}" resolves outside of working directory "${cwd}"`
494
- );
495
- }
496
- }
497
- return resolved;
498
- }
499
- return filePath;
500
- }
501
- /**
502
- * Rename cache keys that start with a given path prefix.
503
- * @method renameCacheKeys
504
- * @param oldPath - The old path prefix to rename
505
- * @param newPath - The new path prefix to rename to
506
- */
507
- renameCacheKeys(oldPath, newPath) {
508
- const keys = this._cache.keys();
509
- for (const key of keys) {
510
- if (key.startsWith(oldPath)) {
511
- const newKey = key.replace(oldPath, newPath);
512
- const meta = this._cache.getKey(key);
513
- this._cache.removeKey(key);
514
- this._cache.setKey(newKey, meta);
515
- }
516
- }
517
- }
518
- };
519
- export {
520
- FileEntryCache,
521
- create,
522
- createFromFile,
523
- FileEntryDefault as default
524
- };
525
- /* v8 ignore next -- @preserve */