@push.rocks/qenv 6.1.5 → 8.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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +2 -1
- package/dist_ts/qenv.classes.missingrequiredenvvarserror.d.ts +18 -0
- package/dist_ts/qenv.classes.missingrequiredenvvarserror.js +19 -0
- package/dist_ts/qenv.classes.qenv.d.ts +45 -9
- package/dist_ts/qenv.classes.qenv.js +94 -50
- package/dist_ts/qenv.plugins.d.ts +0 -4
- package/dist_ts/qenv.plugins.js +1 -7
- package/package.json +6 -8
- package/readme.md +76 -15
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +1 -0
- package/ts/qenv.classes.missingrequiredenvvarserror.ts +30 -0
- package/ts/qenv.classes.qenv.ts +135 -68
- package/ts/qenv.plugins.ts +0 -12
- package/dist_ts/qenv.classes.configvaultadapter.d.ts +0 -6
- package/dist_ts/qenv.classes.configvaultadapter.js +0 -24
- package/readme.hints.md +0 -1
- package/ts/qenv.classes.configvaultadapter.ts +0 -31
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/qenv',
|
|
6
|
-
version: '
|
|
6
|
+
version: '8.0.0',
|
|
7
7
|
description: 'A module for easily handling environment variables in Node.js projects with support for .yml and .json configuration.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxrQkFBa0I7SUFDeEIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHVIQUF1SDtDQUNySSxDQUFBIn0=
|
package/dist_ts/index.d.ts
CHANGED
package/dist_ts/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export * from './qenv.classes.qenv.js';
|
|
2
|
-
|
|
2
|
+
export * from './qenv.classes.missingrequiredenvvarserror.js';
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMsK0NBQStDLENBQUMifQ==
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface IQenvMissingRequiredEnvVarsErrorOptions {
|
|
2
|
+
missingEnvVars: string[];
|
|
3
|
+
requiredEnvVars: string[];
|
|
4
|
+
qenvFilePathAbsolute: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Thrown when a variable listed under `required:` in qenv.yml is not provided by any source.
|
|
8
|
+
* The names are carried as data so a caller can report or branch on them without parsing the
|
|
9
|
+
* message, and `code` identifies the refusal even when two copies of qenv end up in one tree,
|
|
10
|
+
* which makes `instanceof` unreliable.
|
|
11
|
+
*/
|
|
12
|
+
export declare class QenvMissingRequiredEnvVarsError extends Error {
|
|
13
|
+
readonly code = "QENV_MISSING_REQUIRED_ENV_VARS";
|
|
14
|
+
readonly missingEnvVars: string[];
|
|
15
|
+
readonly requiredEnvVars: string[];
|
|
16
|
+
readonly qenvFilePathAbsolute: string;
|
|
17
|
+
constructor(optionsArg: IQenvMissingRequiredEnvVarsErrorOptions);
|
|
18
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a variable listed under `required:` in qenv.yml is not provided by any source.
|
|
3
|
+
* The names are carried as data so a caller can report or branch on them without parsing the
|
|
4
|
+
* message, and `code` identifies the refusal even when two copies of qenv end up in one tree,
|
|
5
|
+
* which makes `instanceof` unreliable.
|
|
6
|
+
*/
|
|
7
|
+
export class QenvMissingRequiredEnvVarsError extends Error {
|
|
8
|
+
constructor(optionsArg) {
|
|
9
|
+
super(`qenv is missing required environment variables: ${optionsArg.missingEnvVars.join(', ')}. ` +
|
|
10
|
+
`They are listed under "required:" in ${optionsArg.qenvFilePathAbsolute} and were found ` +
|
|
11
|
+
`neither in the process environment, nor in the env file, nor in the Docker secrets.`);
|
|
12
|
+
this.code = 'QENV_MISSING_REQUIRED_ENV_VARS';
|
|
13
|
+
this.name = 'QenvMissingRequiredEnvVarsError';
|
|
14
|
+
this.missingEnvVars = [...optionsArg.missingEnvVars];
|
|
15
|
+
this.requiredEnvVars = [...optionsArg.requiredEnvVars];
|
|
16
|
+
this.qenvFilePathAbsolute = optionsArg.qenvFilePathAbsolute;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWVudi5jbGFzc2VzLm1pc3NpbmdyZXF1aXJlZGVudnZhcnNlcnJvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3FlbnYuY2xhc3Nlcy5taXNzaW5ncmVxdWlyZWRlbnZ2YXJzZXJyb3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBTUE7Ozs7O0dBS0c7QUFDSCxNQUFNLE9BQU8sK0JBQWdDLFNBQVEsS0FBSztJQU14RCxZQUFZLFVBQW1EO1FBQzdELEtBQUssQ0FDSCxtREFBbUQsVUFBVSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUk7WUFDekYsd0NBQXdDLFVBQVUsQ0FBQyxvQkFBb0Isa0JBQWtCO1lBQ3pGLHFGQUFxRixDQUN4RixDQUFDO1FBVlksU0FBSSxHQUFHLGdDQUFnQyxDQUFDO1FBV3RELElBQUksQ0FBQyxJQUFJLEdBQUcsaUNBQWlDLENBQUM7UUFDOUMsSUFBSSxDQUFDLGNBQWMsR0FBRyxDQUFDLEdBQUcsVUFBVSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQ3JELElBQUksQ0FBQyxlQUFlLEdBQUcsQ0FBQyxHQUFHLFVBQVUsQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUN2RCxJQUFJLENBQUMsb0JBQW9CLEdBQUcsVUFBVSxDQUFDLG9CQUFvQixDQUFDO0lBQzlELENBQUM7Q0FDRiJ9
|
|
@@ -1,34 +1,71 @@
|
|
|
1
|
-
import { CloudlyAdapter } from './qenv.classes.configvaultadapter.js';
|
|
2
1
|
import * as plugins from './qenv.plugins.js';
|
|
2
|
+
/**
|
|
3
|
+
* a reference to an environment variable: its name, or an async function that produces the value.
|
|
4
|
+
* The function form is only honoured by the async getters; the synchronous getter accepts names.
|
|
5
|
+
*/
|
|
3
6
|
export type TEnvVarRef = string | (() => Promise<string>);
|
|
4
|
-
|
|
7
|
+
/** the resolved value of every available required env var, always a string */
|
|
8
|
+
export type TEnvVarValueMap = Record<string, string>;
|
|
5
9
|
export declare class Qenv {
|
|
10
|
+
/** the names listed under `required:` in qenv.yml */
|
|
6
11
|
requiredEnvVars: string[];
|
|
12
|
+
/** the required names that a source provided */
|
|
7
13
|
availableEnvVars: string[];
|
|
14
|
+
/** the required names that no source provided */
|
|
8
15
|
missingEnvVars: string[];
|
|
9
|
-
|
|
16
|
+
/** the resolved value of every available required name */
|
|
17
|
+
keyValueObject: TEnvVarValueMap;
|
|
10
18
|
logger: plugins.smartlog.ConsoleLog;
|
|
11
|
-
cloudlyAdapter: CloudlyAdapter;
|
|
12
19
|
qenvFilePathAbsolute: string;
|
|
13
20
|
envFilePathAbsolute?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Resolves every name listed under `required:` in qenv.yml while constructing.
|
|
23
|
+
* @param qenvFileBasePathArg directory that holds qenv.yml
|
|
24
|
+
* @param envFileBasePathArg directory that holds env.json, env.yml or env.yaml
|
|
25
|
+
* @param failOnMissing throws a QenvMissingRequiredEnvVarsError when a variable listed under
|
|
26
|
+
* `required:` in qenv.yml is not provided by any source. Pass false to only record the names in
|
|
27
|
+
* `missingEnvVars` and continue.
|
|
28
|
+
* @throws QenvMissingRequiredEnvVarsError
|
|
29
|
+
*/
|
|
14
30
|
constructor(qenvFileBasePathArg?: string, envFileBasePathArg?: string, failOnMissing?: boolean);
|
|
15
31
|
private initializeFilePaths;
|
|
16
32
|
private loadRequiredEnvVars;
|
|
17
33
|
private loadAvailableEnvVars;
|
|
18
34
|
private checkForMissingEnvVars;
|
|
35
|
+
/**
|
|
36
|
+
* Resolves an env var from the process environment, the env file and the Docker secrets, in that
|
|
37
|
+
* order. An array is tried left to right and the first defined value wins.
|
|
38
|
+
* @param envVarNameOrNames a name, an async resolver function, or a list of either
|
|
39
|
+
*/
|
|
19
40
|
getEnvVarOnDemand(envVarNameOrNames: TEnvVarRef | TEnvVarRef[]): Promise<string | undefined>;
|
|
20
41
|
/**
|
|
21
|
-
* Like getEnvVarOnDemand, but throws
|
|
22
|
-
* @param envVarNameOrNames
|
|
23
|
-
* @returns
|
|
42
|
+
* Like getEnvVarOnDemand, but throws when no source provides a value.
|
|
43
|
+
* @param envVarNameOrNames a name, an async resolver function, or a list of either
|
|
24
44
|
*/
|
|
25
45
|
getEnvVarOnDemandStrict(envVarNameOrNames: TEnvVarRef | TEnvVarRef[]): Promise<string>;
|
|
46
|
+
/**
|
|
47
|
+
* The synchronous counterpart of getEnvVarOnDemand. It resolves names only: an async resolver
|
|
48
|
+
* function cannot be awaited here, so the function form of TEnvVarRef is not accepted.
|
|
49
|
+
* @param envVarNameOrNames a name or a list of names
|
|
50
|
+
*/
|
|
26
51
|
getEnvVarOnDemandSync(envVarNameOrNames: string | string[]): string | undefined;
|
|
27
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Resolves an env var whose value was stored as a base64 encoded object and decodes it. A plain
|
|
54
|
+
* value is returned as the string it is, so the caller narrows what it gets.
|
|
55
|
+
* @param envVarNameOrNames a name or a list of names
|
|
56
|
+
*/
|
|
57
|
+
getEnvVarOnDemandAsObject(envVarNameOrNames: string | string[]): Promise<unknown>;
|
|
28
58
|
private tryGetEnvVar;
|
|
29
59
|
private tryGetEnvVarSync;
|
|
60
|
+
/** renders env var references for an error message, naming a resolver function where it has one */
|
|
61
|
+
private describeEnvVarRefs;
|
|
30
62
|
private getFromEnvironmentVariable;
|
|
31
63
|
private getFromEnvYamlOrJsonFile;
|
|
64
|
+
/**
|
|
65
|
+
* the directory Docker mounts secrets into. It is a method so a test can point both secret
|
|
66
|
+
* readers at a directory it is allowed to create; a process can never write /run/secrets itself.
|
|
67
|
+
*/
|
|
68
|
+
protected getDockerSecretsDirectoryPath(): string;
|
|
32
69
|
private getFromDockerSecret;
|
|
33
70
|
private getFromDockerSecretJson;
|
|
34
71
|
private encodeBase64;
|
|
@@ -37,4 +74,3 @@ export declare class Qenv {
|
|
|
37
74
|
private directoryExists;
|
|
38
75
|
private readObjectFromFile;
|
|
39
76
|
}
|
|
40
|
-
export {};
|
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
import { CloudlyAdapter } from './qenv.classes.configvaultadapter.js';
|
|
2
1
|
import * as plugins from './qenv.plugins.js';
|
|
2
|
+
import { QenvMissingRequiredEnvVarsError } from './qenv.classes.missingrequiredenvvarserror.js';
|
|
3
3
|
export class Qenv {
|
|
4
|
+
/**
|
|
5
|
+
* Resolves every name listed under `required:` in qenv.yml while constructing.
|
|
6
|
+
* @param qenvFileBasePathArg directory that holds qenv.yml
|
|
7
|
+
* @param envFileBasePathArg directory that holds env.json, env.yml or env.yaml
|
|
8
|
+
* @param failOnMissing throws a QenvMissingRequiredEnvVarsError when a variable listed under
|
|
9
|
+
* `required:` in qenv.yml is not provided by any source. Pass false to only record the names in
|
|
10
|
+
* `missingEnvVars` and continue.
|
|
11
|
+
* @throws QenvMissingRequiredEnvVarsError
|
|
12
|
+
*/
|
|
4
13
|
constructor(qenvFileBasePathArg = process.cwd(), envFileBasePathArg, failOnMissing = true) {
|
|
14
|
+
/** the names listed under `required:` in qenv.yml */
|
|
5
15
|
this.requiredEnvVars = [];
|
|
16
|
+
/** the required names that a source provided */
|
|
6
17
|
this.availableEnvVars = [];
|
|
18
|
+
/** the required names that no source provided */
|
|
7
19
|
this.missingEnvVars = [];
|
|
20
|
+
/** the resolved value of every available required name */
|
|
8
21
|
this.keyValueObject = {};
|
|
9
22
|
this.logger = new plugins.smartlog.ConsoleLog();
|
|
10
23
|
this.qenvFilePathAbsolute = '';
|
|
11
|
-
this.cloudlyAdapter = new CloudlyAdapter();
|
|
12
24
|
this.initializeFilePaths(qenvFileBasePathArg, envFileBasePathArg);
|
|
13
25
|
this.loadRequiredEnvVars();
|
|
14
26
|
this.loadAvailableEnvVars();
|
|
@@ -40,20 +52,25 @@ export class Qenv {
|
|
|
40
52
|
}
|
|
41
53
|
}
|
|
42
54
|
loadRequiredEnvVars() {
|
|
43
|
-
if (this.fileExists(this.qenvFilePathAbsolute)) {
|
|
44
|
-
|
|
45
|
-
const requiredEnvVars = qenvFile.required;
|
|
46
|
-
if (Array.isArray(requiredEnvVars)) {
|
|
47
|
-
this.requiredEnvVars.push(...requiredEnvVars.filter((envVar) => typeof envVar === 'string'));
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
this.logger.log('warn', 'qenv.yml does not contain a "required" Array!');
|
|
51
|
-
}
|
|
55
|
+
if (!this.fileExists(this.qenvFilePathAbsolute)) {
|
|
56
|
+
return;
|
|
52
57
|
}
|
|
58
|
+
const qenvFile = this.readObjectFromFile(this.qenvFilePathAbsolute);
|
|
59
|
+
const declaredRequirements = qenvFile['required'];
|
|
60
|
+
if (!Array.isArray(declaredRequirements)) {
|
|
61
|
+
this.logger.log('warn', 'qenv.yml does not contain a "required" Array!');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const declaredEntries = declaredRequirements;
|
|
65
|
+
this.requiredEnvVars.push(...declaredEntries.filter((entry) => typeof entry === 'string'));
|
|
53
66
|
}
|
|
54
67
|
loadAvailableEnvVars() {
|
|
68
|
+
// resolved synchronously: a required env var is always a name, and every source a name can come
|
|
69
|
+
// from - process environment, env file, Docker secrets - is a synchronous read. Resolving it
|
|
70
|
+
// here as a promise would store a pending promise instead of a value and make the check below
|
|
71
|
+
// pass for every name.
|
|
55
72
|
for (const envVar of this.requiredEnvVars) {
|
|
56
|
-
const value = this.
|
|
73
|
+
const value = this.tryGetEnvVarSync(envVar);
|
|
57
74
|
if (value !== undefined) {
|
|
58
75
|
this.availableEnvVars.push(envVar);
|
|
59
76
|
this.keyValueObject[envVar] = value;
|
|
@@ -62,18 +79,24 @@ export class Qenv {
|
|
|
62
79
|
}
|
|
63
80
|
checkForMissingEnvVars(failOnMissing) {
|
|
64
81
|
this.missingEnvVars = this.requiredEnvVars.filter((envVar) => !this.availableEnvVars.includes(envVar));
|
|
65
|
-
if (this.missingEnvVars.length
|
|
66
|
-
|
|
67
|
-
console.error('Missing Env Vars:', this.missingEnvVars);
|
|
68
|
-
if (failOnMissing) {
|
|
69
|
-
this.logger.log('error', 'Exiting due to missing env vars!');
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
this.logger.log('warn', 'qenv is not set to fail on missing environment variables');
|
|
74
|
-
}
|
|
82
|
+
if (this.missingEnvVars.length === 0) {
|
|
83
|
+
return;
|
|
75
84
|
}
|
|
85
|
+
if (failOnMissing) {
|
|
86
|
+
// a library must not end the process: the caller decides what an incomplete environment means
|
|
87
|
+
throw new QenvMissingRequiredEnvVarsError({
|
|
88
|
+
missingEnvVars: this.missingEnvVars,
|
|
89
|
+
requiredEnvVars: this.requiredEnvVars,
|
|
90
|
+
qenvFilePathAbsolute: this.qenvFilePathAbsolute,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
this.logger.log('warn', `qenv is not set to fail on missing environment variables. Missing: ${this.missingEnvVars.join(', ')}`);
|
|
76
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Resolves an env var from the process environment, the env file and the Docker secrets, in that
|
|
97
|
+
* order. An array is tried left to right and the first defined value wins.
|
|
98
|
+
* @param envVarNameOrNames a name, an async resolver function, or a list of either
|
|
99
|
+
*/
|
|
77
100
|
async getEnvVarOnDemand(envVarNameOrNames) {
|
|
78
101
|
if (Array.isArray(envVarNameOrNames)) {
|
|
79
102
|
for (const envVarName of envVarNameOrNames) {
|
|
@@ -89,19 +112,22 @@ export class Qenv {
|
|
|
89
112
|
}
|
|
90
113
|
}
|
|
91
114
|
/**
|
|
92
|
-
* Like getEnvVarOnDemand, but throws
|
|
93
|
-
* @param envVarNameOrNames
|
|
94
|
-
* @returns
|
|
115
|
+
* Like getEnvVarOnDemand, but throws when no source provides a value.
|
|
116
|
+
* @param envVarNameOrNames a name, an async resolver function, or a list of either
|
|
95
117
|
*/
|
|
96
118
|
async getEnvVarOnDemandStrict(envVarNameOrNames) {
|
|
97
119
|
const value = await this.getEnvVarOnDemand(envVarNameOrNames);
|
|
98
120
|
if (value === undefined) {
|
|
99
|
-
throw new Error(`Env var ${envVarNameOrNames} is not set!`);
|
|
121
|
+
throw new Error(`Env var ${this.describeEnvVarRefs(envVarNameOrNames)} is not set!`);
|
|
100
122
|
}
|
|
101
123
|
return value;
|
|
102
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* The synchronous counterpart of getEnvVarOnDemand. It resolves names only: an async resolver
|
|
127
|
+
* function cannot be awaited here, so the function form of TEnvVarRef is not accepted.
|
|
128
|
+
* @param envVarNameOrNames a name or a list of names
|
|
129
|
+
*/
|
|
103
130
|
getEnvVarOnDemandSync(envVarNameOrNames) {
|
|
104
|
-
console.warn('requesting env var sync leaves out potentially important async env sources.');
|
|
105
131
|
if (Array.isArray(envVarNameOrNames)) {
|
|
106
132
|
for (const envVarName of envVarNameOrNames) {
|
|
107
133
|
const value = this.tryGetEnvVarSync(envVarName);
|
|
@@ -115,6 +141,11 @@ export class Qenv {
|
|
|
115
141
|
return this.tryGetEnvVarSync(envVarNameOrNames);
|
|
116
142
|
}
|
|
117
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Resolves an env var whose value was stored as a base64 encoded object and decodes it. A plain
|
|
146
|
+
* value is returned as the string it is, so the caller narrows what it gets.
|
|
147
|
+
* @param envVarNameOrNames a name or a list of names
|
|
148
|
+
*/
|
|
118
149
|
async getEnvVarOnDemandAsObject(envVarNameOrNames) {
|
|
119
150
|
const rawValue = await this.getEnvVarOnDemand(envVarNameOrNames);
|
|
120
151
|
if (rawValue && rawValue.startsWith('base64Object:')) {
|
|
@@ -127,33 +158,36 @@ export class Qenv {
|
|
|
127
158
|
if (typeof envVarRefArg === 'function') {
|
|
128
159
|
return await envVarRefArg();
|
|
129
160
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
this.getFromEnvYamlOrJsonFile(envVarRefArg),
|
|
133
|
-
this.getFromDockerSecret(envVarRefArg),
|
|
134
|
-
this.getFromDockerSecretJson(envVarRefArg)
|
|
135
|
-
];
|
|
136
|
-
for (const value of sources) {
|
|
137
|
-
if (value !== undefined) {
|
|
138
|
-
return value;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return undefined;
|
|
161
|
+
// a name resolves from synchronous sources only, so both getters share one resolution order
|
|
162
|
+
return this.tryGetEnvVarSync(envVarRefArg);
|
|
142
163
|
}
|
|
143
164
|
tryGetEnvVarSync(envVarName) {
|
|
165
|
+
// read lazily: a source is only touched once every earlier one came back undefined, so a name
|
|
166
|
+
// the process environment answers never opens a secret file, and a malformed secret.json only
|
|
167
|
+
// ever affects the names that actually reach it
|
|
144
168
|
const sources = [
|
|
145
|
-
this.getFromEnvironmentVariable(envVarName),
|
|
146
|
-
this.getFromEnvYamlOrJsonFile(envVarName),
|
|
147
|
-
this.getFromDockerSecret(envVarName),
|
|
148
|
-
this.getFromDockerSecretJson(envVarName)
|
|
169
|
+
() => this.getFromEnvironmentVariable(envVarName),
|
|
170
|
+
() => this.getFromEnvYamlOrJsonFile(envVarName),
|
|
171
|
+
() => this.getFromDockerSecret(envVarName),
|
|
172
|
+
() => this.getFromDockerSecretJson(envVarName),
|
|
149
173
|
];
|
|
150
|
-
for (const
|
|
174
|
+
for (const readSource of sources) {
|
|
175
|
+
const value = readSource();
|
|
151
176
|
if (value !== undefined) {
|
|
152
177
|
return value;
|
|
153
178
|
}
|
|
154
179
|
}
|
|
155
180
|
return undefined;
|
|
156
181
|
}
|
|
182
|
+
/** renders env var references for an error message, naming a resolver function where it has one */
|
|
183
|
+
describeEnvVarRefs(envVarNameOrNames) {
|
|
184
|
+
const envVarRefs = Array.isArray(envVarNameOrNames) ? envVarNameOrNames : [envVarNameOrNames];
|
|
185
|
+
return envVarRefs
|
|
186
|
+
.map((envVarRef) => typeof envVarRef === 'function'
|
|
187
|
+
? `${envVarRef.name || 'anonymous'}()`
|
|
188
|
+
: envVarRef)
|
|
189
|
+
.join(', ');
|
|
190
|
+
}
|
|
157
191
|
getFromEnvironmentVariable(envVarName) {
|
|
158
192
|
return process.env[envVarName];
|
|
159
193
|
}
|
|
@@ -176,19 +210,27 @@ export class Qenv {
|
|
|
176
210
|
return undefined;
|
|
177
211
|
}
|
|
178
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* the directory Docker mounts secrets into. It is a method so a test can point both secret
|
|
215
|
+
* readers at a directory it is allowed to create; a process can never write /run/secrets itself.
|
|
216
|
+
*/
|
|
217
|
+
getDockerSecretsDirectoryPath() {
|
|
218
|
+
return '/run/secrets';
|
|
219
|
+
}
|
|
179
220
|
getFromDockerSecret(envVarName) {
|
|
180
|
-
const secretPath =
|
|
221
|
+
const secretPath = plugins.path.join(this.getDockerSecretsDirectoryPath(), envVarName);
|
|
181
222
|
if (this.fileExists(secretPath)) {
|
|
182
223
|
return plugins.fs.readFileSync(secretPath, 'utf8');
|
|
183
224
|
}
|
|
184
225
|
return undefined;
|
|
185
226
|
}
|
|
186
227
|
getFromDockerSecretJson(envVarName) {
|
|
187
|
-
|
|
188
|
-
|
|
228
|
+
const secretsDirectoryPath = this.getDockerSecretsDirectoryPath();
|
|
229
|
+
if (this.directoryExists(secretsDirectoryPath)) {
|
|
230
|
+
const availableSecrets = plugins.fs.readdirSync(secretsDirectoryPath);
|
|
189
231
|
for (const secret of availableSecrets) {
|
|
190
232
|
if (secret.includes('secret.json')) {
|
|
191
|
-
const secretObject = this.readObjectFromFile(
|
|
233
|
+
const secretObject = this.readObjectFromFile(plugins.path.join(secretsDirectoryPath, secret));
|
|
192
234
|
const value = secretObject[envVarName];
|
|
193
235
|
if (value === undefined) {
|
|
194
236
|
continue;
|
|
@@ -229,7 +271,9 @@ export class Qenv {
|
|
|
229
271
|
const parsedObject = filePath.endsWith('.json')
|
|
230
272
|
? JSON.parse(fileString)
|
|
231
273
|
: plugins.yaml.parse(fileString);
|
|
232
|
-
return typeof parsedObject === 'object' && parsedObject !== null
|
|
274
|
+
return typeof parsedObject === 'object' && parsedObject !== null
|
|
275
|
+
? parsedObject
|
|
276
|
+
: {};
|
|
233
277
|
}
|
|
234
278
|
}
|
|
235
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWVudi5jbGFzc2VzLnFlbnYuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9xZW52LmNsYXNzZXMucWVudi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0NBQXNDLENBQUM7QUFDdEUsT0FBTyxLQUFLLE9BQU8sTUFBTSxtQkFBbUIsQ0FBQztBQUs3QyxNQUFNLE9BQU8sSUFBSTtJQVlmLFlBQ0Usc0JBQThCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFDM0Msa0JBQTJCLEVBQzNCLGdCQUF5QixJQUFJO1FBZHhCLG9CQUFlLEdBQWEsRUFBRSxDQUFDO1FBQy9CLHFCQUFnQixHQUFhLEVBQUUsQ0FBQztRQUNoQyxtQkFBYyxHQUFhLEVBQUUsQ0FBQztRQUM5QixtQkFBYyxHQUFvQixFQUFFLENBQUM7UUFDckMsV0FBTSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUkzQyx5QkFBb0IsR0FBRyxFQUFFLENBQUM7UUFRL0IsSUFBSSxDQUFDLGNBQWMsR0FBRyxJQUFJLGNBQWMsRUFBRSxDQUFDO1FBQzNDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxtQkFBbUIsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO1FBQ2xFLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO1FBQzNCLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO1FBQzVCLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUM3QyxDQUFDO0lBRU8sbUJBQW1CLENBQUMsbUJBQTJCLEVBQUUsa0JBQTJCO1FBQ2xGLElBQUksQ0FBQyxvQkFBb0IsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FDM0MsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsbUJBQW1CLENBQUMsRUFDekMsVUFBVSxDQUNYLENBQUM7UUFFRixJQUFJLGtCQUFrQixFQUFFLENBQUM7WUFDdkIs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
|
|
279
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWVudi5jbGFzc2VzLnFlbnYuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9xZW52LmNsYXNzZXMucWVudi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLG1CQUFtQixDQUFDO0FBQzdDLE9BQU8sRUFBRSwrQkFBK0IsRUFBRSxNQUFNLCtDQUErQyxDQUFDO0FBY2hHLE1BQU0sT0FBTyxJQUFJO0lBa0JmOzs7Ozs7OztPQVFHO0lBQ0gsWUFDRSxzQkFBOEIsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUMzQyxrQkFBMkIsRUFDM0IsZ0JBQXlCLElBQUk7UUE3Qi9CLHFEQUFxRDtRQUM5QyxvQkFBZSxHQUFhLEVBQUUsQ0FBQztRQUV0QyxnREFBZ0Q7UUFDekMscUJBQWdCLEdBQWEsRUFBRSxDQUFDO1FBRXZDLGlEQUFpRDtRQUMxQyxtQkFBYyxHQUFhLEVBQUUsQ0FBQztRQUVyQywwREFBMEQ7UUFDbkQsbUJBQWMsR0FBb0IsRUFBRSxDQUFDO1FBRXJDLFdBQU0sR0FBRyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUM7UUFFM0MseUJBQW9CLEdBQUcsRUFBRSxDQUFDO1FBaUIvQixJQUFJLENBQUMsbUJBQW1CLENBQUMsbUJBQW1CLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztRQUNsRSxJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUMzQixJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztRQUM1QixJQUFJLENBQUMsc0JBQXNCLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDN0MsQ0FBQztJQUVPLG1CQUFtQixDQUFDLG1CQUEyQixFQUFFLGtCQUEyQjtRQUNsRixJQUFJLENBQUMsb0JBQW9CLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQzNDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDLEVBQ3pDLFVBQVUsQ0FDWCxDQUFDO1FBRUYsSUFBSSxrQkFBa0IsRUFBRSxDQUFDO1lBQ3ZCLE1BQU0sZUFBZSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGtCQUFrQixDQUFDLENBQUM7WUFFakUsTUFBTSxlQUFlLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsZUFBZSxFQUFFLFVBQVUsQ0FBQyxDQUFDO1lBQ3ZFLE1BQU0sY0FBYyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRSxTQUFTLENBQUMsQ0FBQztZQUNyRSxNQUFNLGVBQWUsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUUsVUFBVSxDQUFDLENBQUM7WUFFdkUsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLGVBQWUsQ0FBQyxDQUFDO1lBQzNELE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUMsQ0FBQztZQUN6RCxNQUFNLGlCQUFpQixHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsZUFBZSxDQUFDLENBQUM7WUFFM0QsSUFBSSxpQkFBaUIsSUFBSSxDQUFDLGdCQUFnQixJQUFJLGlCQUFpQixDQUFDLEVBQUUsQ0FBQztnQkFDakUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLHVEQUF1RCxDQUFDLENBQUM7Z0JBQ2pGLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxlQUFlLENBQUM7WUFDN0MsQ0FBQztpQkFBTSxJQUFJLGlCQUFpQixFQUFFLENBQUM7Z0JBQzdCLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxlQUFlLENBQUM7WUFDN0MsQ0FBQztpQkFBTSxJQUFJLGdCQUFnQixFQUFFLENBQUM7Z0JBQzVCLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxjQUFjLENBQUM7WUFDNUMsQ0FBQztpQkFBTSxJQUFJLGlCQUFpQixFQUFFLENBQUM7Z0JBQzdCLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxlQUFlLENBQUM7WUFDN0MsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRU8sbUJBQW1CO1FBQ3pCLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxFQUFFLENBQUM7WUFDaEQsT0FBTztRQUNULENBQUM7UUFDRCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLENBQUM7UUFDcEUsTUFBTSxvQkFBb0IsR0FBRyxRQUFRLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDbEQsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsb0JBQW9CLENBQUMsRUFBRSxDQUFDO1lBQ3pDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSwrQ0FBK0MsQ0FBQyxDQUFDO1lBQ3pFLE9BQU87UUFDVCxDQUFDO1FBQ0QsTUFBTSxlQUFlLEdBQWMsb0JBQW9CLENBQUM7UUFDeEQsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQ3ZCLEdBQUcsZUFBZSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEtBQUssRUFBbUIsRUFBRSxDQUFDLE9BQU8sS0FBSyxLQUFLLFFBQVEsQ0FBQyxDQUNqRixDQUFDO0lBQ0osQ0FBQztJQUVPLG9CQUFvQjtRQUMxQixnR0FBZ0c7UUFDaEcsNkZBQTZGO1FBQzdGLDhGQUE4RjtRQUM5Rix1QkFBdUI7UUFDdkIsS0FBSyxNQUFNLE1BQU0sSUFBSSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDMUMsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQzVDLElBQUksS0FBSyxLQUFLLFNBQVMsRUFBRSxDQUFDO2dCQUN4QixJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUNuQyxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxHQUFHLEtBQUssQ0FBQztZQUN0QyxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFTyxzQkFBc0IsQ0FBQyxhQUFzQjtRQUNuRCxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUMvQyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUNwRCxDQUFDO1FBRUYsSUFBSSxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUNyQyxPQUFPO1FBQ1QsQ0FBQztRQUVELElBQUksYUFBYSxFQUFFLENBQUM7WUFDbEIsOEZBQThGO1lBQzlGLE1BQU0sSUFBSSwrQkFBK0IsQ0FBQztnQkFDeEMsY0FBYyxFQUFFLElBQUksQ0FBQyxjQUFjO2dCQUNuQyxlQUFlLEVBQUUsSUFBSSxDQUFDLGVBQWU7Z0JBQ3JDLG9CQUFvQixFQUFFLElBQUksQ0FBQyxvQkFBb0I7YUFDaEQsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUVELElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUNiLE1BQU0sRUFDTixzRUFBc0UsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FDdkcsQ0FBQztJQUNKLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksS0FBSyxDQUFDLGlCQUFpQixDQUM1QixpQkFBNEM7UUFFNUMsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLGlCQUFpQixDQUFDLEVBQUUsQ0FBQztZQUNyQyxLQUFLLE1BQU0sVUFBVSxJQUFJLGlCQUFpQixFQUFFLENBQUM7Z0JBQzNDLE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUMsQ0FBQztnQkFDbEQsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7b0JBQ3hCLE9BQU8sS0FBSyxDQUFDO2dCQUNmLENBQUM7WUFDSCxDQUFDO1lBQ0QsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQzthQUFNLENBQUM7WUFDTixPQUFPLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQ3BELENBQUM7SUFDSCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLHVCQUF1QixDQUNsQyxpQkFBNEM7UUFFNUMsTUFBTSxLQUFLLEdBQUcsTUFBTSxJQUFJLENBQUMsaUJBQWlCLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUM5RCxJQUFJLEtBQUssS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUN4QixNQUFNLElBQUksS0FBSyxDQUFDLFdBQVcsSUFBSSxDQUFDLGtCQUFrQixDQUFDLGlCQUFpQixDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQ3ZGLENBQUM7UUFDRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRDs7OztPQUlHO0lBQ0kscUJBQXFCLENBQUMsaUJBQW9DO1FBQy9ELElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLENBQUM7WUFDckMsS0FBSyxNQUFNLFVBQVUsSUFBSSxpQkFBaUIsRUFBRSxDQUFDO2dCQUMzQyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsVUFBVSxDQUFDLENBQUM7Z0JBQ2hELElBQUksS0FBSyxLQUFLLFNBQVMsRUFBRSxDQUFDO29CQUN4QixPQUFPLEtBQUssQ0FBQztnQkFDZixDQUFDO1lBQ0gsQ0FBQztZQUNELE9BQU8sU0FBUyxDQUFDO1FBQ25CLENBQUM7YUFBTSxDQUFDO1lBQ04sT0FBTyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUNsRCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMseUJBQXlCLENBQ3BDLGlCQUFvQztRQUVwQyxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQ2pFLElBQUksUUFBUSxJQUFJLFFBQVEsQ0FBQyxVQUFVLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQztZQUNyRCxNQUFNLFVBQVUsR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3RELE9BQU8sSUFBSSxDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUN2QyxDQUFDO1FBQ0QsT0FBTyxRQUFRLENBQUM7SUFDbEIsQ0FBQztJQUVPLEtBQUssQ0FBQyxZQUFZLENBQUMsWUFBd0I7UUFDakQsSUFBSSxPQUFPLFlBQVksS0FBSyxVQUFVLEVBQUUsQ0FBQztZQUN2QyxPQUFPLE1BQU0sWUFBWSxFQUFFLENBQUM7UUFDOUIsQ0FBQztRQUVELDRGQUE0RjtRQUM1RixPQUFPLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxZQUFZLENBQUMsQ0FBQztJQUM3QyxDQUFDO0lBRU8sZ0JBQWdCLENBQUMsVUFBa0I7UUFDekMsOEZBQThGO1FBQzlGLDhGQUE4RjtRQUM5RixnREFBZ0Q7UUFDaEQsTUFBTSxPQUFPLEdBQW9DO1lBQy9DLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQywwQkFBMEIsQ0FBQyxVQUFVLENBQUM7WUFDakQsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLHdCQUF3QixDQUFDLFVBQVUsQ0FBQztZQUMvQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsVUFBVSxDQUFDO1lBQzFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxVQUFVLENBQUM7U0FDL0MsQ0FBQztRQUVGLEtBQUssTUFBTSxVQUFVLElBQUksT0FBTyxFQUFFLENBQUM7WUFDakMsTUFBTSxLQUFLLEdBQUcsVUFBVSxFQUFFLENBQUM7WUFDM0IsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7Z0JBQ3hCLE9BQU8sS0FBSyxDQUFDO1lBQ2YsQ0FBQztRQUNILENBQUM7UUFFRCxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBRUQsbUdBQW1HO0lBQzNGLGtCQUFrQixDQUFDLGlCQUE0QztRQUNyRSxNQUFNLFVBQVUsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDOUYsT0FBTyxVQUFVO2FBQ2QsR0FBRyxDQUFDLENBQUMsU0FBUyxFQUFFLEVBQUUsQ0FDakIsT0FBTyxTQUFTLEtBQUssVUFBVTtZQUM3QixDQUFDLENBQUMsR0FBRyxTQUFTLENBQUMsSUFBSSxJQUFJLFdBQVcsSUFBSTtZQUN0QyxDQUFDLENBQUMsU0FBUyxDQUNkO2FBQ0EsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2hCLENBQUM7SUFFTywwQkFBMEIsQ0FBQyxVQUFrQjtRQUNuRCxPQUFPLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDakMsQ0FBQztJQUVPLHdCQUF3QixDQUFDLFVBQWtCO1FBQ2pELElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxFQUFFLENBQUM7WUFDL0MsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztRQUNELElBQUksQ0FBQztZQUNILE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsQ0FBQztZQUNsRSxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7WUFDbEMsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7Z0JBQ3hCLE9BQU8sU0FBUyxDQUFDO1lBQ25CLENBQUM7WUFDRCxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLEtBQUssSUFBSSxFQUFFLENBQUM7Z0JBQ2hELE9BQU8sZUFBZSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDcEQsQ0FBQztZQUNELE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3ZCLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztJQUNILENBQUM7SUFFRDs7O09BR0c7SUFDTyw2QkFBNkI7UUFDckMsT0FBTyxjQUFjLENBQUM7SUFDeEIsQ0FBQztJQUVPLG1CQUFtQixDQUFDLFVBQWtCO1FBQzVDLE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyw2QkFBNkIsRUFBRSxFQUFFLFVBQVUsQ0FBQyxDQUFDO1FBQ3ZGLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1lBQ2hDLE9BQU8sT0FBTyxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3JELENBQUM7UUFDRCxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBRU8sdUJBQXVCLENBQUMsVUFBa0I7UUFDaEQsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLENBQUMsNkJBQTZCLEVBQUUsQ0FBQztRQUNsRSxJQUFJLElBQUksQ0FBQyxlQUFlLENBQUMsb0JBQW9CLENBQUMsRUFBRSxDQUFDO1lBQy9DLE1BQU0sZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLEVBQUUsQ0FBQyxXQUFXLENBQUMsb0JBQW9CLENBQUMsQ0FBQztZQUN0RSxLQUFLLE1BQU0sTUFBTSxJQUFJLGdCQUFnQixFQUFFLENBQUM7Z0JBQ3RDLElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDO29CQUNuQyxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQzFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLG9CQUFvQixFQUFFLE1BQU0sQ0FBQyxDQUNoRCxDQUFDO29CQUNGLE1BQU0sS0FBSyxHQUFHLFlBQVksQ0FBQyxVQUFVLENBQUMsQ0FBQztvQkFDdkMsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7d0JBQ3hCLFNBQVM7b0JBQ1gsQ0FBQztvQkFDRCxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLEtBQUssSUFBSSxFQUFFLENBQUM7d0JBQ2hELE9BQU8sZUFBZSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUM7b0JBQ3BELENBQUM7b0JBQ0QsT0FBTyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQ3ZCLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUNELE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFFTyxZQUFZLENBQUMsSUFBYTtRQUNoQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3hDLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDcEQsQ0FBQztJQUVPLFlBQVksQ0FBQyxhQUFxQjtRQUN4QyxNQUFNLGFBQWEsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLGFBQWEsRUFBRSxRQUFRLENBQUMsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDN0UsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ25DLENBQUM7SUFFTyxVQUFVLENBQUMsUUFBNEI7UUFDN0MsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ2QsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBQ0QsT0FBTyxPQUFPLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBRU8sZUFBZSxDQUFDLGFBQXFCO1FBQzNDLElBQUksQ0FBQztZQUNILE9BQU8sT0FBTyxDQUFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsYUFBYSxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDMUQsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztJQUNILENBQUM7SUFFTyxrQkFBa0IsQ0FBQyxRQUFnQjtRQUN6QyxNQUFNLFVBQVUsR0FBRyxPQUFPLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDN0QsTUFBTSxZQUFZLEdBQVksUUFBUSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7WUFDdEQsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDO1lBQ3hCLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNuQyxPQUFPLE9BQU8sWUFBWSxLQUFLLFFBQVEsSUFBSSxZQUFZLEtBQUssSUFBSTtZQUM5RCxDQUFDLENBQUUsWUFBa0M7WUFDckMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUNULENBQUM7Q0FDRiJ9
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
export { fs, path };
|
|
4
|
-
import * as typedrequest from '@api.global/typedrequest';
|
|
5
|
-
export { typedrequest, };
|
|
6
4
|
import * as smartlog from '@push.rocks/smartlog';
|
|
7
5
|
export { smartlog };
|
|
8
|
-
import * as configvaultInterfaces from '@configvault.io/interfaces';
|
|
9
|
-
export { configvaultInterfaces };
|
|
10
6
|
import * as yaml from 'yaml';
|
|
11
7
|
export { yaml };
|
package/dist_ts/qenv.plugins.js
CHANGED
|
@@ -2,16 +2,10 @@
|
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
export { fs, path };
|
|
5
|
-
// @api.global scope
|
|
6
|
-
import * as typedrequest from '@api.global/typedrequest';
|
|
7
|
-
export { typedrequest, };
|
|
8
5
|
// @pushrocks scope
|
|
9
6
|
import * as smartlog from '@push.rocks/smartlog';
|
|
10
7
|
export { smartlog };
|
|
11
|
-
// @configvault.io scope
|
|
12
|
-
import * as configvaultInterfaces from '@configvault.io/interfaces';
|
|
13
|
-
export { configvaultInterfaces };
|
|
14
8
|
// third party scope
|
|
15
9
|
import * as yaml from 'yaml';
|
|
16
10
|
export { yaml };
|
|
17
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWVudi5wbHVnaW5zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvcWVudi5wbHVnaW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFNBQVM7QUFDVCxPQUFPLEtBQUssRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUM5QixPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUU3QixPQUFPLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxDQUFDO0FBRXBCLG1CQUFtQjtBQUNuQixPQUFPLEtBQUssUUFBUSxNQUFNLHNCQUFzQixDQUFDO0FBRWpELE9BQU8sRUFBRSxRQUFRLEVBQUUsQ0FBQztBQUVwQixvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFFN0IsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDIn0=
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/qenv",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A module for easily handling environment variables in Node.js projects with support for .yml and .json configuration.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -22,18 +22,16 @@
|
|
|
22
22
|
"author": "Task Venture Capital GmbH <hello@task.vc>",
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"bugs": {
|
|
25
|
-
"url": "https://
|
|
25
|
+
"url": "https://code.foss.global/push.rocks/qenv/issues"
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://code.foss.global/push.rocks/qenv",
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@git.zone/tsbuild": "^4.
|
|
29
|
+
"@git.zone/tsbuild": "^4.5.0",
|
|
30
30
|
"@git.zone/tsrun": "^2.0.6",
|
|
31
|
-
"@git.zone/tstest": "^
|
|
32
|
-
"@types/node": "^26.
|
|
31
|
+
"@git.zone/tstest": "^6.1.1",
|
|
32
|
+
"@types/node": "^26.5.1"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@api.global/typedrequest": "^8.0.3",
|
|
36
|
-
"@configvault.io/interfaces": "^1.0.17",
|
|
37
35
|
"@push.rocks/smartlog": "^3.2.2",
|
|
38
36
|
"@push.rocks/smartpath": "^6.0.0",
|
|
39
37
|
"yaml": "^2.8.3"
|
|
@@ -56,7 +54,7 @@
|
|
|
56
54
|
"last 1 chrome versions"
|
|
57
55
|
],
|
|
58
56
|
"scripts": {
|
|
59
|
-
"test": "(tstest test/ --verbose --
|
|
57
|
+
"test": "(tstest test/ --verbose --logfile --timeout 20)",
|
|
60
58
|
"build": "tsbuild --web",
|
|
61
59
|
"format": "gitzone format",
|
|
62
60
|
"buildDocs": "tsdoc"
|
package/readme.md
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
✅ **Flexible Formats** - Supports `.yml`, `.yaml`, and `.json` configuration files
|
|
11
11
|
✅ **Docker Ready** - Built-in support for Docker secrets and secret.json files
|
|
12
12
|
✅ **Async & Sync** - Both synchronous and asynchronous variable retrieval
|
|
13
|
-
✅ **
|
|
13
|
+
✅ **Enforced Requirements** - `required:` in `qenv.yml` is checked while constructing, with a typed error
|
|
14
|
+
✅ **Strict Mode** - Optional strict getter that throws for a missing variable
|
|
14
15
|
✅ **Base64 Objects** - Handle complex configuration objects with automatic encoding/decoding
|
|
15
16
|
✅ **Dynamic Resolution** - Support for async functions as environment variable sources
|
|
16
17
|
|
|
@@ -32,7 +33,8 @@ yarn add @push.rocks/qenv
|
|
|
32
33
|
```typescript
|
|
33
34
|
import { Qenv } from '@push.rocks/qenv';
|
|
34
35
|
|
|
35
|
-
// Create a new Qenv instance
|
|
36
|
+
// Create a new Qenv instance. Every name listed under `required:` in qenv.yml is resolved right
|
|
37
|
+
// here, and a missing one throws a QenvMissingRequiredEnvVarsError.
|
|
36
38
|
const qenv = new Qenv('./', './', true);
|
|
37
39
|
|
|
38
40
|
// Access environment variables
|
|
@@ -61,6 +63,10 @@ required:
|
|
|
61
63
|
- LOG_LEVEL
|
|
62
64
|
```
|
|
63
65
|
|
|
66
|
+
Every listed name is resolved while the `Qenv` instance is constructed. What is found lands in
|
|
67
|
+
`availableEnvVars` and, as a string, in `keyValueObject`; what is missing lands in `missingEnvVars`
|
|
68
|
+
and, unless you pass `failOnMissing: false`, makes the constructor throw.
|
|
69
|
+
|
|
64
70
|
#### 2. Provide Values (`env.yml` or `env.json`)
|
|
65
71
|
|
|
66
72
|
For local development, create an `env.yml` or `env.json` file:
|
|
@@ -97,6 +103,10 @@ Qenv loads variables in this order (first found wins):
|
|
|
97
103
|
3. **Docker secrets** - From `/run/secrets/`
|
|
98
104
|
4. **Docker secret JSON** - From `/run/secrets/secret.json`
|
|
99
105
|
|
|
106
|
+
All four sources are synchronous, so `getEnvVarOnDemand` and `getEnvVarOnDemandSync` resolve a name
|
|
107
|
+
identically. A `required:` entry is always a name; an async resolver function is a per-call source
|
|
108
|
+
and never takes part in the requirement check.
|
|
109
|
+
|
|
100
110
|
### Handling Complex Objects
|
|
101
111
|
|
|
102
112
|
Store and retrieve complex configuration objects:
|
|
@@ -134,6 +144,9 @@ const fetchFromVault = async () => {
|
|
|
134
144
|
const secret = await qenv.getEnvVarOnDemand(fetchFromVault);
|
|
135
145
|
```
|
|
136
146
|
|
|
147
|
+
The function form of `TEnvVarRef` is honoured by `getEnvVarOnDemand` and `getEnvVarOnDemandStrict`
|
|
148
|
+
only. `getEnvVarOnDemandSync` cannot await, so it accepts names and takes no resolver function.
|
|
149
|
+
|
|
137
150
|
### Working with Docker
|
|
138
151
|
|
|
139
152
|
Qenv seamlessly integrates with Docker secrets:
|
|
@@ -168,9 +181,18 @@ const dbPassword = await qenv.getEnvVarOnDemand('db_password');
|
|
|
168
181
|
Control how your application handles missing environment variables:
|
|
169
182
|
|
|
170
183
|
```typescript
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
//
|
|
184
|
+
import { Qenv, QenvMissingRequiredEnvVarsError } from '@push.rocks/qenv';
|
|
185
|
+
|
|
186
|
+
// Fail fast (default behaviour): the constructor throws
|
|
187
|
+
try {
|
|
188
|
+
const qenvStrict = new Qenv('./', './', true);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (error instanceof QenvMissingRequiredEnvVarsError) {
|
|
191
|
+
console.error('Missing variables:', error.missingEnvVars);
|
|
192
|
+
console.error('Declared in:', error.qenvFilePathAbsolute);
|
|
193
|
+
}
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
174
196
|
|
|
175
197
|
// Graceful handling
|
|
176
198
|
const qenvRelaxed = new Qenv('./', './', false);
|
|
@@ -183,6 +205,16 @@ if (qenvRelaxed.missingEnvVars.length > 0) {
|
|
|
183
205
|
}
|
|
184
206
|
```
|
|
185
207
|
|
|
208
|
+
qenv never ends the process: the caller decides what an incomplete environment means. Where two
|
|
209
|
+
copies of qenv can end up in one dependency tree, `instanceof` is unreliable - match on the code
|
|
210
|
+
instead:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
if (error instanceof Error && 'code' in error && error.code === 'QENV_MISSING_REQUIRED_ENV_VARS') {
|
|
214
|
+
// handle the incomplete environment
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
186
218
|
### Strict Mode for Critical Variables
|
|
187
219
|
|
|
188
220
|
Use the new strict getter when you absolutely need a variable:
|
|
@@ -191,15 +223,19 @@ Use the new strict getter when you absolutely need a variable:
|
|
|
191
223
|
try {
|
|
192
224
|
// This will throw if TOKEN is not set
|
|
193
225
|
const token = await qenv.getEnvVarOnDemandStrict('TOKEN');
|
|
194
|
-
|
|
226
|
+
|
|
195
227
|
// You can also check multiple fallback names
|
|
196
228
|
const db = await qenv.getEnvVarOnDemandStrict(['DATABASE_URL', 'DB_CONNECTION']);
|
|
197
229
|
} catch (error) {
|
|
230
|
+
// the message names every reference that could not be resolved
|
|
198
231
|
console.error('Critical configuration missing:', error.message);
|
|
199
|
-
|
|
232
|
+
throw error;
|
|
200
233
|
}
|
|
201
234
|
```
|
|
202
235
|
|
|
236
|
+
A name that `qenv.yml` already lists under `required:` is resolved at construction time, so the
|
|
237
|
+
strict getter is for variables you look up on demand.
|
|
238
|
+
|
|
203
239
|
## 🏗️ CI/CD Integration
|
|
204
240
|
|
|
205
241
|
### GitHub Actions
|
|
@@ -276,12 +312,12 @@ qenv.logger.log('info', 'Custom log message');
|
|
|
276
312
|
Here's how you might use qenv in a production Node.js application:
|
|
277
313
|
|
|
278
314
|
```typescript
|
|
279
|
-
import { Qenv } from '@push.rocks/qenv';
|
|
315
|
+
import { Qenv, QenvMissingRequiredEnvVarsError } from '@push.rocks/qenv';
|
|
280
316
|
import { createServer } from './server';
|
|
281
317
|
import { connectDatabase } from './database';
|
|
282
318
|
|
|
283
319
|
async function bootstrap() {
|
|
284
|
-
// Initialize environment
|
|
320
|
+
// Initialize environment: throws when qenv.yml requires something no source provides
|
|
285
321
|
const qenv = new Qenv();
|
|
286
322
|
|
|
287
323
|
// Load critical configuration
|
|
@@ -304,7 +340,12 @@ async function bootstrap() {
|
|
|
304
340
|
}
|
|
305
341
|
|
|
306
342
|
bootstrap().catch(error => {
|
|
307
|
-
|
|
343
|
+
if (error instanceof QenvMissingRequiredEnvVarsError) {
|
|
344
|
+
console.error('Incomplete environment, missing:', error.missingEnvVars.join(', '));
|
|
345
|
+
} else {
|
|
346
|
+
console.error('Failed to start application:', error);
|
|
347
|
+
}
|
|
348
|
+
// exiting is the application's decision - qenv itself never calls process.exit
|
|
308
349
|
process.exit(1);
|
|
309
350
|
});
|
|
310
351
|
```
|
|
@@ -318,18 +359,21 @@ bootstrap().catch(error => {
|
|
|
318
359
|
new Qenv(
|
|
319
360
|
qenvFileBasePathArg?: string, // Path to qenv.yml (default: process.cwd())
|
|
320
361
|
envFileBasePathArg?: string, // Path to env.yml/json (default: same as qenv)
|
|
321
|
-
failOnMissing?: boolean //
|
|
362
|
+
failOnMissing?: boolean // Throw on missing required vars (default: true)
|
|
322
363
|
)
|
|
323
364
|
```
|
|
324
365
|
|
|
366
|
+
Throws `QenvMissingRequiredEnvVarsError` when `failOnMissing` is true and a name listed under
|
|
367
|
+
`required:` is not provided by any source.
|
|
368
|
+
|
|
325
369
|
#### Methods
|
|
326
370
|
|
|
327
371
|
| Method | Description | Returns |
|
|
328
372
|
|--------|-------------|---------|
|
|
329
|
-
| `getEnvVarOnDemand(name)` | Get environment variable value | `Promise<string \| undefined>` |
|
|
373
|
+
| `getEnvVarOnDemand(name)` | Get environment variable value, resolver functions included | `Promise<string \| undefined>` |
|
|
330
374
|
| `getEnvVarOnDemandStrict(name)` | Get variable or throw error | `Promise<string>` |
|
|
331
|
-
| `getEnvVarOnDemandSync(name)` | Synchronously get variable | `string \| undefined` |
|
|
332
|
-
| `getEnvVarOnDemandAsObject(name)` | Get variable as decoded object | `Promise<
|
|
375
|
+
| `getEnvVarOnDemandSync(name)` | Synchronously get variable, names only | `string \| undefined` |
|
|
376
|
+
| `getEnvVarOnDemandAsObject(name)` | Get variable as decoded object | `Promise<unknown>` |
|
|
333
377
|
|
|
334
378
|
#### Properties
|
|
335
379
|
|
|
@@ -338,7 +382,24 @@ new Qenv(
|
|
|
338
382
|
| `requiredEnvVars` | `string[]` | List of required variable names |
|
|
339
383
|
| `availableEnvVars` | `string[]` | List of found variable names |
|
|
340
384
|
| `missingEnvVars` | `string[]` | List of missing variable names |
|
|
341
|
-
| `keyValueObject` | `
|
|
385
|
+
| `keyValueObject` | `Record<string, string>` | Every available required variable as a resolved string |
|
|
386
|
+
| `qenvFilePathAbsolute` | `string` | Absolute path of the qenv.yml in use |
|
|
387
|
+
| `envFilePathAbsolute` | `string \| undefined` | Absolute path of the env file in use |
|
|
388
|
+
|
|
389
|
+
### Class: `QenvMissingRequiredEnvVarsError`
|
|
390
|
+
|
|
391
|
+
Thrown by the constructor when `failOnMissing` is true and a required variable has no source.
|
|
392
|
+
|
|
393
|
+
| Member | Type | Description |
|
|
394
|
+
|--------|------|-------------|
|
|
395
|
+
| `code` | `'QENV_MISSING_REQUIRED_ENV_VARS'` | Stable identifier, safe across duplicate installs |
|
|
396
|
+
| `missingEnvVars` | `string[]` | The required names no source provided |
|
|
397
|
+
| `requiredEnvVars` | `string[]` | Every name listed under `required:` |
|
|
398
|
+
| `qenvFilePathAbsolute` | `string` | The qenv.yml that declared them |
|
|
399
|
+
|
|
400
|
+
## Issue Reporting and Security
|
|
401
|
+
|
|
402
|
+
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
|
|
342
403
|
|
|
343
404
|
## License and Legal Information
|
|
344
405
|
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/index.ts
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface IQenvMissingRequiredEnvVarsErrorOptions {
|
|
2
|
+
missingEnvVars: string[];
|
|
3
|
+
requiredEnvVars: string[];
|
|
4
|
+
qenvFilePathAbsolute: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Thrown when a variable listed under `required:` in qenv.yml is not provided by any source.
|
|
9
|
+
* The names are carried as data so a caller can report or branch on them without parsing the
|
|
10
|
+
* message, and `code` identifies the refusal even when two copies of qenv end up in one tree,
|
|
11
|
+
* which makes `instanceof` unreliable.
|
|
12
|
+
*/
|
|
13
|
+
export class QenvMissingRequiredEnvVarsError extends Error {
|
|
14
|
+
public readonly code = 'QENV_MISSING_REQUIRED_ENV_VARS';
|
|
15
|
+
public readonly missingEnvVars: string[];
|
|
16
|
+
public readonly requiredEnvVars: string[];
|
|
17
|
+
public readonly qenvFilePathAbsolute: string;
|
|
18
|
+
|
|
19
|
+
constructor(optionsArg: IQenvMissingRequiredEnvVarsErrorOptions) {
|
|
20
|
+
super(
|
|
21
|
+
`qenv is missing required environment variables: ${optionsArg.missingEnvVars.join(', ')}. ` +
|
|
22
|
+
`They are listed under "required:" in ${optionsArg.qenvFilePathAbsolute} and were found ` +
|
|
23
|
+
`neither in the process environment, nor in the env file, nor in the Docker secrets.`
|
|
24
|
+
);
|
|
25
|
+
this.name = 'QenvMissingRequiredEnvVarsError';
|
|
26
|
+
this.missingEnvVars = [...optionsArg.missingEnvVars];
|
|
27
|
+
this.requiredEnvVars = [...optionsArg.requiredEnvVars];
|
|
28
|
+
this.qenvFilePathAbsolute = optionsArg.qenvFilePathAbsolute;
|
|
29
|
+
}
|
|
30
|
+
}
|
package/ts/qenv.classes.qenv.ts
CHANGED
|
@@ -1,27 +1,50 @@
|
|
|
1
|
-
import { CloudlyAdapter } from './qenv.classes.configvaultadapter.js';
|
|
2
1
|
import * as plugins from './qenv.plugins.js';
|
|
2
|
+
import { QenvMissingRequiredEnvVarsError } from './qenv.classes.missingrequiredenvvarserror.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* a reference to an environment variable: its name, or an async function that produces the value.
|
|
6
|
+
* The function form is only honoured by the async getters; the synchronous getter accepts names.
|
|
7
|
+
*/
|
|
4
8
|
export type TEnvVarRef = string | (() => Promise<string>);
|
|
5
|
-
|
|
9
|
+
|
|
10
|
+
/** the resolved value of every available required env var, always a string */
|
|
11
|
+
export type TEnvVarValueMap = Record<string, string>;
|
|
12
|
+
|
|
13
|
+
/** a parsed qenv.yml, env file or secret.json, whose values are whatever the file declared */
|
|
14
|
+
type TParsedFileObject = Record<string, unknown>;
|
|
6
15
|
|
|
7
16
|
export class Qenv {
|
|
17
|
+
/** the names listed under `required:` in qenv.yml */
|
|
8
18
|
public requiredEnvVars: string[] = [];
|
|
19
|
+
|
|
20
|
+
/** the required names that a source provided */
|
|
9
21
|
public availableEnvVars: string[] = [];
|
|
22
|
+
|
|
23
|
+
/** the required names that no source provided */
|
|
10
24
|
public missingEnvVars: string[] = [];
|
|
11
|
-
public keyValueObject: TKeyValueObject = {};
|
|
12
|
-
public logger = new plugins.smartlog.ConsoleLog();
|
|
13
25
|
|
|
14
|
-
|
|
26
|
+
/** the resolved value of every available required name */
|
|
27
|
+
public keyValueObject: TEnvVarValueMap = {};
|
|
28
|
+
|
|
29
|
+
public logger = new plugins.smartlog.ConsoleLog();
|
|
15
30
|
|
|
16
31
|
public qenvFilePathAbsolute = '';
|
|
17
32
|
public envFilePathAbsolute?: string;
|
|
18
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Resolves every name listed under `required:` in qenv.yml while constructing.
|
|
36
|
+
* @param qenvFileBasePathArg directory that holds qenv.yml
|
|
37
|
+
* @param envFileBasePathArg directory that holds env.json, env.yml or env.yaml
|
|
38
|
+
* @param failOnMissing throws a QenvMissingRequiredEnvVarsError when a variable listed under
|
|
39
|
+
* `required:` in qenv.yml is not provided by any source. Pass false to only record the names in
|
|
40
|
+
* `missingEnvVars` and continue.
|
|
41
|
+
* @throws QenvMissingRequiredEnvVarsError
|
|
42
|
+
*/
|
|
19
43
|
constructor(
|
|
20
44
|
qenvFileBasePathArg: string = process.cwd(),
|
|
21
45
|
envFileBasePathArg?: string,
|
|
22
46
|
failOnMissing: boolean = true
|
|
23
47
|
) {
|
|
24
|
-
this.cloudlyAdapter = new CloudlyAdapter();
|
|
25
48
|
this.initializeFilePaths(qenvFileBasePathArg, envFileBasePathArg);
|
|
26
49
|
this.loadRequiredEnvVars();
|
|
27
50
|
this.loadAvailableEnvVars();
|
|
@@ -33,18 +56,18 @@ export class Qenv {
|
|
|
33
56
|
plugins.path.resolve(qenvFileBasePathArg),
|
|
34
57
|
'qenv.yml'
|
|
35
58
|
);
|
|
36
|
-
|
|
59
|
+
|
|
37
60
|
if (envFileBasePathArg) {
|
|
38
61
|
const envFileBasePath = plugins.path.resolve(envFileBasePathArg);
|
|
39
|
-
|
|
62
|
+
|
|
40
63
|
const envFileJsonPath = plugins.path.join(envFileBasePath, 'env.json');
|
|
41
64
|
const envFileYmlPath = plugins.path.join(envFileBasePath, 'env.yml');
|
|
42
65
|
const envFileYamlPath = plugins.path.join(envFileBasePath, 'env.yaml');
|
|
43
|
-
|
|
66
|
+
|
|
44
67
|
const envFileJsonExists = this.fileExists(envFileJsonPath);
|
|
45
68
|
const envFileYmlExists = this.fileExists(envFileYmlPath);
|
|
46
69
|
const envFileYamlExists = this.fileExists(envFileYamlPath);
|
|
47
|
-
|
|
70
|
+
|
|
48
71
|
if (envFileJsonExists && (envFileYmlExists || envFileYamlExists)) {
|
|
49
72
|
this.logger.log('warn', 'Both env.json and env.yml files exist! Using env.json');
|
|
50
73
|
this.envFilePathAbsolute = envFileJsonPath;
|
|
@@ -59,22 +82,28 @@ export class Qenv {
|
|
|
59
82
|
}
|
|
60
83
|
|
|
61
84
|
private loadRequiredEnvVars() {
|
|
62
|
-
if (this.fileExists(this.qenvFilePathAbsolute)) {
|
|
63
|
-
|
|
64
|
-
const requiredEnvVars = qenvFile.required;
|
|
65
|
-
if (Array.isArray(requiredEnvVars)) {
|
|
66
|
-
this.requiredEnvVars.push(
|
|
67
|
-
...requiredEnvVars.filter((envVar): envVar is string => typeof envVar === 'string')
|
|
68
|
-
);
|
|
69
|
-
} else {
|
|
70
|
-
this.logger.log('warn', 'qenv.yml does not contain a "required" Array!');
|
|
71
|
-
}
|
|
85
|
+
if (!this.fileExists(this.qenvFilePathAbsolute)) {
|
|
86
|
+
return;
|
|
72
87
|
}
|
|
88
|
+
const qenvFile = this.readObjectFromFile(this.qenvFilePathAbsolute);
|
|
89
|
+
const declaredRequirements = qenvFile['required'];
|
|
90
|
+
if (!Array.isArray(declaredRequirements)) {
|
|
91
|
+
this.logger.log('warn', 'qenv.yml does not contain a "required" Array!');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const declaredEntries: unknown[] = declaredRequirements;
|
|
95
|
+
this.requiredEnvVars.push(
|
|
96
|
+
...declaredEntries.filter((entry): entry is string => typeof entry === 'string')
|
|
97
|
+
);
|
|
73
98
|
}
|
|
74
99
|
|
|
75
100
|
private loadAvailableEnvVars() {
|
|
101
|
+
// resolved synchronously: a required env var is always a name, and every source a name can come
|
|
102
|
+
// from - process environment, env file, Docker secrets - is a synchronous read. Resolving it
|
|
103
|
+
// here as a promise would store a pending promise instead of a value and make the check below
|
|
104
|
+
// pass for every name.
|
|
76
105
|
for (const envVar of this.requiredEnvVars) {
|
|
77
|
-
const value = this.
|
|
106
|
+
const value = this.tryGetEnvVarSync(envVar);
|
|
78
107
|
if (value !== undefined) {
|
|
79
108
|
this.availableEnvVars.push(envVar);
|
|
80
109
|
this.keyValueObject[envVar] = value;
|
|
@@ -87,18 +116,30 @@ export class Qenv {
|
|
|
87
116
|
(envVar) => !this.availableEnvVars.includes(envVar)
|
|
88
117
|
);
|
|
89
118
|
|
|
90
|
-
if (this.missingEnvVars.length
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
this.
|
|
98
|
-
|
|
119
|
+
if (this.missingEnvVars.length === 0) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (failOnMissing) {
|
|
124
|
+
// a library must not end the process: the caller decides what an incomplete environment means
|
|
125
|
+
throw new QenvMissingRequiredEnvVarsError({
|
|
126
|
+
missingEnvVars: this.missingEnvVars,
|
|
127
|
+
requiredEnvVars: this.requiredEnvVars,
|
|
128
|
+
qenvFilePathAbsolute: this.qenvFilePathAbsolute,
|
|
129
|
+
});
|
|
99
130
|
}
|
|
131
|
+
|
|
132
|
+
this.logger.log(
|
|
133
|
+
'warn',
|
|
134
|
+
`qenv is not set to fail on missing environment variables. Missing: ${this.missingEnvVars.join(', ')}`
|
|
135
|
+
);
|
|
100
136
|
}
|
|
101
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Resolves an env var from the process environment, the env file and the Docker secrets, in that
|
|
140
|
+
* order. An array is tried left to right and the first defined value wins.
|
|
141
|
+
* @param envVarNameOrNames a name, an async resolver function, or a list of either
|
|
142
|
+
*/
|
|
102
143
|
public async getEnvVarOnDemand(
|
|
103
144
|
envVarNameOrNames: TEnvVarRef | TEnvVarRef[]
|
|
104
145
|
): Promise<string | undefined> {
|
|
@@ -116,23 +157,25 @@ export class Qenv {
|
|
|
116
157
|
}
|
|
117
158
|
|
|
118
159
|
/**
|
|
119
|
-
* Like getEnvVarOnDemand, but throws
|
|
120
|
-
* @param envVarNameOrNames
|
|
121
|
-
* @returns
|
|
160
|
+
* Like getEnvVarOnDemand, but throws when no source provides a value.
|
|
161
|
+
* @param envVarNameOrNames a name, an async resolver function, or a list of either
|
|
122
162
|
*/
|
|
123
163
|
public async getEnvVarOnDemandStrict(
|
|
124
164
|
envVarNameOrNames: TEnvVarRef | TEnvVarRef[]
|
|
125
165
|
): Promise<string> {
|
|
126
166
|
const value = await this.getEnvVarOnDemand(envVarNameOrNames);
|
|
127
167
|
if (value === undefined) {
|
|
128
|
-
throw new Error(`Env var ${envVarNameOrNames} is not set!`);
|
|
168
|
+
throw new Error(`Env var ${this.describeEnvVarRefs(envVarNameOrNames)} is not set!`);
|
|
129
169
|
}
|
|
130
170
|
return value;
|
|
131
171
|
}
|
|
132
172
|
|
|
173
|
+
/**
|
|
174
|
+
* The synchronous counterpart of getEnvVarOnDemand. It resolves names only: an async resolver
|
|
175
|
+
* function cannot be awaited here, so the function form of TEnvVarRef is not accepted.
|
|
176
|
+
* @param envVarNameOrNames a name or a list of names
|
|
177
|
+
*/
|
|
133
178
|
public getEnvVarOnDemandSync(envVarNameOrNames: string | string[]): string | undefined {
|
|
134
|
-
console.warn('requesting env var sync leaves out potentially important async env sources.');
|
|
135
|
-
|
|
136
179
|
if (Array.isArray(envVarNameOrNames)) {
|
|
137
180
|
for (const envVarName of envVarNameOrNames) {
|
|
138
181
|
const value = this.tryGetEnvVarSync(envVarName);
|
|
@@ -146,7 +189,14 @@ export class Qenv {
|
|
|
146
189
|
}
|
|
147
190
|
}
|
|
148
191
|
|
|
149
|
-
|
|
192
|
+
/**
|
|
193
|
+
* Resolves an env var whose value was stored as a base64 encoded object and decodes it. A plain
|
|
194
|
+
* value is returned as the string it is, so the caller narrows what it gets.
|
|
195
|
+
* @param envVarNameOrNames a name or a list of names
|
|
196
|
+
*/
|
|
197
|
+
public async getEnvVarOnDemandAsObject(
|
|
198
|
+
envVarNameOrNames: string | string[]
|
|
199
|
+
): Promise<unknown> {
|
|
150
200
|
const rawValue = await this.getEnvVarOnDemand(envVarNameOrNames);
|
|
151
201
|
if (rawValue && rawValue.startsWith('base64Object:')) {
|
|
152
202
|
const base64Part = rawValue.split('base64Object:')[1];
|
|
@@ -160,39 +210,43 @@ export class Qenv {
|
|
|
160
210
|
return await envVarRefArg();
|
|
161
211
|
}
|
|
162
212
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
this.getFromEnvYamlOrJsonFile(envVarRefArg),
|
|
166
|
-
this.getFromDockerSecret(envVarRefArg),
|
|
167
|
-
this.getFromDockerSecretJson(envVarRefArg)
|
|
168
|
-
];
|
|
169
|
-
|
|
170
|
-
for (const value of sources) {
|
|
171
|
-
if (value !== undefined) {
|
|
172
|
-
return value;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return undefined;
|
|
213
|
+
// a name resolves from synchronous sources only, so both getters share one resolution order
|
|
214
|
+
return this.tryGetEnvVarSync(envVarRefArg);
|
|
177
215
|
}
|
|
178
216
|
|
|
179
217
|
private tryGetEnvVarSync(envVarName: string): string | undefined {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
this.
|
|
218
|
+
// read lazily: a source is only touched once every earlier one came back undefined, so a name
|
|
219
|
+
// the process environment answers never opens a secret file, and a malformed secret.json only
|
|
220
|
+
// ever affects the names that actually reach it
|
|
221
|
+
const sources: Array<() => string | undefined> = [
|
|
222
|
+
() => this.getFromEnvironmentVariable(envVarName),
|
|
223
|
+
() => this.getFromEnvYamlOrJsonFile(envVarName),
|
|
224
|
+
() => this.getFromDockerSecret(envVarName),
|
|
225
|
+
() => this.getFromDockerSecretJson(envVarName),
|
|
185
226
|
];
|
|
186
|
-
|
|
187
|
-
for (const
|
|
227
|
+
|
|
228
|
+
for (const readSource of sources) {
|
|
229
|
+
const value = readSource();
|
|
188
230
|
if (value !== undefined) {
|
|
189
231
|
return value;
|
|
190
232
|
}
|
|
191
233
|
}
|
|
192
|
-
|
|
234
|
+
|
|
193
235
|
return undefined;
|
|
194
236
|
}
|
|
195
237
|
|
|
238
|
+
/** renders env var references for an error message, naming a resolver function where it has one */
|
|
239
|
+
private describeEnvVarRefs(envVarNameOrNames: TEnvVarRef | TEnvVarRef[]): string {
|
|
240
|
+
const envVarRefs = Array.isArray(envVarNameOrNames) ? envVarNameOrNames : [envVarNameOrNames];
|
|
241
|
+
return envVarRefs
|
|
242
|
+
.map((envVarRef) =>
|
|
243
|
+
typeof envVarRef === 'function'
|
|
244
|
+
? `${envVarRef.name || 'anonymous'}()`
|
|
245
|
+
: envVarRef
|
|
246
|
+
)
|
|
247
|
+
.join(', ');
|
|
248
|
+
}
|
|
249
|
+
|
|
196
250
|
private getFromEnvironmentVariable(envVarName: string): string | undefined {
|
|
197
251
|
return process.env[envVarName];
|
|
198
252
|
}
|
|
@@ -216,8 +270,16 @@ export class Qenv {
|
|
|
216
270
|
}
|
|
217
271
|
}
|
|
218
272
|
|
|
273
|
+
/**
|
|
274
|
+
* the directory Docker mounts secrets into. It is a method so a test can point both secret
|
|
275
|
+
* readers at a directory it is allowed to create; a process can never write /run/secrets itself.
|
|
276
|
+
*/
|
|
277
|
+
protected getDockerSecretsDirectoryPath(): string {
|
|
278
|
+
return '/run/secrets';
|
|
279
|
+
}
|
|
280
|
+
|
|
219
281
|
private getFromDockerSecret(envVarName: string): string | undefined {
|
|
220
|
-
const secretPath =
|
|
282
|
+
const secretPath = plugins.path.join(this.getDockerSecretsDirectoryPath(), envVarName);
|
|
221
283
|
if (this.fileExists(secretPath)) {
|
|
222
284
|
return plugins.fs.readFileSync(secretPath, 'utf8');
|
|
223
285
|
}
|
|
@@ -225,11 +287,14 @@ export class Qenv {
|
|
|
225
287
|
}
|
|
226
288
|
|
|
227
289
|
private getFromDockerSecretJson(envVarName: string): string | undefined {
|
|
228
|
-
|
|
229
|
-
|
|
290
|
+
const secretsDirectoryPath = this.getDockerSecretsDirectoryPath();
|
|
291
|
+
if (this.directoryExists(secretsDirectoryPath)) {
|
|
292
|
+
const availableSecrets = plugins.fs.readdirSync(secretsDirectoryPath);
|
|
230
293
|
for (const secret of availableSecrets) {
|
|
231
294
|
if (secret.includes('secret.json')) {
|
|
232
|
-
const secretObject = this.readObjectFromFile(
|
|
295
|
+
const secretObject = this.readObjectFromFile(
|
|
296
|
+
plugins.path.join(secretsDirectoryPath, secret)
|
|
297
|
+
);
|
|
233
298
|
const value = secretObject[envVarName];
|
|
234
299
|
if (value === undefined) {
|
|
235
300
|
continue;
|
|
@@ -244,12 +309,12 @@ export class Qenv {
|
|
|
244
309
|
return undefined;
|
|
245
310
|
}
|
|
246
311
|
|
|
247
|
-
private encodeBase64(data:
|
|
312
|
+
private encodeBase64(data: unknown): string {
|
|
248
313
|
const jsonString = JSON.stringify(data);
|
|
249
314
|
return Buffer.from(jsonString).toString('base64');
|
|
250
315
|
}
|
|
251
316
|
|
|
252
|
-
private decodeBase64(encodedString: string):
|
|
317
|
+
private decodeBase64(encodedString: string): unknown {
|
|
253
318
|
const decodedString = Buffer.from(encodedString, 'base64').toString('utf-8');
|
|
254
319
|
return JSON.parse(decodedString);
|
|
255
320
|
}
|
|
@@ -269,11 +334,13 @@ export class Qenv {
|
|
|
269
334
|
}
|
|
270
335
|
}
|
|
271
336
|
|
|
272
|
-
private readObjectFromFile(filePath: string):
|
|
337
|
+
private readObjectFromFile(filePath: string): TParsedFileObject {
|
|
273
338
|
const fileString = plugins.fs.readFileSync(filePath, 'utf8');
|
|
274
|
-
const parsedObject = filePath.endsWith('.json')
|
|
339
|
+
const parsedObject: unknown = filePath.endsWith('.json')
|
|
275
340
|
? JSON.parse(fileString)
|
|
276
341
|
: plugins.yaml.parse(fileString);
|
|
277
|
-
return typeof parsedObject === 'object' && parsedObject !== null
|
|
342
|
+
return typeof parsedObject === 'object' && parsedObject !== null
|
|
343
|
+
? (parsedObject as TParsedFileObject)
|
|
344
|
+
: {};
|
|
278
345
|
}
|
|
279
346
|
}
|
package/ts/qenv.plugins.ts
CHANGED
|
@@ -4,23 +4,11 @@ import * as path from 'path';
|
|
|
4
4
|
|
|
5
5
|
export { fs, path };
|
|
6
6
|
|
|
7
|
-
// @api.global scope
|
|
8
|
-
import * as typedrequest from '@api.global/typedrequest';
|
|
9
|
-
|
|
10
|
-
export {
|
|
11
|
-
typedrequest,
|
|
12
|
-
}
|
|
13
|
-
|
|
14
7
|
// @pushrocks scope
|
|
15
8
|
import * as smartlog from '@push.rocks/smartlog';
|
|
16
9
|
|
|
17
10
|
export { smartlog };
|
|
18
11
|
|
|
19
|
-
// @configvault.io scope
|
|
20
|
-
import * as configvaultInterfaces from '@configvault.io/interfaces';
|
|
21
|
-
|
|
22
|
-
export { configvaultInterfaces };
|
|
23
|
-
|
|
24
12
|
// third party scope
|
|
25
13
|
import * as yaml from 'yaml';
|
|
26
14
|
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import * as plugins from './qenv.plugins.js';
|
|
2
|
-
export class CloudlyAdapter {
|
|
3
|
-
constructor(configVaultUrl) {
|
|
4
|
-
this.configVaultUrl = configVaultUrl;
|
|
5
|
-
}
|
|
6
|
-
async getConfigBundle() {
|
|
7
|
-
if (this.configVaultUrl) {
|
|
8
|
-
console.log(`ConfigVault specified through constructor`);
|
|
9
|
-
}
|
|
10
|
-
else if (process.env['CONFIGVAULT_URL']) {
|
|
11
|
-
this.configVaultUrl = process.env['CONFIGVAULT_URL'];
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
const parsedUrl = new URL(this.configVaultUrl);
|
|
17
|
-
const tr = new plugins.typedrequest.TypedRequest(`${parsedUrl.host}/typedrequest`, 'getEnvBundle');
|
|
18
|
-
const response = await tr.fire({
|
|
19
|
-
authorization: parsedUrl.pathname.replace('/', ''),
|
|
20
|
-
});
|
|
21
|
-
return response.envBundle;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWVudi5jbGFzc2VzLmNvbmZpZ3ZhdWx0YWRhcHRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3FlbnYuY2xhc3Nlcy5jb25maWd2YXVsdGFkYXB0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxtQkFBbUIsQ0FBQztBQUU3QyxNQUFNLE9BQU8sY0FBYztJQUd6QixZQUFZLGNBQXVCO1FBQ2pDLElBQUksQ0FBQyxjQUFjLEdBQUcsY0FBYyxDQUFDO0lBQ3ZDLENBQUM7SUFFTSxLQUFLLENBQUMsZUFBZTtRQUMxQixJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN4QixPQUFPLENBQUMsR0FBRyxDQUFDLDJDQUEyQyxDQUFDLENBQUE7UUFDMUQsQ0FBQzthQUFNLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLENBQUM7WUFDMUMsSUFBSSxDQUFDLGNBQWMsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDdkQsQ0FBQzthQUFNLENBQUM7WUFDTixPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFFRCxNQUFNLFNBQVMsR0FBRyxJQUFJLEdBQUcsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7UUFFL0MsTUFBTSxFQUFFLEdBQ04sSUFBSSxPQUFPLENBQUMsWUFBWSxDQUFDLFlBQVksQ0FDbkMsR0FBRyxTQUFTLENBQUMsSUFBSSxlQUFlLEVBQ2hDLGNBQWMsQ0FDZixDQUFDO1FBQ0osTUFBTSxRQUFRLEdBQUcsTUFBTSxFQUFFLENBQUMsSUFBSSxDQUFDO1lBQzdCLGFBQWEsRUFBRSxTQUFTLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDO1NBQ25ELENBQUMsQ0FBQTtRQUNGLE9BQU8sUUFBUSxDQUFDLFNBQVMsQ0FBQztJQUM1QixDQUFDO0NBQ0YifQ==
|
package/readme.hints.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import * as plugins from './qenv.plugins.js';
|
|
2
|
-
|
|
3
|
-
export class CloudlyAdapter {
|
|
4
|
-
public configVaultUrl?: string;
|
|
5
|
-
|
|
6
|
-
constructor(configVaultUrl?: string) {
|
|
7
|
-
this.configVaultUrl = configVaultUrl;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
public async getConfigBundle(): Promise<plugins.configvaultInterfaces.data.IEnvBundle | null> {
|
|
11
|
-
if (this.configVaultUrl) {
|
|
12
|
-
console.log(`ConfigVault specified through constructor`)
|
|
13
|
-
} else if (process.env['CONFIGVAULT_URL']) {
|
|
14
|
-
this.configVaultUrl = process.env['CONFIGVAULT_URL'];
|
|
15
|
-
} else {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const parsedUrl = new URL(this.configVaultUrl);
|
|
20
|
-
|
|
21
|
-
const tr =
|
|
22
|
-
new plugins.typedrequest.TypedRequest<plugins.configvaultInterfaces.requests.IReq_GetEnvBundle>(
|
|
23
|
-
`${parsedUrl.host}/typedrequest`,
|
|
24
|
-
'getEnvBundle'
|
|
25
|
-
);
|
|
26
|
-
const response = await tr.fire({
|
|
27
|
-
authorization: parsedUrl.pathname.replace('/', ''),
|
|
28
|
-
})
|
|
29
|
-
return response.envBundle;
|
|
30
|
-
}
|
|
31
|
-
}
|