nestjs-env-getter 1.0.0-beta.2 → 1.0.0-beta.3

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/CHANGELOG.md CHANGED
@@ -7,6 +7,17 @@ All notable changes to this project will be documented in this file.
7
7
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
8
8
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
9
 
10
+ ## [1.0.0-beta.3] - 2025-11-26
11
+
12
+ ### Added
13
+
14
+ - Improved file watcher resilience: re-establishes the file watcher after each successful update to handle atomic file replacements (e.g., Vault Agent credential rotations).
15
+
16
+ ### Changed
17
+
18
+ - Increased default `debounceMs` from `200` to `350` ms to reduce noisy re-parses on rapid file updates.
19
+ - Documentation and unit tests updated to cover file replacement and re-establishment behavior.
20
+
10
21
  ## [1.0.0-beta.2] - 2025-10-17
11
22
 
12
23
  ### Added
package/README.md CHANGED
@@ -272,7 +272,7 @@ Load and validate configuration from JSON files with automatic file watching and
272
272
  { enabled: false },
273
273
  );
274
274
 
275
- // Custom debounce timing (default is 200ms)
275
+ // Custom debounce timing (default is 350ms)
276
276
  const config = this.envGetter.getRequiredConfigFromFile(
277
277
  "config.json",
278
278
  undefined,
@@ -381,14 +381,14 @@ When the file watcher detects changes, it updates the **same object instance** i
381
381
  **File Watcher Options:**
382
382
 
383
383
  - `enabled` (boolean, default: `true`): Enable or disable automatic file watching
384
- - `debounceMs` (number, default: `200`): Delay in milliseconds before re-reading the file after a change
384
+ - `debounceMs` (number, default: `350`): Delay in milliseconds before re-reading the file after a change
385
385
 
386
386
  **Features:**
387
387
 
388
388
  - ✅ Supports both absolute and relative paths (relative to `process.cwd()`)
389
389
  - ✅ Automatic JSON parsing and validation
390
390
  - ✅ **Hot-reload with reference preservation** - updates existing object instances in-place
391
- - ✅ File watching with automatic change detection
391
+ - ✅ File watching with automatic change detection and file replacement support (e.g., Vault agent flow)
392
392
  - ✅ Debouncing to prevent excessive re-reads
393
393
  - ✅ Class-based validation with constructor pattern
394
394
  - ✅ **Graceful error handling for optional configs** - missing files or JSON parsing errors return default values
@@ -345,6 +345,7 @@ export declare class EnvGetterService implements OnModuleDestroy {
345
345
  * Sets up a file watcher for a configuration file.
346
346
  * - Watches for file changes and automatically re-reads and updates the cached config.
347
347
  * - Applies debouncing to avoid excessive re-reads.
348
+ * - Re-establishes the watcher after each successful update to handle file replacements (e.g., Vault agent flow).
348
349
  * - Emits 'updated' or 'error' events on re-parse.
349
350
  * @param filePath - The absolute path to the config file.
350
351
  * @param cls - (Optional) A class constructor to validate and instantiate the parsed config.
@@ -599,6 +599,7 @@ let EnvGetterService = class EnvGetterService {
599
599
  * Sets up a file watcher for a configuration file.
600
600
  * - Watches for file changes and automatically re-reads and updates the cached config.
601
601
  * - Applies debouncing to avoid excessive re-reads.
602
+ * - Re-establishes the watcher after each successful update to handle file replacements (e.g., Vault agent flow).
602
603
  * - Emits 'updated' or 'error' events on re-parse.
603
604
  * @param filePath - The absolute path to the config file.
604
605
  * @param cls - (Optional) A class constructor to validate and instantiate the parsed config.
@@ -610,20 +611,43 @@ let EnvGetterService = class EnvGetterService {
610
611
  var _a, _b, _c;
611
612
  const watcherOptions = {
612
613
  enabled: (_a = options === null || options === void 0 ? void 0 : options.enabled) !== null && _a !== void 0 ? _a : true,
613
- debounceMs: (_b = options === null || options === void 0 ? void 0 : options.debounceMs) !== null && _b !== void 0 ? _b : 200,
614
+ debounceMs: (_b = options === null || options === void 0 ? void 0 : options.debounceMs) !== null && _b !== void 0 ? _b : 350,
614
615
  breakOnError: (_c = options === null || options === void 0 ? void 0 : options.breakOnError) !== null && _c !== void 0 ? _c : true,
615
616
  };
616
- if (!watcherOptions.enabled || this.fileWatchers.has(filePath))
617
+ if (!watcherOptions.enabled)
617
618
  return;
619
+ // Close existing watcher if present (for re-establishment)
620
+ const existingWatcher = this.fileWatchers.get(filePath);
621
+ if (existingWatcher) {
622
+ existingWatcher.close();
623
+ this.fileWatchers.delete(filePath);
624
+ }
618
625
  let debounceTimer = null;
619
626
  const watcher = (0, fs_1.watch)(filePath, (eventType) => {
620
- if (eventType === "change") {
627
+ // Handle both "change" and "rename" events to catch file replacements
628
+ if (eventType === "change" || eventType === "rename") {
621
629
  if (debounceTimer)
622
630
  clearTimeout(debounceTimer);
623
631
  debounceTimer = setTimeout(() => {
632
+ // Verify file still exists before attempting to read
633
+ if (!(0, fs_1.existsSync)(filePath)) {
634
+ // File was deleted, emit error event
635
+ const errorEvent = {
636
+ filePath,
637
+ error: new Error(`Config file '${filePath}' was deleted`),
638
+ timestamp: Date.now(),
639
+ };
640
+ this.events.emit(`error:${filePath}`, errorEvent);
641
+ return;
642
+ }
624
643
  try {
625
644
  // Re-parse the config (isInitialLoad = false, with breakOnError setting)
626
645
  this.readAndParseConfigFile(filePath, cls, false, watcherOptions.breakOnError, isOptional);
646
+ // Close the old watcher
647
+ watcher.close();
648
+ this.fileWatchers.delete(filePath);
649
+ // Re-establish the watcher to handle file replacement scenarios (e.g., Vault agent)
650
+ this.setupFileWatcher(filePath, cls, options, isOptional);
627
651
  // Emit file-specific success event
628
652
  const event = {
629
653
  filePath,
@@ -9,7 +9,7 @@ export type FileWatcherOptions = {
9
9
  enabled?: boolean;
10
10
  /**
11
11
  * Debounce delay in milliseconds before re-reading the file after a change is detected.
12
- * @default 200
12
+ * @default 350
13
13
  */
14
14
  debounceMs?: number;
15
15
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestjs-env-getter",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.3",
4
4
  "description": "Environment variables getter for Nestjs applications",
5
5
  "author": "Ivan Baha",
6
6
  "private": false,
@@ -41,24 +41,24 @@
41
41
  "dotenv": "^17.2.3"
42
42
  },
43
43
  "devDependencies": {
44
- "@eslint/js": "^9.26.0",
45
- "@nestjs/cli": "^11.0.10",
46
- "@nestjs/common": "^11.1.6",
47
- "@nestjs/core": "^11.1.6",
48
- "@nestjs/testing": "^11.1.6",
44
+ "@eslint/js": "^9.39.1",
45
+ "@nestjs/cli": "^11.0.12",
46
+ "@nestjs/common": "^11.1.9",
47
+ "@nestjs/core": "^11.1.9",
48
+ "@nestjs/testing": "^11.1.9",
49
49
  "@types/jest": "^30.0.0",
50
- "@types/node": "^24.7.0",
51
- "eslint": "^9.26.0",
50
+ "@types/node": "^24.10.1",
51
+ "eslint": "^9.39.1",
52
52
  "eslint-config-prettier": "^10.0.3",
53
- "eslint-plugin-jsdoc": "^61.0.0",
53
+ "eslint-plugin-jsdoc": "^61.4.1",
54
54
  "eslint-plugin-prettier": "^5.4.0",
55
- "globals": "^16.1.0",
55
+ "globals": "^16.5.0",
56
56
  "jest": "^30.2.0",
57
57
  "prettier": "^3.5.3",
58
58
  "reflect-metadata": "^0.2.2",
59
59
  "rxjs": "^7.8.2",
60
- "ts-jest": "^29.3.2",
60
+ "ts-jest": "^29.4.5",
61
61
  "typescript": "^5.8.3",
62
- "typescript-eslint": "^8.32.0"
62
+ "typescript-eslint": "^8.48.0"
63
63
  }
64
64
  }