@wolfstar/env-utilities 2.2.0 → 2.2.1-next-20260927142235
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/cjs/index.cjs +1 -1
- package/dist/cjs/setup.cjs +1 -1
- package/dist/cjs/{src-DCzHag6L.cjs → src-C_78_z43.cjs} +75 -5
- package/dist/cjs/{src-DCzHag6L.cjs.map → src-C_78_z43.cjs.map} +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/setup.js +1 -1
- package/dist/esm/{src-BHEW4qZd.js → src-BOak8HED.js} +76 -6
- package/dist/esm/{src-BHEW4qZd.js.map → src-BOak8HED.js.map} +1 -1
- package/package.json +3 -3
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_src = require('./src-
|
|
2
|
+
const require_src = require('./src-C_78_z43.cjs');
|
|
3
3
|
|
|
4
4
|
exports.envIsDefined = require_src.envIsDefined;
|
|
5
5
|
exports.envIsNull = require_src.envIsNull;
|
package/dist/cjs/setup.cjs
CHANGED
|
@@ -6,7 +6,7 @@ let node_module = require("node:module");
|
|
|
6
6
|
let node_path = require("node:path");
|
|
7
7
|
|
|
8
8
|
//#region src/lib/env-loader.ts
|
|
9
|
-
const packageVersion = "2.2.
|
|
9
|
+
const packageVersion = "2.2.1-next-20260927142235";
|
|
10
10
|
function resolveDebugLogger() {
|
|
11
11
|
return _sapphire_pieces.container.logger ?? console;
|
|
12
12
|
}
|
|
@@ -29,14 +29,16 @@ function loadEnvFiles(options) {
|
|
|
29
29
|
* @see {@linkplain https://github.com/facebook/create-react-app/blob/d960b9e38c062584ff6cfb1a70e1512509a966e7/packages/react-scripts/config/env.js#L36-L49}
|
|
30
30
|
*/
|
|
31
31
|
let parsed = {};
|
|
32
|
+
const scratch = dotenvSettingsFromProcessEnv();
|
|
32
33
|
for (const dotenvFile of dotenvFiles) {
|
|
33
34
|
const dotenvFileString = typeof dotenvFile === "string" ? dotenvFile : (0, node_url.fileURLToPath)(dotenvFile);
|
|
34
35
|
log(`loading \`${(0, node_path.basename)(dotenvFileString)}\``);
|
|
35
|
-
const result = (0,
|
|
36
|
+
const result = (0, dotenv.config)({
|
|
36
37
|
debug: options?.debug,
|
|
37
38
|
encoding: options?.encoding,
|
|
38
|
-
path: dotenvFile
|
|
39
|
-
|
|
39
|
+
path: dotenvFile,
|
|
40
|
+
processEnv: scratch
|
|
41
|
+
});
|
|
40
42
|
if (result.error) {
|
|
41
43
|
if (result.error.code === "ENOENT") {
|
|
42
44
|
log(`\`${(0, node_path.basename)(dotenvFileString)}\` file not found`);
|
|
@@ -49,6 +51,13 @@ function loadEnvFiles(options) {
|
|
|
49
51
|
...parsed
|
|
50
52
|
};
|
|
51
53
|
}
|
|
54
|
+
const alreadySet = new Set(Object.keys(parsed).filter((key) => process.env[key]));
|
|
55
|
+
for (const key of findReferenceCycles(parsed, alreadySet)) {
|
|
56
|
+
log(`\`${key}\` is part of a circular reference and resolves to an empty string`);
|
|
57
|
+
parsed[key] = "";
|
|
58
|
+
}
|
|
59
|
+
(0, dotenv.populate)(process.env, parsed, { debug: options?.debug });
|
|
60
|
+
parsed = (0, dotenv_expand.expand)({ parsed }).parsed;
|
|
52
61
|
/**
|
|
53
62
|
* @see {@linkplain https://github.com/facebook/create-react-app/blob/d960b9e38c062584ff6cfb1a70e1512509a966e7/packages/react-scripts/config/env.js#L72-L89}
|
|
54
63
|
*/
|
|
@@ -94,6 +103,67 @@ function filterByPrefix(parsed, prefix, log) {
|
|
|
94
103
|
return obj;
|
|
95
104
|
}, {});
|
|
96
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* dotenv reads its own `DOTENV_CONFIG_DEBUG` and `DOTENV_CONFIG_QUIET` switches from the `processEnv` it writes to,
|
|
108
|
+
* so forward the ones set in the real environment to the scratch object used while parsing.
|
|
109
|
+
*/
|
|
110
|
+
function dotenvSettingsFromProcessEnv() {
|
|
111
|
+
const settings = {};
|
|
112
|
+
for (const key of ["DOTENV_CONFIG_DEBUG", "DOTENV_CONFIG_QUIET"]) {
|
|
113
|
+
const value = process.env[key];
|
|
114
|
+
if (value !== void 0) settings[key] = value;
|
|
115
|
+
}
|
|
116
|
+
return settings;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Finds the variables that take part in a reference cycle spanning several variables (`A=${B}`, `B=${A}`).
|
|
120
|
+
*
|
|
121
|
+
* dotenv-expand only stops a cycle when a substituted value equals a whole intermediate result, so once a value
|
|
122
|
+
* around the cycle carries a prefix or suffix (e.g. `C=${B}z`) it never terminates. Resolving the variables of such
|
|
123
|
+
* a cycle to an empty string beforehand keeps loading bounded. Variables already set in `process.env` are left out,
|
|
124
|
+
* as `expand` uses their existing value instead of expanding the one from the file. A variable referencing itself
|
|
125
|
+
* is left alone too: dotenv-expand handles that case on its own.
|
|
126
|
+
*/
|
|
127
|
+
function findReferenceCycles(parsed, alreadySet) {
|
|
128
|
+
const references = /* @__PURE__ */ new Map();
|
|
129
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
130
|
+
if (alreadySet.has(key)) continue;
|
|
131
|
+
const targets = /* @__PURE__ */ new Set();
|
|
132
|
+
for (const match of value.matchAll(/(?<!\\)\$\{?([A-Za-z_][A-Za-z0-9_]*)/g)) {
|
|
133
|
+
const target = match[1];
|
|
134
|
+
if (target !== key && target in parsed && !alreadySet.has(target)) targets.add(target);
|
|
135
|
+
}
|
|
136
|
+
references.set(key, [...targets]);
|
|
137
|
+
}
|
|
138
|
+
const cyclic = /* @__PURE__ */ new Set();
|
|
139
|
+
const indexes = /* @__PURE__ */ new Map();
|
|
140
|
+
const lowLinks = /* @__PURE__ */ new Map();
|
|
141
|
+
const stack = [];
|
|
142
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
143
|
+
let counter = 0;
|
|
144
|
+
const visit = (key) => {
|
|
145
|
+
indexes.set(key, counter);
|
|
146
|
+
lowLinks.set(key, counter);
|
|
147
|
+
counter++;
|
|
148
|
+
stack.push(key);
|
|
149
|
+
onStack.add(key);
|
|
150
|
+
for (const target of references.get(key) ?? []) if (!indexes.has(target)) {
|
|
151
|
+
visit(target);
|
|
152
|
+
lowLinks.set(key, Math.min(lowLinks.get(key), lowLinks.get(target)));
|
|
153
|
+
} else if (onStack.has(target)) lowLinks.set(key, Math.min(lowLinks.get(key), indexes.get(target)));
|
|
154
|
+
if (lowLinks.get(key) !== indexes.get(key)) return;
|
|
155
|
+
const component = [];
|
|
156
|
+
let member;
|
|
157
|
+
do {
|
|
158
|
+
member = stack.pop();
|
|
159
|
+
onStack.delete(member);
|
|
160
|
+
component.push(member);
|
|
161
|
+
} while (member !== key);
|
|
162
|
+
if (component.length > 1) for (const item of component) cyclic.add(item);
|
|
163
|
+
};
|
|
164
|
+
for (const key of references.keys()) if (!indexes.has(key)) visit(key);
|
|
165
|
+
return cyclic;
|
|
166
|
+
}
|
|
97
167
|
function appendSuffix(path, suffix) {
|
|
98
168
|
if (typeof path === "string") return `${path}${suffix}`;
|
|
99
169
|
const file = (0, node_url.fileURLToPath)(path);
|
|
@@ -280,4 +350,4 @@ Object.defineProperty(exports, 'setup', {
|
|
|
280
350
|
return setup;
|
|
281
351
|
}
|
|
282
352
|
});
|
|
283
|
-
//# sourceMappingURL=src-
|
|
353
|
+
//# sourceMappingURL=src-C_78_z43.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"src-
|
|
1
|
+
{"version":3,"file":"src-C_78_z43.cjs","names":["fileURLToPath"],"sources":["../../src/lib/utils.ts","../../src/lib/setup.ts"],"sourcesContent":["import type { Env, EnvAny, EnvArray, EnvBoolean, EnvInteger, EnvNumber, EnvString } from './types';\n\nexport function envParseInteger(key: EnvInteger, defaultValue?: number): number;\nexport function envParseInteger(key: EnvInteger, defaultValue: number | null): number | null;\nexport function envParseInteger(key: EnvInteger, defaultValue?: number | null): number | null {\n\tconst value = process.env[key];\n\tif (!value) {\n\t\tif (defaultValue === undefined) throw new ReferenceError(`[ENV] ${key} - The key must be an integer, but is empty or undefined.`);\n\t\treturn defaultValue;\n\t}\n\n\tconst integer = Number(value);\n\tif (Number.isInteger(integer)) return integer;\n\tthrow new TypeError(`[ENV] ${key} - The key must be an integer, but received '${value}'.`);\n}\n\nexport function envParseNumber(key: EnvNumber, defaultValue?: number): number;\nexport function envParseNumber(key: EnvNumber, defaultValue: number | null): number | null;\nexport function envParseNumber(key: EnvNumber, defaultValue?: number | null): number | null {\n\tconst value = process.env[key];\n\tif (!value) {\n\t\tif (defaultValue === undefined) throw new ReferenceError(`[ENV] ${key} - The key must be a number, but is empty or undefined.`);\n\t\treturn defaultValue;\n\t}\n\n\tconst integer = Number(value);\n\tif (!Number.isNaN(integer)) return integer;\n\tthrow new TypeError(`[ENV] ${key} - The key must be a number, but received '${value}'.`);\n}\n\nexport function envParseBoolean(key: EnvBoolean, defaultValue?: boolean): boolean;\nexport function envParseBoolean(key: EnvBoolean, defaultValue: boolean | null): boolean | null;\nexport function envParseBoolean(key: EnvBoolean, defaultValue?: boolean | null): boolean | null {\n\tconst value = process.env[key];\n\tif (!value) {\n\t\tif (defaultValue === undefined) throw new ReferenceError(`[ENV] ${key} - The key must be a boolean, but is empty or undefined.`);\n\t\treturn defaultValue;\n\t}\n\n\tif (value.length === 4 && value.toLowerCase() === 'true') return true;\n\tif (value.length === 5 && value.toLowerCase() === 'false') return false;\n\tthrow new TypeError(`[ENV] ${key} - The key must be a boolean, but received '${value}'.`);\n}\n\nexport function envParseString<K extends EnvString>(key: K, defaultValue?: Env[K]): NonNullable<Env[K]>;\nexport function envParseString<K extends EnvString>(key: K, defaultValue: Env[K] | null): NonNullable<Env[K]> | null;\nexport function envParseString<K extends EnvString>(key: K, defaultValue?: Env[K] | null): NonNullable<Env[K]> | null {\n\tconst value = process.env[key];\n\tif (!value) {\n\t\tif (defaultValue === undefined) throw new ReferenceError(`[ENV] ${key} - The key must be a string, but is empty or undefined.`);\n\t\treturn defaultValue!;\n\t}\n\n\treturn value!;\n}\n\nexport function envParseArray(key: EnvArray, defaultValue?: string[]): string[];\nexport function envParseArray(key: EnvArray, defaultValue: string[] | null): string[] | null;\nexport function envParseArray(key: EnvArray, defaultValue?: string[] | null): string[] | null {\n\tconst value = process.env[key];\n\tif (!value) {\n\t\tif (defaultValue === undefined) throw new ReferenceError(`[ENV] ${key} - The key must be an array, but is empty or undefined.`);\n\t\treturn defaultValue;\n\t}\n\n\treturn value.split(' ');\n}\n\n/**\n * Checks if the value of the specified environment variable is null.\n * @param key - The name of the environment variable.\n * @returns Whether the value of the specified environment variable is:\n * - The string `\"0\"`\n * - The string `\"null\"`, case insensitive\n */\nexport function envIsNull(key: EnvAny): boolean {\n\tconst value = process.env[key];\n\treturn typeof value === 'string' && (value === '0' || (value.length === 4 && value.toLowerCase() === 'null'));\n}\n\n/**\n * Checks if the value of the specified environment variable is undefined.\n * @param key - The name of the environment variable.\n * @returns Whether the value of the specified environment variable is:\n * - `undefined`\n * - An empty string (`\"\"`)\n */\nexport function envIsUndefined(key: EnvAny): boolean {\n\tconst value = process.env[key];\n\treturn value === undefined || value.length === 0;\n}\n\n/**\n * Checks if the value of the specified environment variable is nullish.\n * @param key - The name of the environment variable.\n * @returns Whether the value of the specified environment variable is:\n * - `undefined`\n * - An empty string (`\"\"`)\n * - The string `\"0\"`\n * - The string `\"null\"`, case insensitive\n */\nexport function envIsNullish(key: EnvAny): boolean {\n\treturn envIsUndefined(key) || envIsNull(key);\n}\n\n/**\n * Checks if any of the specified environment variables is defined.\n * @param key - The name of the environment variable.\n * @returns Whether the value of any of the specified environment variables is a non-empty string\n */\nexport function envIsDefined(...keys: readonly EnvAny[]): boolean {\n\treturn keys.every((key) => !envIsUndefined(key));\n}\n","import type { DotenvConfigOutput } from 'dotenv';\nimport { fileURLToPath } from 'node:url';\nimport { loadEnvFiles, type EnvLoaderOptions } from './env-loader';\nimport { envIsDefined, envParseBoolean } from './utils';\n\nexport function setup(pathOrOptions?: string | URL | EnvSetupOptions): DotenvConfigOutput {\n\t// Unless explicitly defined, set NODE_ENV as development:\n\tprocess.env.NODE_ENV ??= 'development';\n\n\tlet options: EnvLoaderOptions;\n\tif (typeof pathOrOptions === 'undefined') {\n\t\toptions = {};\n\t} else if (typeof pathOrOptions === 'string') {\n\t\toptions = { path: pathOrOptions };\n\t} else if (typeof pathOrOptions === 'object') {\n\t\tif (pathOrOptions instanceof URL) {\n\t\t\toptions = { path: fileURLToPath(pathOrOptions) };\n\t\t} else {\n\t\t\tconst { path, ...rest } = pathOrOptions;\n\t\t\toptions = { path: typeof path === 'undefined' ? path : typeof path === 'string' ? path : fileURLToPath(path), ...rest };\n\t\t}\n\t} else {\n\t\tthrow new TypeError('Expected undefined, string, URL, or EnvSetupOptions');\n\t}\n\n\treturn loadEnvFiles({\n\t\tdebug: envIsDefined('DOTENV_DEBUG') ? envParseBoolean('DOTENV_DEBUG') : undefined,\n\t\tencoding: process.env.DOTENV_ENCODING,\n\t\tenv: process.env.DOTENV_ENV,\n\t\tpath: process.env.DOTENV_PATH,\n\t\tprefix: process.env.DOTENV_PREFIX,\n\t\tloader: process.env.DOTENV_LOADER,\n\t\t...options\n\t});\n}\n\nexport interface EnvSetupOptions extends Omit<EnvLoaderOptions, 'path'> {\n\t/**\n\t * You may specify a custom path if your file containing environment variables is located elsewhere.\n\t */\n\tpath?: string | URL;\n}\n\nexport type EnvSetupResult = DotenvConfigOutput;\n"],"mappings":"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"}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as envIsUndefined, c as envParseInteger, i as envIsNullish, l as envParseNumber, n as envIsDefined, o as envParseArray, r as envIsNull, s as envParseBoolean, t as setup, u as envParseString } from "./src-
|
|
1
|
+
import { a as envIsUndefined, c as envParseInteger, i as envIsNullish, l as envParseNumber, n as envIsDefined, o as envParseArray, r as envIsNull, s as envParseBoolean, t as setup, u as envParseString } from "./src-BOak8HED.js";
|
|
2
2
|
|
|
3
3
|
export { envIsDefined, envIsNull, envIsNullish, envIsUndefined, envParseArray, envParseBoolean, envParseInteger, envParseNumber, envParseString, setup };
|
package/dist/esm/setup.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { container } from "@sapphire/pieces";
|
|
4
|
-
import { config } from "dotenv";
|
|
4
|
+
import { config, populate } from "dotenv";
|
|
5
5
|
import { expand } from "dotenv-expand";
|
|
6
6
|
import { basename, dirname, join, resolve } from "node:path";
|
|
7
7
|
|
|
8
8
|
//#region src/lib/env-loader.ts
|
|
9
|
-
const packageVersion = "2.2.
|
|
9
|
+
const packageVersion = "2.2.1-next-20260927142235";
|
|
10
10
|
function resolveDebugLogger() {
|
|
11
11
|
return container.logger ?? console;
|
|
12
12
|
}
|
|
@@ -29,14 +29,16 @@ function loadEnvFiles(options) {
|
|
|
29
29
|
* @see {@linkplain https://github.com/facebook/create-react-app/blob/d960b9e38c062584ff6cfb1a70e1512509a966e7/packages/react-scripts/config/env.js#L36-L49}
|
|
30
30
|
*/
|
|
31
31
|
let parsed = {};
|
|
32
|
+
const scratch = dotenvSettingsFromProcessEnv();
|
|
32
33
|
for (const dotenvFile of dotenvFiles) {
|
|
33
34
|
const dotenvFileString = typeof dotenvFile === "string" ? dotenvFile : fileURLToPath(dotenvFile);
|
|
34
35
|
log(`loading \`${basename(dotenvFileString)}\``);
|
|
35
|
-
const result =
|
|
36
|
+
const result = config({
|
|
36
37
|
debug: options?.debug,
|
|
37
38
|
encoding: options?.encoding,
|
|
38
|
-
path: dotenvFile
|
|
39
|
-
|
|
39
|
+
path: dotenvFile,
|
|
40
|
+
processEnv: scratch
|
|
41
|
+
});
|
|
40
42
|
if (result.error) {
|
|
41
43
|
if (result.error.code === "ENOENT") {
|
|
42
44
|
log(`\`${basename(dotenvFileString)}\` file not found`);
|
|
@@ -49,6 +51,13 @@ function loadEnvFiles(options) {
|
|
|
49
51
|
...parsed
|
|
50
52
|
};
|
|
51
53
|
}
|
|
54
|
+
const alreadySet = new Set(Object.keys(parsed).filter((key) => process.env[key]));
|
|
55
|
+
for (const key of findReferenceCycles(parsed, alreadySet)) {
|
|
56
|
+
log(`\`${key}\` is part of a circular reference and resolves to an empty string`);
|
|
57
|
+
parsed[key] = "";
|
|
58
|
+
}
|
|
59
|
+
populate(process.env, parsed, { debug: options?.debug });
|
|
60
|
+
parsed = expand({ parsed }).parsed;
|
|
52
61
|
/**
|
|
53
62
|
* @see {@linkplain https://github.com/facebook/create-react-app/blob/d960b9e38c062584ff6cfb1a70e1512509a966e7/packages/react-scripts/config/env.js#L72-L89}
|
|
54
63
|
*/
|
|
@@ -94,6 +103,67 @@ function filterByPrefix(parsed, prefix, log) {
|
|
|
94
103
|
return obj;
|
|
95
104
|
}, {});
|
|
96
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* dotenv reads its own `DOTENV_CONFIG_DEBUG` and `DOTENV_CONFIG_QUIET` switches from the `processEnv` it writes to,
|
|
108
|
+
* so forward the ones set in the real environment to the scratch object used while parsing.
|
|
109
|
+
*/
|
|
110
|
+
function dotenvSettingsFromProcessEnv() {
|
|
111
|
+
const settings = {};
|
|
112
|
+
for (const key of ["DOTENV_CONFIG_DEBUG", "DOTENV_CONFIG_QUIET"]) {
|
|
113
|
+
const value = process.env[key];
|
|
114
|
+
if (value !== void 0) settings[key] = value;
|
|
115
|
+
}
|
|
116
|
+
return settings;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Finds the variables that take part in a reference cycle spanning several variables (`A=${B}`, `B=${A}`).
|
|
120
|
+
*
|
|
121
|
+
* dotenv-expand only stops a cycle when a substituted value equals a whole intermediate result, so once a value
|
|
122
|
+
* around the cycle carries a prefix or suffix (e.g. `C=${B}z`) it never terminates. Resolving the variables of such
|
|
123
|
+
* a cycle to an empty string beforehand keeps loading bounded. Variables already set in `process.env` are left out,
|
|
124
|
+
* as `expand` uses their existing value instead of expanding the one from the file. A variable referencing itself
|
|
125
|
+
* is left alone too: dotenv-expand handles that case on its own.
|
|
126
|
+
*/
|
|
127
|
+
function findReferenceCycles(parsed, alreadySet) {
|
|
128
|
+
const references = /* @__PURE__ */ new Map();
|
|
129
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
130
|
+
if (alreadySet.has(key)) continue;
|
|
131
|
+
const targets = /* @__PURE__ */ new Set();
|
|
132
|
+
for (const match of value.matchAll(/(?<!\\)\$\{?([A-Za-z_][A-Za-z0-9_]*)/g)) {
|
|
133
|
+
const target = match[1];
|
|
134
|
+
if (target !== key && target in parsed && !alreadySet.has(target)) targets.add(target);
|
|
135
|
+
}
|
|
136
|
+
references.set(key, [...targets]);
|
|
137
|
+
}
|
|
138
|
+
const cyclic = /* @__PURE__ */ new Set();
|
|
139
|
+
const indexes = /* @__PURE__ */ new Map();
|
|
140
|
+
const lowLinks = /* @__PURE__ */ new Map();
|
|
141
|
+
const stack = [];
|
|
142
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
143
|
+
let counter = 0;
|
|
144
|
+
const visit = (key) => {
|
|
145
|
+
indexes.set(key, counter);
|
|
146
|
+
lowLinks.set(key, counter);
|
|
147
|
+
counter++;
|
|
148
|
+
stack.push(key);
|
|
149
|
+
onStack.add(key);
|
|
150
|
+
for (const target of references.get(key) ?? []) if (!indexes.has(target)) {
|
|
151
|
+
visit(target);
|
|
152
|
+
lowLinks.set(key, Math.min(lowLinks.get(key), lowLinks.get(target)));
|
|
153
|
+
} else if (onStack.has(target)) lowLinks.set(key, Math.min(lowLinks.get(key), indexes.get(target)));
|
|
154
|
+
if (lowLinks.get(key) !== indexes.get(key)) return;
|
|
155
|
+
const component = [];
|
|
156
|
+
let member;
|
|
157
|
+
do {
|
|
158
|
+
member = stack.pop();
|
|
159
|
+
onStack.delete(member);
|
|
160
|
+
component.push(member);
|
|
161
|
+
} while (member !== key);
|
|
162
|
+
if (component.length > 1) for (const item of component) cyclic.add(item);
|
|
163
|
+
};
|
|
164
|
+
for (const key of references.keys()) if (!indexes.has(key)) visit(key);
|
|
165
|
+
return cyclic;
|
|
166
|
+
}
|
|
97
167
|
function appendSuffix(path, suffix) {
|
|
98
168
|
if (typeof path === "string") return `${path}${suffix}`;
|
|
99
169
|
const file = fileURLToPath(path);
|
|
@@ -221,4 +291,4 @@ function setup(pathOrOptions) {
|
|
|
221
291
|
|
|
222
292
|
//#endregion
|
|
223
293
|
export { envIsUndefined as a, envParseInteger as c, envIsNullish as i, envParseNumber as l, envIsDefined as n, envParseArray as o, envIsNull as r, envParseBoolean as s, setup as t, envParseString as u };
|
|
224
|
-
//# sourceMappingURL=src-
|
|
294
|
+
//# sourceMappingURL=src-BOak8HED.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"src-
|
|
1
|
+
{"version":3,"file":"src-BOak8HED.js","names":[],"sources":["../../src/lib/utils.ts","../../src/lib/setup.ts"],"sourcesContent":["import type { Env, EnvAny, EnvArray, EnvBoolean, EnvInteger, EnvNumber, EnvString } from './types';\n\nexport function envParseInteger(key: EnvInteger, defaultValue?: number): number;\nexport function envParseInteger(key: EnvInteger, defaultValue: number | null): number | null;\nexport function envParseInteger(key: EnvInteger, defaultValue?: number | null): number | null {\n\tconst value = process.env[key];\n\tif (!value) {\n\t\tif (defaultValue === undefined) throw new ReferenceError(`[ENV] ${key} - The key must be an integer, but is empty or undefined.`);\n\t\treturn defaultValue;\n\t}\n\n\tconst integer = Number(value);\n\tif (Number.isInteger(integer)) return integer;\n\tthrow new TypeError(`[ENV] ${key} - The key must be an integer, but received '${value}'.`);\n}\n\nexport function envParseNumber(key: EnvNumber, defaultValue?: number): number;\nexport function envParseNumber(key: EnvNumber, defaultValue: number | null): number | null;\nexport function envParseNumber(key: EnvNumber, defaultValue?: number | null): number | null {\n\tconst value = process.env[key];\n\tif (!value) {\n\t\tif (defaultValue === undefined) throw new ReferenceError(`[ENV] ${key} - The key must be a number, but is empty or undefined.`);\n\t\treturn defaultValue;\n\t}\n\n\tconst integer = Number(value);\n\tif (!Number.isNaN(integer)) return integer;\n\tthrow new TypeError(`[ENV] ${key} - The key must be a number, but received '${value}'.`);\n}\n\nexport function envParseBoolean(key: EnvBoolean, defaultValue?: boolean): boolean;\nexport function envParseBoolean(key: EnvBoolean, defaultValue: boolean | null): boolean | null;\nexport function envParseBoolean(key: EnvBoolean, defaultValue?: boolean | null): boolean | null {\n\tconst value = process.env[key];\n\tif (!value) {\n\t\tif (defaultValue === undefined) throw new ReferenceError(`[ENV] ${key} - The key must be a boolean, but is empty or undefined.`);\n\t\treturn defaultValue;\n\t}\n\n\tif (value.length === 4 && value.toLowerCase() === 'true') return true;\n\tif (value.length === 5 && value.toLowerCase() === 'false') return false;\n\tthrow new TypeError(`[ENV] ${key} - The key must be a boolean, but received '${value}'.`);\n}\n\nexport function envParseString<K extends EnvString>(key: K, defaultValue?: Env[K]): NonNullable<Env[K]>;\nexport function envParseString<K extends EnvString>(key: K, defaultValue: Env[K] | null): NonNullable<Env[K]> | null;\nexport function envParseString<K extends EnvString>(key: K, defaultValue?: Env[K] | null): NonNullable<Env[K]> | null {\n\tconst value = process.env[key];\n\tif (!value) {\n\t\tif (defaultValue === undefined) throw new ReferenceError(`[ENV] ${key} - The key must be a string, but is empty or undefined.`);\n\t\treturn defaultValue!;\n\t}\n\n\treturn value!;\n}\n\nexport function envParseArray(key: EnvArray, defaultValue?: string[]): string[];\nexport function envParseArray(key: EnvArray, defaultValue: string[] | null): string[] | null;\nexport function envParseArray(key: EnvArray, defaultValue?: string[] | null): string[] | null {\n\tconst value = process.env[key];\n\tif (!value) {\n\t\tif (defaultValue === undefined) throw new ReferenceError(`[ENV] ${key} - The key must be an array, but is empty or undefined.`);\n\t\treturn defaultValue;\n\t}\n\n\treturn value.split(' ');\n}\n\n/**\n * Checks if the value of the specified environment variable is null.\n * @param key - The name of the environment variable.\n * @returns Whether the value of the specified environment variable is:\n * - The string `\"0\"`\n * - The string `\"null\"`, case insensitive\n */\nexport function envIsNull(key: EnvAny): boolean {\n\tconst value = process.env[key];\n\treturn typeof value === 'string' && (value === '0' || (value.length === 4 && value.toLowerCase() === 'null'));\n}\n\n/**\n * Checks if the value of the specified environment variable is undefined.\n * @param key - The name of the environment variable.\n * @returns Whether the value of the specified environment variable is:\n * - `undefined`\n * - An empty string (`\"\"`)\n */\nexport function envIsUndefined(key: EnvAny): boolean {\n\tconst value = process.env[key];\n\treturn value === undefined || value.length === 0;\n}\n\n/**\n * Checks if the value of the specified environment variable is nullish.\n * @param key - The name of the environment variable.\n * @returns Whether the value of the specified environment variable is:\n * - `undefined`\n * - An empty string (`\"\"`)\n * - The string `\"0\"`\n * - The string `\"null\"`, case insensitive\n */\nexport function envIsNullish(key: EnvAny): boolean {\n\treturn envIsUndefined(key) || envIsNull(key);\n}\n\n/**\n * Checks if any of the specified environment variables is defined.\n * @param key - The name of the environment variable.\n * @returns Whether the value of any of the specified environment variables is a non-empty string\n */\nexport function envIsDefined(...keys: readonly EnvAny[]): boolean {\n\treturn keys.every((key) => !envIsUndefined(key));\n}\n","import type { DotenvConfigOutput } from 'dotenv';\nimport { fileURLToPath } from 'node:url';\nimport { loadEnvFiles, type EnvLoaderOptions } from './env-loader';\nimport { envIsDefined, envParseBoolean } from './utils';\n\nexport function setup(pathOrOptions?: string | URL | EnvSetupOptions): DotenvConfigOutput {\n\t// Unless explicitly defined, set NODE_ENV as development:\n\tprocess.env.NODE_ENV ??= 'development';\n\n\tlet options: EnvLoaderOptions;\n\tif (typeof pathOrOptions === 'undefined') {\n\t\toptions = {};\n\t} else if (typeof pathOrOptions === 'string') {\n\t\toptions = { path: pathOrOptions };\n\t} else if (typeof pathOrOptions === 'object') {\n\t\tif (pathOrOptions instanceof URL) {\n\t\t\toptions = { path: fileURLToPath(pathOrOptions) };\n\t\t} else {\n\t\t\tconst { path, ...rest } = pathOrOptions;\n\t\t\toptions = { path: typeof path === 'undefined' ? path : typeof path === 'string' ? path : fileURLToPath(path), ...rest };\n\t\t}\n\t} else {\n\t\tthrow new TypeError('Expected undefined, string, URL, or EnvSetupOptions');\n\t}\n\n\treturn loadEnvFiles({\n\t\tdebug: envIsDefined('DOTENV_DEBUG') ? envParseBoolean('DOTENV_DEBUG') : undefined,\n\t\tencoding: process.env.DOTENV_ENCODING,\n\t\tenv: process.env.DOTENV_ENV,\n\t\tpath: process.env.DOTENV_PATH,\n\t\tprefix: process.env.DOTENV_PREFIX,\n\t\tloader: process.env.DOTENV_LOADER,\n\t\t...options\n\t});\n}\n\nexport interface EnvSetupOptions extends Omit<EnvLoaderOptions, 'path'> {\n\t/**\n\t * You may specify a custom path if your file containing environment variables is located elsewhere.\n\t */\n\tpath?: string | URL;\n}\n\nexport type EnvSetupResult = DotenvConfigOutput;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wolfstar/env-utilities",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1-next-20260927142235",
|
|
4
4
|
"description": "Functional utilities for reading and parsing environmental variables",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api",
|
|
@@ -65,12 +65,12 @@
|
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@redstardev/unplugin-version-injector": "^0.0.2",
|
|
68
|
-
"@vitest/coverage-v8": "^
|
|
68
|
+
"@vitest/coverage-v8": "^5.0.0",
|
|
69
69
|
"golar": "^0.1.10",
|
|
70
70
|
"tsdown": "^0.23.0",
|
|
71
71
|
"typescript": "~7.0.2",
|
|
72
72
|
"varlock": "^1.19.0",
|
|
73
|
-
"vitest": "^
|
|
73
|
+
"vitest": "^5.0.0"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"varlock": "^1.19.0"
|