nestjs-env-getter 1.0.0-beta.2 → 1.0.0
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 +27 -0
- package/README.md +17 -4
- package/dist/app-config/app-config.module.d.ts +2 -2
- package/dist/env-getter/env-getter.service.d.ts +1 -0
- package/dist/env-getter/env-getter.service.js +36 -12
- package/dist/env-getter/types/file-watcher-options.type.d.ts +1 -1
- package/dist/shared/utils/env-parser/env-parser.utils.d.ts +41 -0
- package/dist/shared/utils/env-parser/env-parser.utils.js +442 -0
- package/dist/shared/utils/env-parser/index.d.ts +2 -0
- package/dist/shared/utils/env-parser/index.js +24 -0
- package/dist/shared/utils/env-parser/types.d.ts +51 -0
- package/dist/shared/utils/env-parser/types.js +2 -0
- package/dist/shared/utils/index.d.ts +1 -0
- package/dist/shared/utils/index.js +15 -0
- package/package.json +17 -19
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,33 @@ 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] - 2026-01-17
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Custom .env Parser**: Replaced `dotenv` dependency with a robust, custom-built parser.
|
|
15
|
+
- Full support for multiline strings (e.g., private keys).
|
|
16
|
+
- Variable interpolation/expansion (e.g., `APP_URL=${HOST}:${PORT}`).
|
|
17
|
+
- Detection of circular variable references.
|
|
18
|
+
- Detailed error reporting for malformed lines and unterminated quotes.
|
|
19
|
+
- Support for `export` prefix and miscellaneous quoting styles (single/double).
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **Dependency Removal**: Removed `dotenv` to reduce external dependencies and control parsing logic directly.
|
|
24
|
+
- **Example Project**: Updated `nestjs-server` example to demonstrate new parser features and diverse variable types.
|
|
25
|
+
|
|
26
|
+
## [1.0.0-beta.3] - 2025-11-26
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- Improved file watcher resilience: re-establishes the file watcher after each successful update to handle atomic file replacements (e.g., Vault Agent credential rotations).
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- Increased default `debounceMs` from `200` to `350` ms to reduce noisy re-parses on rapid file updates.
|
|
35
|
+
- Documentation and unit tests updated to cover file replacement and re-establishment behavior.
|
|
36
|
+
|
|
10
37
|
## [1.0.0-beta.2] - 2025-10-17
|
|
11
38
|
|
|
12
39
|
### Added
|
package/README.md
CHANGED
|
@@ -8,9 +8,22 @@
|
|
|
8
8
|
- **Built-in Validation**: Validate variables against allowed values or use custom validation functions.
|
|
9
9
|
- **Required vs. Optional**: Clearly distinguish between required variables that terminate the process if missing and optional ones with default values.
|
|
10
10
|
- **Error Handling**: Automatically terminates the process with a descriptive error message if a required variable is missing or invalid.
|
|
11
|
-
- **`.env` Support**:
|
|
11
|
+
- **`.env` Support**: Built-in parser with variable interpolation, multiline strings, and system env priority.
|
|
12
12
|
- **Configuration Scaffolding**: Includes a CLI helper to quickly set up a centralized configuration file.
|
|
13
13
|
|
|
14
|
+
## Environment File Parsing
|
|
15
|
+
|
|
16
|
+
The module includes a zero-dependency `.env` file parser. Key behaviors:
|
|
17
|
+
|
|
18
|
+
- **System environment priority**: Variables already set in `process.env` are NOT overwritten by `.env` file values. This ensures CI/CD and Docker environment injections take precedence over local files.
|
|
19
|
+
- **Variable interpolation**: Use `${VAR_NAME}` syntax to reference other variables.
|
|
20
|
+
- **Quoting rules**:
|
|
21
|
+
- **Unquoted**: Value ends at `#` (comment) or end of line.
|
|
22
|
+
- **Single quotes** (`'`): Raw literal, no escape processing.
|
|
23
|
+
- **Double quotes** (`"`): Supports `\n`, `\t`, `\\`, `\"` escapes and multiline values.
|
|
24
|
+
- **`export` prefix**: Automatically stripped for shell compatibility.
|
|
25
|
+
- **Empty values**: `KEY=` results in an empty string (not undefined).
|
|
26
|
+
|
|
14
27
|
## Installation
|
|
15
28
|
|
|
16
29
|
```bash
|
|
@@ -272,7 +285,7 @@ Load and validate configuration from JSON files with automatic file watching and
|
|
|
272
285
|
{ enabled: false },
|
|
273
286
|
);
|
|
274
287
|
|
|
275
|
-
// Custom debounce timing (default is
|
|
288
|
+
// Custom debounce timing (default is 350ms)
|
|
276
289
|
const config = this.envGetter.getRequiredConfigFromFile(
|
|
277
290
|
"config.json",
|
|
278
291
|
undefined,
|
|
@@ -381,14 +394,14 @@ When the file watcher detects changes, it updates the **same object instance** i
|
|
|
381
394
|
**File Watcher Options:**
|
|
382
395
|
|
|
383
396
|
- `enabled` (boolean, default: `true`): Enable or disable automatic file watching
|
|
384
|
-
- `debounceMs` (number, default: `
|
|
397
|
+
- `debounceMs` (number, default: `350`): Delay in milliseconds before re-reading the file after a change
|
|
385
398
|
|
|
386
399
|
**Features:**
|
|
387
400
|
|
|
388
401
|
- ✅ Supports both absolute and relative paths (relative to `process.cwd()`)
|
|
389
402
|
- ✅ Automatic JSON parsing and validation
|
|
390
403
|
- ✅ **Hot-reload with reference preservation** - updates existing object instances in-place
|
|
391
|
-
- ✅ File watching with automatic change detection
|
|
404
|
+
- ✅ File watching with automatic change detection and file replacement support (e.g., Vault agent flow)
|
|
392
405
|
- ✅ Debouncing to prevent excessive re-reads
|
|
393
406
|
- ✅ Class-based validation with constructor pattern
|
|
394
407
|
- ✅ **Graceful error handling for optional configs** - missing files or JSON parsing errors return default values
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { DynamicModule, Provider, Type, InjectionToken, OptionalFactoryDependency } from "@nestjs/common";
|
|
1
|
+
import { DynamicModule, Provider, Type, InjectionToken, OptionalFactoryDependency, ModuleMetadata } from "@nestjs/common";
|
|
2
2
|
export interface AppConfigModuleOptions {
|
|
3
3
|
useClass?: Type<unknown>;
|
|
4
4
|
useFactory?: (...args: unknown[]) => unknown;
|
|
5
5
|
inject?: (InjectionToken | OptionalFactoryDependency)[];
|
|
6
|
-
imports?:
|
|
6
|
+
imports?: ModuleMetadata["imports"];
|
|
7
7
|
providers?: Provider[];
|
|
8
8
|
}
|
|
9
9
|
export declare class AppConfigModule {
|
|
@@ -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.
|
|
@@ -10,7 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.EnvGetterService = void 0;
|
|
13
|
-
const
|
|
13
|
+
const utils_1 = require("../shared/utils");
|
|
14
14
|
const events_1 = require("events");
|
|
15
15
|
const fs_1 = require("fs");
|
|
16
16
|
const path_1 = require("path");
|
|
@@ -34,11 +34,11 @@ let EnvGetterService = class EnvGetterService {
|
|
|
34
34
|
*/
|
|
35
35
|
safeObjectCopy(source) {
|
|
36
36
|
const copy = Object.create(null);
|
|
37
|
-
Object.keys(source)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
37
|
+
for (const key of Object.keys(source)) {
|
|
38
|
+
if (!this.isUnsafeKey(key)) {
|
|
39
|
+
copy[key] = source[key];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
42
|
return copy;
|
|
43
43
|
}
|
|
44
44
|
constructor() {
|
|
@@ -49,7 +49,7 @@ let EnvGetterService = class EnvGetterService {
|
|
|
49
49
|
* Emits 'updated' and 'error' events globally for all config files.
|
|
50
50
|
*/
|
|
51
51
|
this.events = new events_1.EventEmitter();
|
|
52
|
-
(0,
|
|
52
|
+
(0, utils_1.loadEnvFile)(".env", { quiet: true });
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
55
|
* Lifecycle hook called when the module is being destroyed.
|
|
@@ -434,8 +434,8 @@ let EnvGetterService = class EnvGetterService {
|
|
|
434
434
|
*/
|
|
435
435
|
stopProcess(message) {
|
|
436
436
|
// eslint-disable-next-line no-console
|
|
437
|
-
console.
|
|
438
|
-
|
|
437
|
+
console.error(`\x1b[31m${message}\x1b[0m`);
|
|
438
|
+
throw new Error(message);
|
|
439
439
|
}
|
|
440
440
|
/**
|
|
441
441
|
* Checks if an environment variable exists.
|
|
@@ -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 :
|
|
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
|
|
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
|
-
|
|
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,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type EnvParseOptions, type EnvParseResult } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Parses environment variables from a string.
|
|
4
|
+
* @param content - The .env file content as a string.
|
|
5
|
+
* @param options - Parsing options.
|
|
6
|
+
* @returns An {@link EnvParseResult} containing parsed variables, errors, and success flag.
|
|
7
|
+
* @throws {Error} If parsing fails and options.accumulate is false.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseEnvString(content: string, options?: EnvParseOptions): EnvParseResult;
|
|
10
|
+
/**
|
|
11
|
+
* Parses a single .env file.
|
|
12
|
+
* @param filePath - Path to the .env file.
|
|
13
|
+
* @param options - Parsing options.
|
|
14
|
+
* @returns An {@link EnvParseResult} with variables, errors, and success status.
|
|
15
|
+
* @throws {Error} If file is missing or reading fails and options.accumulate is false.
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseEnvFile(filePath: string, options?: EnvParseOptions): EnvParseResult;
|
|
18
|
+
/**
|
|
19
|
+
* Parses multiple .env files with cascading priority (later files override earlier ones).
|
|
20
|
+
* @param filePaths - Array of .env file paths in order of priority, last wins.
|
|
21
|
+
* @param options - Parsing options.
|
|
22
|
+
* @returns Combined {@link EnvParseResult} from all files.
|
|
23
|
+
* @throws {Error} If any file parsing fails and options.accumulate is false.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseEnvFiles(filePaths: string[], options?: EnvParseOptions): EnvParseResult;
|
|
26
|
+
/**
|
|
27
|
+
* Loads environment variables from a .env file into `process.env`.
|
|
28
|
+
* By default, existing system environment variables take precedence (they are NOT overwritten).
|
|
29
|
+
* Use `{ override: true }` to force overwrite.
|
|
30
|
+
* @param filePath - Path to the .env file (defaults to `.env`).
|
|
31
|
+
* @param options - Parsing and loading options.
|
|
32
|
+
* @throws {Error} If parsing fails and options.accumulate is false.
|
|
33
|
+
*/
|
|
34
|
+
export declare function loadEnvFile(filePath?: string, options?: EnvParseOptions): void;
|
|
35
|
+
/**
|
|
36
|
+
* Loads multiple .env files into `process.env` with cascading priority.
|
|
37
|
+
* @param filePaths - Array of .env file paths.
|
|
38
|
+
* @param options - Parsing and loading options.
|
|
39
|
+
* @throws {Error} If any file parsing fails and options.accumulate is false.
|
|
40
|
+
*/
|
|
41
|
+
export declare function loadEnvFiles(filePaths: string[], options?: EnvParseOptions): void;
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseEnvString = parseEnvString;
|
|
4
|
+
exports.parseEnvFile = parseEnvFile;
|
|
5
|
+
exports.parseEnvFiles = parseEnvFiles;
|
|
6
|
+
exports.loadEnvFile = loadEnvFile;
|
|
7
|
+
exports.loadEnvFiles = loadEnvFiles;
|
|
8
|
+
const fs_1 = require("fs");
|
|
9
|
+
/**
|
|
10
|
+
* Regular expression to validate environment variable keys.
|
|
11
|
+
* Keys must start with a letter or underscore, followed by alphanumeric characters or underscores.
|
|
12
|
+
*/
|
|
13
|
+
const KEY_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
14
|
+
// Variable interpolation pattern: ${VAR_NAME}
|
|
15
|
+
const INTERPOLATION_REGEX = /\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
16
|
+
/**
|
|
17
|
+
* Creates an {@link EnvParseError} object.
|
|
18
|
+
* @param lineNumber - The line number where the error occurred.
|
|
19
|
+
* @param rawContent - The raw line content.
|
|
20
|
+
* @param message - Human‑readable error message.
|
|
21
|
+
* @param type - The type of parsing error.
|
|
22
|
+
* @returns An {@link EnvParseError} instance.
|
|
23
|
+
*/
|
|
24
|
+
function createError(lineNumber, rawContent, message, type) {
|
|
25
|
+
return { lineNumber, rawContent, message, type };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Processes escape sequences in double-quoted strings.
|
|
29
|
+
* @param value - The raw string containing escape sequences.
|
|
30
|
+
* @returns The string with escape sequences interpreted.
|
|
31
|
+
*/
|
|
32
|
+
function processEscapes(value) {
|
|
33
|
+
let result = "";
|
|
34
|
+
let i = 0;
|
|
35
|
+
while (i < value.length) {
|
|
36
|
+
if (value[i] === "\\" && i + 1 < value.length) {
|
|
37
|
+
const next = value[i + 1];
|
|
38
|
+
switch (next) {
|
|
39
|
+
case "n":
|
|
40
|
+
result += "\n";
|
|
41
|
+
i += 2;
|
|
42
|
+
break;
|
|
43
|
+
case "t":
|
|
44
|
+
result += "\t";
|
|
45
|
+
i += 2;
|
|
46
|
+
break;
|
|
47
|
+
case "r":
|
|
48
|
+
result += "\r";
|
|
49
|
+
i += 2;
|
|
50
|
+
break;
|
|
51
|
+
case "\\":
|
|
52
|
+
result += "\\";
|
|
53
|
+
i += 2;
|
|
54
|
+
break;
|
|
55
|
+
case '"':
|
|
56
|
+
result += '"';
|
|
57
|
+
i += 2;
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
// Unknown escape, keep as-is
|
|
61
|
+
result += value[i];
|
|
62
|
+
i += 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
result += value[i];
|
|
67
|
+
i += 1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Detects circular references during variable interpolation.
|
|
74
|
+
* @param varName - The variable name being resolved.
|
|
75
|
+
* @param resolutionStack - Set of variable names already in the resolution chain.
|
|
76
|
+
* @returns An error message if a circular reference is found, otherwise null.
|
|
77
|
+
*/
|
|
78
|
+
function detectCircularReference(varName, resolutionStack) {
|
|
79
|
+
if (resolutionStack.has(varName)) {
|
|
80
|
+
return `Circular reference detected: ${[...resolutionStack, varName].join(" -> ")}`;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Expands variable interpolation in a value.
|
|
86
|
+
* Uses a two‑pass strategy: first checks local variables, then system environment variables.
|
|
87
|
+
* @param value - The string value to expand.
|
|
88
|
+
* @param variables - Map of local variables defined in the .env file.
|
|
89
|
+
* @param systemEnv - System environment variables.
|
|
90
|
+
* @param resolutionStack - Set tracking variables already visited to detect cycles.
|
|
91
|
+
* @param quiet - If true, suppresses warnings for undefined variables.
|
|
92
|
+
* @returns An object containing the expanded result and optionally a circular reference error.
|
|
93
|
+
*/
|
|
94
|
+
function expandVariables(value, variables, systemEnv, resolutionStack = new Set(), quiet = false) {
|
|
95
|
+
let result = value;
|
|
96
|
+
let match;
|
|
97
|
+
const regex = new RegExp(INTERPOLATION_REGEX.source, "g");
|
|
98
|
+
while ((match = regex.exec(value)) !== null) {
|
|
99
|
+
const fullMatch = match[0];
|
|
100
|
+
const varName = match[1];
|
|
101
|
+
if (!varName)
|
|
102
|
+
continue;
|
|
103
|
+
// Check for circular reference
|
|
104
|
+
const circularError = detectCircularReference(varName, resolutionStack);
|
|
105
|
+
if (circularError) {
|
|
106
|
+
return { result: value, circularError };
|
|
107
|
+
}
|
|
108
|
+
// Lookup order: local variables first, then system env
|
|
109
|
+
let replacement;
|
|
110
|
+
if (varName in variables) {
|
|
111
|
+
// Recursively expand the referenced variable
|
|
112
|
+
const newStack = new Set(resolutionStack);
|
|
113
|
+
newStack.add(varName);
|
|
114
|
+
const expanded = expandVariables(variables[varName], variables, systemEnv, newStack, quiet);
|
|
115
|
+
if (expanded.circularError) {
|
|
116
|
+
return expanded;
|
|
117
|
+
}
|
|
118
|
+
replacement = expanded.result;
|
|
119
|
+
}
|
|
120
|
+
else if (varName in systemEnv && systemEnv[varName] !== undefined) {
|
|
121
|
+
replacement = systemEnv[varName];
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
// Variable not found - leave as empty string
|
|
125
|
+
if (!quiet) {
|
|
126
|
+
// eslint-disable-next-line no-console
|
|
127
|
+
console.warn(`[env-parser] Warning: Variable '${varName}' is not defined`);
|
|
128
|
+
}
|
|
129
|
+
replacement = "";
|
|
130
|
+
}
|
|
131
|
+
result = result.replace(fullMatch, replacement);
|
|
132
|
+
}
|
|
133
|
+
return { result };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Parses a single line from a .env file and extracts a key‑value pair.
|
|
137
|
+
* Returns `null` for comments or empty lines.
|
|
138
|
+
* @param line - The raw line text.
|
|
139
|
+
* @param lineNumber - The line number in the file (1‑based).
|
|
140
|
+
* @param buffer - Current multiline buffer state, if any.
|
|
141
|
+
* @returns An object describing the parsed result, any errors, and buffer state.
|
|
142
|
+
*/
|
|
143
|
+
function parseLine(line, lineNumber, buffer) {
|
|
144
|
+
var _a, _b, _c, _d;
|
|
145
|
+
// If we're in multiline mode, continue accumulating
|
|
146
|
+
if (buffer) {
|
|
147
|
+
if (buffer.quoteChar === "'") {
|
|
148
|
+
const closingIndex = line.indexOf("'");
|
|
149
|
+
if (closingIndex !== -1) {
|
|
150
|
+
// Found closing single quote
|
|
151
|
+
buffer.value += "\n" + line.substring(0, closingIndex);
|
|
152
|
+
return { key: buffer.key, value: buffer.value, isComplete: true, buffer: null };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else if (buffer.quoteChar === '"') {
|
|
156
|
+
// Double-quoted multiline - scan for non-escaped quote
|
|
157
|
+
let i = 0;
|
|
158
|
+
let collected = "";
|
|
159
|
+
while (i < line.length) {
|
|
160
|
+
if (line[i] === "\\" && i + 1 < line.length) {
|
|
161
|
+
// Preserve escape sequence
|
|
162
|
+
collected += ((_a = line[i]) !== null && _a !== void 0 ? _a : "") + ((_b = line[i + 1]) !== null && _b !== void 0 ? _b : "");
|
|
163
|
+
i += 2;
|
|
164
|
+
}
|
|
165
|
+
else if (line[i] === '"') {
|
|
166
|
+
// Found closing double quote
|
|
167
|
+
buffer.value += "\n" + collected;
|
|
168
|
+
return { key: buffer.key, value: processEscapes(buffer.value), isComplete: true, buffer: null };
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
collected += line[i];
|
|
172
|
+
i++;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Not found on this line, append entire line (literal including escapes)
|
|
176
|
+
buffer.value += "\n" + line;
|
|
177
|
+
return { isComplete: false, buffer };
|
|
178
|
+
}
|
|
179
|
+
// Continue accumulating for other cases (should be handled above but for safety)
|
|
180
|
+
buffer.value += "\n" + line;
|
|
181
|
+
return { isComplete: false, buffer };
|
|
182
|
+
}
|
|
183
|
+
// Trim leading whitespace (but preserve for error reporting)
|
|
184
|
+
const trimmedLine = line.trimStart();
|
|
185
|
+
// Skip empty lines and full-line comments
|
|
186
|
+
if (!trimmedLine || trimmedLine.startsWith("#")) {
|
|
187
|
+
return { isComplete: true, buffer: null };
|
|
188
|
+
}
|
|
189
|
+
// Strip 'export ' prefix if present
|
|
190
|
+
let workingLine = trimmedLine;
|
|
191
|
+
if (workingLine.startsWith("export ")) {
|
|
192
|
+
workingLine = workingLine.substring(7).trimStart();
|
|
193
|
+
}
|
|
194
|
+
// Find the equals sign
|
|
195
|
+
const equalsIndex = workingLine.indexOf("=");
|
|
196
|
+
if (equalsIndex === -1) {
|
|
197
|
+
return {
|
|
198
|
+
isComplete: true,
|
|
199
|
+
buffer: null,
|
|
200
|
+
error: createError(lineNumber, line, `Line ${lineNumber}: Missing '=' in assignment`, "MALFORMED_LINE"),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
// Extract key and validate
|
|
204
|
+
const key = workingLine.substring(0, equalsIndex).trim();
|
|
205
|
+
if (!KEY_REGEX.test(key)) {
|
|
206
|
+
return {
|
|
207
|
+
isComplete: true,
|
|
208
|
+
buffer: null,
|
|
209
|
+
error: createError(lineNumber, line, `Line ${lineNumber}: Invalid key '${key}'. Keys must start with a letter or underscore and contain only alphanumeric characters and underscores.`, "INVALID_KEY"),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
// Extract raw value (everything after =)
|
|
213
|
+
let rawValue = workingLine.substring(equalsIndex + 1);
|
|
214
|
+
// Determine value type based on first character
|
|
215
|
+
const firstChar = rawValue.charAt(0);
|
|
216
|
+
if (firstChar === "'") {
|
|
217
|
+
// Single-quoted value - raw literal
|
|
218
|
+
const closingIndex = rawValue.indexOf("'", 1);
|
|
219
|
+
if (closingIndex === -1) {
|
|
220
|
+
// Check if it's multiline or unterminated
|
|
221
|
+
return {
|
|
222
|
+
isComplete: false,
|
|
223
|
+
buffer: { key, value: rawValue.substring(1), quoteChar: "'", startLine: lineNumber },
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const value = rawValue.substring(1, closingIndex);
|
|
227
|
+
return { key, value, isComplete: true, buffer: null };
|
|
228
|
+
}
|
|
229
|
+
else if (firstChar === '"') {
|
|
230
|
+
// Double-quoted value - supports escapes
|
|
231
|
+
// Find the closing quote, accounting for escaped quotes
|
|
232
|
+
let i = 1;
|
|
233
|
+
let value = "";
|
|
234
|
+
let foundClosing = false;
|
|
235
|
+
while (i < rawValue.length) {
|
|
236
|
+
if (rawValue[i] === "\\" && i + 1 < rawValue.length) {
|
|
237
|
+
// Escape sequence - keep it for later processing
|
|
238
|
+
value += ((_c = rawValue[i]) !== null && _c !== void 0 ? _c : "") + ((_d = rawValue[i + 1]) !== null && _d !== void 0 ? _d : "");
|
|
239
|
+
i += 2;
|
|
240
|
+
}
|
|
241
|
+
else if (rawValue[i] === '"') {
|
|
242
|
+
foundClosing = true;
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
value += rawValue[i];
|
|
247
|
+
i += 1;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (!foundClosing) {
|
|
251
|
+
// Multiline double-quoted string
|
|
252
|
+
return {
|
|
253
|
+
isComplete: false,
|
|
254
|
+
buffer: { key, value, quoteChar: '"', startLine: lineNumber },
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
// Process escape sequences
|
|
258
|
+
const processedValue = processEscapes(value);
|
|
259
|
+
return { key, value: processedValue, isComplete: true, buffer: null };
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
// Unquoted value - trim whitespace and stop at comment
|
|
263
|
+
rawValue = rawValue.trim();
|
|
264
|
+
// Find inline comment (# not inside quotes)
|
|
265
|
+
const commentIndex = rawValue.indexOf("#");
|
|
266
|
+
if (commentIndex !== -1) {
|
|
267
|
+
rawValue = rawValue.substring(0, commentIndex).trimEnd();
|
|
268
|
+
}
|
|
269
|
+
return { key, value: rawValue, isComplete: true, buffer: null };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Parses environment variables from a string.
|
|
274
|
+
* @param content - The .env file content as a string.
|
|
275
|
+
* @param options - Parsing options.
|
|
276
|
+
* @returns An {@link EnvParseResult} containing parsed variables, errors, and success flag.
|
|
277
|
+
* @throws {Error} If parsing fails and options.accumulate is false.
|
|
278
|
+
*/
|
|
279
|
+
function parseEnvString(content, options = {}) {
|
|
280
|
+
var _a;
|
|
281
|
+
const { accumulate = false, systemEnv = process.env, quiet = false } = options;
|
|
282
|
+
// Normalize line endings
|
|
283
|
+
const normalizedContent = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
284
|
+
const lines = normalizedContent.split("\n");
|
|
285
|
+
const variables = {};
|
|
286
|
+
const errors = [];
|
|
287
|
+
let buffer = null;
|
|
288
|
+
for (let i = 0; i < lines.length; i++) {
|
|
289
|
+
const lineNumber = i + 1;
|
|
290
|
+
const line = lines[i];
|
|
291
|
+
const result = parseLine(line !== null && line !== void 0 ? line : "", lineNumber, buffer);
|
|
292
|
+
if (result.error) {
|
|
293
|
+
if (accumulate) {
|
|
294
|
+
errors.push(result.error);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
throw new Error(result.error.message);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (result.isComplete && result.key !== undefined) {
|
|
301
|
+
variables[result.key] = (_a = result.value) !== null && _a !== void 0 ? _a : "";
|
|
302
|
+
}
|
|
303
|
+
buffer = result.buffer;
|
|
304
|
+
}
|
|
305
|
+
// Check for unterminated multiline string
|
|
306
|
+
if (buffer) {
|
|
307
|
+
const error = createError(buffer.startLine, `Started at line ${buffer.startLine}`, `Unterminated ${buffer.quoteChar === '"' ? "double" : "single"}-quoted string starting at line ${buffer.startLine}`, "UNTERMINATED_QUOTE");
|
|
308
|
+
if (accumulate) {
|
|
309
|
+
errors.push(error);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
throw new Error(error.message);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Second pass: variable interpolation
|
|
316
|
+
const expandedVariables = {};
|
|
317
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
318
|
+
const { result, circularError } = expandVariables(value, variables, systemEnv, new Set([key]), quiet);
|
|
319
|
+
if (circularError) {
|
|
320
|
+
const error = createError(0, key, circularError, "CIRCULAR_REFERENCE");
|
|
321
|
+
if (accumulate) {
|
|
322
|
+
errors.push(error);
|
|
323
|
+
expandedVariables[key] = value; // Keep original value on error
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
throw new Error(error.message);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
expandedVariables[key] = result;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
variables: expandedVariables,
|
|
335
|
+
errors,
|
|
336
|
+
success: errors.length === 0,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Parses a single .env file.
|
|
341
|
+
* @param filePath - Path to the .env file.
|
|
342
|
+
* @param options - Parsing options.
|
|
343
|
+
* @returns An {@link EnvParseResult} with variables, errors, and success status.
|
|
344
|
+
* @throws {Error} If file is missing or reading fails and options.accumulate is false.
|
|
345
|
+
*/
|
|
346
|
+
function parseEnvFile(filePath, options = {}) {
|
|
347
|
+
if (!(0, fs_1.existsSync)(filePath)) {
|
|
348
|
+
const error = createError(0, filePath, `File not found: ${filePath}`, "FILE_READ_ERROR");
|
|
349
|
+
if (options.accumulate) {
|
|
350
|
+
return { variables: {}, errors: [error], success: false };
|
|
351
|
+
}
|
|
352
|
+
throw new Error(error.message);
|
|
353
|
+
}
|
|
354
|
+
try {
|
|
355
|
+
const content = (0, fs_1.readFileSync)(filePath, "utf-8");
|
|
356
|
+
return parseEnvString(content, options);
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
// parseEnvString throws for all parse errors (unterminated quotes, invalid keys, etc.)
|
|
360
|
+
if (err instanceof Error) {
|
|
361
|
+
// Check if this is a file-system error (not a parsing error)
|
|
362
|
+
const isFileSystemError = err.message.includes("ENOENT") ||
|
|
363
|
+
err.message.includes("EACCES") ||
|
|
364
|
+
err.message.includes("EPERM") ||
|
|
365
|
+
err.message.startsWith("Error reading file");
|
|
366
|
+
if (!isFileSystemError) {
|
|
367
|
+
throw err; // Re-throw parsing errors
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const error = createError(0, filePath, `Error reading file ${filePath}: ${err instanceof Error ? err.message : String(err)}`, "FILE_READ_ERROR");
|
|
371
|
+
if (options.accumulate) {
|
|
372
|
+
return { variables: {}, errors: [error], success: false };
|
|
373
|
+
}
|
|
374
|
+
throw new Error(error.message);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Parses multiple .env files with cascading priority (later files override earlier ones).
|
|
379
|
+
* @param filePaths - Array of .env file paths in order of priority, last wins.
|
|
380
|
+
* @param options - Parsing options.
|
|
381
|
+
* @returns Combined {@link EnvParseResult} from all files.
|
|
382
|
+
* @throws {Error} If any file parsing fails and options.accumulate is false.
|
|
383
|
+
*/
|
|
384
|
+
function parseEnvFiles(filePaths, options = {}) {
|
|
385
|
+
const allVariables = {};
|
|
386
|
+
const allErrors = [];
|
|
387
|
+
for (const filePath of filePaths) {
|
|
388
|
+
if (!(0, fs_1.existsSync)(filePath)) {
|
|
389
|
+
continue; // Skip non-existent files in multi-file mode
|
|
390
|
+
}
|
|
391
|
+
const result = parseEnvFile(filePath, { ...options, accumulate: true });
|
|
392
|
+
// Merge variables (later files override)
|
|
393
|
+
Object.assign(allVariables, result.variables);
|
|
394
|
+
// Collect errors
|
|
395
|
+
allErrors.push(...result.errors);
|
|
396
|
+
}
|
|
397
|
+
if (!options.accumulate && allErrors.length > 0) {
|
|
398
|
+
throw new Error(allErrors[0].message);
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
variables: allVariables,
|
|
402
|
+
errors: allErrors,
|
|
403
|
+
success: allErrors.length === 0,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Loads environment variables from a .env file into `process.env`.
|
|
408
|
+
* By default, existing system environment variables take precedence (they are NOT overwritten).
|
|
409
|
+
* Use `{ override: true }` to force overwrite.
|
|
410
|
+
* @param filePath - Path to the .env file (defaults to `.env`).
|
|
411
|
+
* @param options - Parsing and loading options.
|
|
412
|
+
* @throws {Error} If parsing fails and options.accumulate is false.
|
|
413
|
+
*/
|
|
414
|
+
function loadEnvFile(filePath = ".env", options = {}) {
|
|
415
|
+
const { override = false } = options;
|
|
416
|
+
if (!(0, fs_1.existsSync)(filePath)) {
|
|
417
|
+
// Silently skip if file doesn't exist (matches dotenv behavior)
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const result = parseEnvFile(filePath, options);
|
|
421
|
+
for (const [key, value] of Object.entries(result.variables)) {
|
|
422
|
+
// Respect immutable system env rule: don't overwrite existing vars unless override=true
|
|
423
|
+
if (override || !(key in process.env)) {
|
|
424
|
+
process.env[key] = value;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Loads multiple .env files into `process.env` with cascading priority.
|
|
430
|
+
* @param filePaths - Array of .env file paths.
|
|
431
|
+
* @param options - Parsing and loading options.
|
|
432
|
+
* @throws {Error} If any file parsing fails and options.accumulate is false.
|
|
433
|
+
*/
|
|
434
|
+
function loadEnvFiles(filePaths, options = {}) {
|
|
435
|
+
const { override = false } = options;
|
|
436
|
+
const result = parseEnvFiles(filePaths, options);
|
|
437
|
+
for (const [key, value] of Object.entries(result.variables)) {
|
|
438
|
+
if (override || !(key in process.env)) {
|
|
439
|
+
process.env[key] = value;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.loadEnvFiles = exports.loadEnvFile = exports.parseEnvFiles = exports.parseEnvFile = exports.parseEnvString = void 0;
|
|
18
|
+
__exportStar(require("./types"), exports);
|
|
19
|
+
var env_parser_utils_1 = require("./env-parser.utils");
|
|
20
|
+
Object.defineProperty(exports, "parseEnvString", { enumerable: true, get: function () { return env_parser_utils_1.parseEnvString; } });
|
|
21
|
+
Object.defineProperty(exports, "parseEnvFile", { enumerable: true, get: function () { return env_parser_utils_1.parseEnvFile; } });
|
|
22
|
+
Object.defineProperty(exports, "parseEnvFiles", { enumerable: true, get: function () { return env_parser_utils_1.parseEnvFiles; } });
|
|
23
|
+
Object.defineProperty(exports, "loadEnvFile", { enumerable: true, get: function () { return env_parser_utils_1.loadEnvFile; } });
|
|
24
|
+
Object.defineProperty(exports, "loadEnvFiles", { enumerable: true, get: function () { return env_parser_utils_1.loadEnvFiles; } });
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for parsing environment files.
|
|
3
|
+
*/
|
|
4
|
+
export interface EnvParseOptions {
|
|
5
|
+
/**
|
|
6
|
+
* If true, .env file values will override existing system environment variables.
|
|
7
|
+
* By default (false), system env takes precedence over .env file values.
|
|
8
|
+
* @default false
|
|
9
|
+
*/
|
|
10
|
+
override?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* If true, collect all errors instead of throwing on first error.
|
|
13
|
+
* Useful for pre-flight validation of the entire file.
|
|
14
|
+
* @default false
|
|
15
|
+
*/
|
|
16
|
+
accumulate?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* System environment variables to use for variable interpolation lookup.
|
|
19
|
+
* Defaults to process.env.
|
|
20
|
+
*/
|
|
21
|
+
systemEnv?: Record<string, string | undefined>;
|
|
22
|
+
/**
|
|
23
|
+
* If true, suppress console warnings for undefined variable references.
|
|
24
|
+
* @default false
|
|
25
|
+
*/
|
|
26
|
+
quiet?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Represents a parsing error with context information.
|
|
30
|
+
*/
|
|
31
|
+
export interface EnvParseError {
|
|
32
|
+
/** Line number where the error occurred (1-indexed). */
|
|
33
|
+
lineNumber: number;
|
|
34
|
+
/** Raw content of the problematic line. */
|
|
35
|
+
rawContent: string;
|
|
36
|
+
/** Human-readable error message. */
|
|
37
|
+
message: string;
|
|
38
|
+
/** Error type for programmatic handling. */
|
|
39
|
+
type: "UNTERMINATED_QUOTE" | "INVALID_KEY" | "MALFORMED_LINE" | "CIRCULAR_REFERENCE" | "FILE_READ_ERROR";
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Result of parsing environment content.
|
|
43
|
+
*/
|
|
44
|
+
export interface EnvParseResult {
|
|
45
|
+
/** Parsed environment variables as key-value pairs. */
|
|
46
|
+
variables: Record<string, string>;
|
|
47
|
+
/** List of errors encountered during parsing (populated when accumulate=true). */
|
|
48
|
+
errors: EnvParseError[];
|
|
49
|
+
/** Whether parsing completed successfully (no errors). */
|
|
50
|
+
success: boolean;
|
|
51
|
+
}
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
17
|
exports.parseTimePeriod = exports.isTimePeriod = void 0;
|
|
4
18
|
var time_period_utils_1 = require("./time-period.utils");
|
|
5
19
|
Object.defineProperty(exports, "isTimePeriod", { enumerable: true, get: function () { return time_period_utils_1.isTimePeriod; } });
|
|
6
20
|
Object.defineProperty(exports, "parseTimePeriod", { enumerable: true, get: function () { return time_period_utils_1.parseTimePeriod; } });
|
|
21
|
+
__exportStar(require("./env-parser"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nestjs-env-getter",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Environment variables getter for Nestjs applications",
|
|
5
5
|
"author": "Ivan Baha",
|
|
6
6
|
"private": false,
|
|
@@ -37,28 +37,26 @@
|
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@nestjs/common": ">=9"
|
|
39
39
|
},
|
|
40
|
-
"dependencies": {
|
|
41
|
-
"dotenv": "^17.2.3"
|
|
42
|
-
},
|
|
40
|
+
"dependencies": {},
|
|
43
41
|
"devDependencies": {
|
|
44
|
-
"@eslint/js": "^9.
|
|
45
|
-
"@nestjs/cli": "^11.0.
|
|
46
|
-
"@nestjs/common": "^11.1.
|
|
47
|
-
"@nestjs/core": "^11.1.
|
|
48
|
-
"@nestjs/testing": "^11.1.
|
|
42
|
+
"@eslint/js": "^9.39.2",
|
|
43
|
+
"@nestjs/cli": "^11.0.16",
|
|
44
|
+
"@nestjs/common": "^11.1.12",
|
|
45
|
+
"@nestjs/core": "^11.1.12",
|
|
46
|
+
"@nestjs/testing": "^11.1.12",
|
|
49
47
|
"@types/jest": "^30.0.0",
|
|
50
|
-
"@types/node": "^
|
|
51
|
-
"eslint": "^9.
|
|
52
|
-
"eslint-config-prettier": "^10.
|
|
53
|
-
"eslint-plugin-jsdoc": "^
|
|
54
|
-
"eslint-plugin-prettier": "^5.
|
|
55
|
-
"globals": "^
|
|
48
|
+
"@types/node": "^25.0.9",
|
|
49
|
+
"eslint": "^9.39.2",
|
|
50
|
+
"eslint-config-prettier": "^10.1.8",
|
|
51
|
+
"eslint-plugin-jsdoc": "^62.0.1",
|
|
52
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
53
|
+
"globals": "^17.0.0",
|
|
56
54
|
"jest": "^30.2.0",
|
|
57
|
-
"prettier": "^3.
|
|
55
|
+
"prettier": "^3.8.0",
|
|
58
56
|
"reflect-metadata": "^0.2.2",
|
|
59
57
|
"rxjs": "^7.8.2",
|
|
60
|
-
"ts-jest": "^29.
|
|
61
|
-
"typescript": "^5.
|
|
62
|
-
"typescript-eslint": "^8.
|
|
58
|
+
"ts-jest": "^29.4.6",
|
|
59
|
+
"typescript": "^5.9.3",
|
|
60
|
+
"typescript-eslint": "^8.53.0"
|
|
63
61
|
}
|
|
64
62
|
}
|