@push.rocks/qenv 8.0.0 → 8.1.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 +2 -0
- package/dist_ts/index.js +3 -1
- package/dist_ts/qenv.classes.invalidqenvfileerror.d.ts +21 -0
- package/dist_ts/qenv.classes.invalidqenvfileerror.js +25 -0
- package/dist_ts/qenv.classes.missingqenvfileerror.d.ts +16 -0
- package/dist_ts/qenv.classes.missingqenvfileerror.js +19 -0
- package/dist_ts/qenv.classes.qenv.d.ts +13 -2
- package/dist_ts/qenv.classes.qenv.js +51 -6
- package/package.json +1 -1
- package/readme.md +46 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +2 -0
- package/ts/qenv.classes.invalidqenvfileerror.ts +43 -0
- package/ts/qenv.classes.missingqenvfileerror.ts +28 -0
- package/ts/qenv.classes.qenv.ts +63 -5
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/qenv',
|
|
6
|
-
version: '8.
|
|
6
|
+
version: '8.1.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,3 +1,5 @@
|
|
|
1
1
|
export * from './qenv.classes.qenv.js';
|
|
2
|
+
export * from './qenv.classes.invalidqenvfileerror.js';
|
|
3
|
+
export * from './qenv.classes.missingqenvfileerror.js';
|
|
2
4
|
export * from './qenv.classes.missingrequiredenvvarserror.js';
|
|
3
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMsd0NBQXdDLENBQUM7QUFDdkQsY0FBYyx3Q0FBd0MsQ0FBQztBQUN2RCxjQUFjLCtDQUErQyxDQUFDIn0=
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** which rule of the `required:` list a qenv.yml broke */
|
|
2
|
+
export type TQenvInvalidQenvFileReason = 'requiredNotAnArray' | 'requiredEntryNotAString';
|
|
3
|
+
export interface IQenvInvalidQenvFileErrorOptions {
|
|
4
|
+
qenvFilePathAbsolute: string;
|
|
5
|
+
reason: TQenvInvalidQenvFileReason;
|
|
6
|
+
/** the type name of the offending value, never the value itself */
|
|
7
|
+
invalidValueTypeName: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Thrown when qenv.yml declares a `required:` that qenv cannot read as a list of names. Such a
|
|
11
|
+
* declaration silently enforced nothing, which is the same fail-open as a missing qenv.yml.
|
|
12
|
+
* A file without any `required:` key, and one with an empty list, legitimately require nothing
|
|
13
|
+
* and never reach this error.
|
|
14
|
+
*/
|
|
15
|
+
export declare class QenvInvalidQenvFileError extends Error {
|
|
16
|
+
readonly code = "QENV_INVALID_QENV_FILE";
|
|
17
|
+
readonly qenvFilePathAbsolute: string;
|
|
18
|
+
readonly reason: TQenvInvalidQenvFileReason;
|
|
19
|
+
readonly invalidValueTypeName: string;
|
|
20
|
+
constructor(optionsArg: IQenvInvalidQenvFileErrorOptions);
|
|
21
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const reasonDescriptions = {
|
|
2
|
+
requiredNotAnArray: (typeNameArg) => `"required:" is of type ${typeNameArg} instead of a list of environment variable names`,
|
|
3
|
+
requiredEntryNotAString: (typeNameArg) => `"required:" contains an entry of type ${typeNameArg} instead of an environment variable name`,
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Thrown when qenv.yml declares a `required:` that qenv cannot read as a list of names. Such a
|
|
7
|
+
* declaration silently enforced nothing, which is the same fail-open as a missing qenv.yml.
|
|
8
|
+
* A file without any `required:` key, and one with an empty list, legitimately require nothing
|
|
9
|
+
* and never reach this error.
|
|
10
|
+
*/
|
|
11
|
+
export class QenvInvalidQenvFileError extends Error {
|
|
12
|
+
constructor(optionsArg) {
|
|
13
|
+
super(`The qenv.yml at ${optionsArg.qenvFilePathAbsolute} declares an unreadable requirement: ` +
|
|
14
|
+
`${reasonDescriptions[optionsArg.reason](optionsArg.invalidValueTypeName)}. ` +
|
|
15
|
+
`Write "required:" as a list of environment variable names, or omit it to require nothing.`);
|
|
16
|
+
this.code = 'QENV_INVALID_QENV_FILE';
|
|
17
|
+
this.name = 'QenvInvalidQenvFileError';
|
|
18
|
+
this.qenvFilePathAbsolute = optionsArg.qenvFilePathAbsolute;
|
|
19
|
+
this.reason = optionsArg.reason;
|
|
20
|
+
// only the type is carried and rendered: a malformed qenv.yml can hold a value where a name
|
|
21
|
+
// belongs, and a value may be a secret
|
|
22
|
+
this.invalidValueTypeName = optionsArg.invalidValueTypeName;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWVudi5jbGFzc2VzLmludmFsaWRxZW52ZmlsZWVycm9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvcWVudi5jbGFzc2VzLmludmFsaWRxZW52ZmlsZWVycm9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQVVBLE1BQU0sa0JBQWtCLEdBQXdFO0lBQzlGLGtCQUFrQixFQUFFLENBQUMsV0FBVyxFQUFFLEVBQUUsQ0FDbEMsMEJBQTBCLFdBQVcsa0RBQWtEO0lBQ3pGLHVCQUF1QixFQUFFLENBQUMsV0FBVyxFQUFFLEVBQUUsQ0FDdkMseUNBQXlDLFdBQVcsMENBQTBDO0NBQ2pHLENBQUM7QUFFRjs7Ozs7R0FLRztBQUNILE1BQU0sT0FBTyx3QkFBeUIsU0FBUSxLQUFLO0lBTWpELFlBQVksVUFBNEM7UUFDdEQsS0FBSyxDQUNILG1CQUFtQixVQUFVLENBQUMsb0JBQW9CLHVDQUF1QztZQUN2RixHQUFHLGtCQUFrQixDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxVQUFVLENBQUMsb0JBQW9CLENBQUMsSUFBSTtZQUM3RSwyRkFBMkYsQ0FDOUYsQ0FBQztRQVZZLFNBQUksR0FBRyx3QkFBd0IsQ0FBQztRQVc5QyxJQUFJLENBQUMsSUFBSSxHQUFHLDBCQUEwQixDQUFDO1FBQ3ZDLElBQUksQ0FBQyxvQkFBb0IsR0FBRyxVQUFVLENBQUMsb0JBQW9CLENBQUM7UUFDNUQsSUFBSSxDQUFDLE1BQU0sR0FBRyxVQUFVLENBQUMsTUFBTSxDQUFDO1FBQ2hDLDRGQUE0RjtRQUM1Rix1Q0FBdUM7UUFDdkMsSUFBSSxDQUFDLG9CQUFvQixHQUFHLFVBQVUsQ0FBQyxvQkFBb0IsQ0FBQztJQUM5RCxDQUFDO0NBQ0YifQ==
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface IQenvMissingQenvFileErrorOptions {
|
|
2
|
+
qenvFilePathAbsolute: string;
|
|
3
|
+
qenvDir: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Thrown when a Qenv that is set to fail on missing variables finds no qenv.yml at all.
|
|
7
|
+
* Without the file there is no `required:` list to enforce, so every check would pass by
|
|
8
|
+
* accident - a deployable that forgot to ship its qenv.yml would look healthy. `code` identifies
|
|
9
|
+
* the refusal even when two copies of qenv end up in one tree, which makes `instanceof` unreliable.
|
|
10
|
+
*/
|
|
11
|
+
export declare class QenvMissingQenvFileError extends Error {
|
|
12
|
+
readonly code = "QENV_MISSING_QENV_FILE";
|
|
13
|
+
readonly qenvFilePathAbsolute: string;
|
|
14
|
+
readonly qenvDir: string;
|
|
15
|
+
constructor(optionsArg: IQenvMissingQenvFileErrorOptions);
|
|
16
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a Qenv that is set to fail on missing variables finds no qenv.yml at all.
|
|
3
|
+
* Without the file there is no `required:` list to enforce, so every check would pass by
|
|
4
|
+
* accident - a deployable that forgot to ship its qenv.yml would look healthy. `code` identifies
|
|
5
|
+
* the refusal even when two copies of qenv end up in one tree, which makes `instanceof` unreliable.
|
|
6
|
+
*/
|
|
7
|
+
export class QenvMissingQenvFileError extends Error {
|
|
8
|
+
constructor(optionsArg) {
|
|
9
|
+
super(`qenv found no qenv.yml at ${optionsArg.qenvFilePathAbsolute}. It was constructed to fail ` +
|
|
10
|
+
`on missing required environment variables, and without qenv.yml there is no "required:" ` +
|
|
11
|
+
`list to enforce, so nothing would be checked. Ship qenv.yml with your deployable, or ` +
|
|
12
|
+
`construct Qenv with failOnMissing set to false.`);
|
|
13
|
+
this.code = 'QENV_MISSING_QENV_FILE';
|
|
14
|
+
this.name = 'QenvMissingQenvFileError';
|
|
15
|
+
this.qenvFilePathAbsolute = optionsArg.qenvFilePathAbsolute;
|
|
16
|
+
this.qenvDir = optionsArg.qenvDir;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWVudi5jbGFzc2VzLm1pc3NpbmdxZW52ZmlsZWVycm9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvcWVudi5jbGFzc2VzLm1pc3NpbmdxZW52ZmlsZWVycm9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUtBOzs7OztHQUtHO0FBQ0gsTUFBTSxPQUFPLHdCQUF5QixTQUFRLEtBQUs7SUFLakQsWUFBWSxVQUE0QztRQUN0RCxLQUFLLENBQ0gsNkJBQTZCLFVBQVUsQ0FBQyxvQkFBb0IsK0JBQStCO1lBQ3pGLDBGQUEwRjtZQUMxRix1RkFBdUY7WUFDdkYsaURBQWlELENBQ3BELENBQUM7UUFWWSxTQUFJLEdBQUcsd0JBQXdCLENBQUM7UUFXOUMsSUFBSSxDQUFDLElBQUksR0FBRywwQkFBMEIsQ0FBQztRQUN2QyxJQUFJLENBQUMsb0JBQW9CLEdBQUcsVUFBVSxDQUFDLG9CQUFvQixDQUFDO1FBQzVELElBQUksQ0FBQyxPQUFPLEdBQUcsVUFBVSxDQUFDLE9BQU8sQ0FBQztJQUNwQyxDQUFDO0NBQ0YifQ==
|
|
@@ -23,13 +23,24 @@ export declare class Qenv {
|
|
|
23
23
|
* @param qenvFileBasePathArg directory that holds qenv.yml
|
|
24
24
|
* @param envFileBasePathArg directory that holds env.json, env.yml or env.yaml
|
|
25
25
|
* @param failOnMissing throws a QenvMissingRequiredEnvVarsError when a variable listed under
|
|
26
|
-
* `required:` in qenv.yml is not provided by any source
|
|
27
|
-
*
|
|
26
|
+
* `required:` in qenv.yml is not provided by any source, a QenvMissingQenvFileError when there
|
|
27
|
+
* is no qenv.yml to read that list from, and a QenvInvalidQenvFileError when the list cannot be
|
|
28
|
+
* read as names. Pass false to only record the names in `missingEnvVars` and continue.
|
|
29
|
+
* @throws QenvMissingQenvFileError
|
|
30
|
+
* @throws QenvInvalidQenvFileError
|
|
28
31
|
* @throws QenvMissingRequiredEnvVarsError
|
|
29
32
|
*/
|
|
30
33
|
constructor(qenvFileBasePathArg?: string, envFileBasePathArg?: string, failOnMissing?: boolean);
|
|
31
34
|
private initializeFilePaths;
|
|
32
35
|
private loadRequiredEnvVars;
|
|
36
|
+
/**
|
|
37
|
+
* Refuses a qenv.yml whose `required:` cannot be read as names: it enforces nothing, which is
|
|
38
|
+
* the fail-open a missing qenv.yml would be. A caller that opted out of failing keeps running,
|
|
39
|
+
* but hears the same named reason instead of losing the requirement silently.
|
|
40
|
+
*/
|
|
41
|
+
private refuseInvalidQenvFile;
|
|
42
|
+
/** names the type of a value without rendering it: a value in a name's place may be a secret */
|
|
43
|
+
private describeValueType;
|
|
33
44
|
private loadAvailableEnvVars;
|
|
34
45
|
private checkForMissingEnvVars;
|
|
35
46
|
/**
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import * as plugins from './qenv.plugins.js';
|
|
2
|
+
import { QenvInvalidQenvFileError, } from './qenv.classes.invalidqenvfileerror.js';
|
|
3
|
+
import { QenvMissingQenvFileError } from './qenv.classes.missingqenvfileerror.js';
|
|
2
4
|
import { QenvMissingRequiredEnvVarsError } from './qenv.classes.missingrequiredenvvarserror.js';
|
|
3
5
|
export class Qenv {
|
|
4
6
|
/**
|
|
@@ -6,8 +8,11 @@ export class Qenv {
|
|
|
6
8
|
* @param qenvFileBasePathArg directory that holds qenv.yml
|
|
7
9
|
* @param envFileBasePathArg directory that holds env.json, env.yml or env.yaml
|
|
8
10
|
* @param failOnMissing throws a QenvMissingRequiredEnvVarsError when a variable listed under
|
|
9
|
-
* `required:` in qenv.yml is not provided by any source
|
|
10
|
-
*
|
|
11
|
+
* `required:` in qenv.yml is not provided by any source, a QenvMissingQenvFileError when there
|
|
12
|
+
* is no qenv.yml to read that list from, and a QenvInvalidQenvFileError when the list cannot be
|
|
13
|
+
* read as names. Pass false to only record the names in `missingEnvVars` and continue.
|
|
14
|
+
* @throws QenvMissingQenvFileError
|
|
15
|
+
* @throws QenvInvalidQenvFileError
|
|
11
16
|
* @throws QenvMissingRequiredEnvVarsError
|
|
12
17
|
*/
|
|
13
18
|
constructor(qenvFileBasePathArg = process.cwd(), envFileBasePathArg, failOnMissing = true) {
|
|
@@ -22,7 +27,7 @@ export class Qenv {
|
|
|
22
27
|
this.logger = new plugins.smartlog.ConsoleLog();
|
|
23
28
|
this.qenvFilePathAbsolute = '';
|
|
24
29
|
this.initializeFilePaths(qenvFileBasePathArg, envFileBasePathArg);
|
|
25
|
-
this.loadRequiredEnvVars();
|
|
30
|
+
this.loadRequiredEnvVars(failOnMissing);
|
|
26
31
|
this.loadAvailableEnvVars();
|
|
27
32
|
this.checkForMissingEnvVars(failOnMissing);
|
|
28
33
|
}
|
|
@@ -51,19 +56,59 @@ export class Qenv {
|
|
|
51
56
|
}
|
|
52
57
|
}
|
|
53
58
|
}
|
|
54
|
-
loadRequiredEnvVars() {
|
|
59
|
+
loadRequiredEnvVars(failOnMissing) {
|
|
55
60
|
if (!this.fileExists(this.qenvFilePathAbsolute)) {
|
|
61
|
+
if (failOnMissing) {
|
|
62
|
+
// enforcing without the file that declares what to enforce would pass every check by
|
|
63
|
+
// accident: a deployable that forgot to ship qenv.yml must be refused by name, not
|
|
64
|
+
// silently exempted
|
|
65
|
+
throw new QenvMissingQenvFileError({
|
|
66
|
+
qenvFilePathAbsolute: this.qenvFilePathAbsolute,
|
|
67
|
+
qenvDir: plugins.path.dirname(this.qenvFilePathAbsolute),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
56
70
|
return;
|
|
57
71
|
}
|
|
58
72
|
const qenvFile = this.readObjectFromFile(this.qenvFilePathAbsolute);
|
|
59
73
|
const declaredRequirements = qenvFile['required'];
|
|
74
|
+
if (declaredRequirements === undefined || declaredRequirements === null) {
|
|
75
|
+
// a qenv.yml that lists no requirement legitimately requires nothing
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
60
78
|
if (!Array.isArray(declaredRequirements)) {
|
|
61
|
-
this.
|
|
79
|
+
this.refuseInvalidQenvFile(failOnMissing, 'requiredNotAnArray', declaredRequirements);
|
|
62
80
|
return;
|
|
63
81
|
}
|
|
64
82
|
const declaredEntries = declaredRequirements;
|
|
83
|
+
const invalidEntryIndex = declaredEntries.findIndex((entry) => typeof entry !== 'string');
|
|
84
|
+
if (invalidEntryIndex !== -1) {
|
|
85
|
+
this.refuseInvalidQenvFile(failOnMissing, 'requiredEntryNotAString', declaredEntries[invalidEntryIndex]);
|
|
86
|
+
}
|
|
65
87
|
this.requiredEnvVars.push(...declaredEntries.filter((entry) => typeof entry === 'string'));
|
|
66
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Refuses a qenv.yml whose `required:` cannot be read as names: it enforces nothing, which is
|
|
91
|
+
* the fail-open a missing qenv.yml would be. A caller that opted out of failing keeps running,
|
|
92
|
+
* but hears the same named reason instead of losing the requirement silently.
|
|
93
|
+
*/
|
|
94
|
+
refuseInvalidQenvFile(failOnMissing, reason, invalidValue) {
|
|
95
|
+
const invalidQenvFileError = new QenvInvalidQenvFileError({
|
|
96
|
+
qenvFilePathAbsolute: this.qenvFilePathAbsolute,
|
|
97
|
+
reason,
|
|
98
|
+
invalidValueTypeName: this.describeValueType(invalidValue),
|
|
99
|
+
});
|
|
100
|
+
if (failOnMissing) {
|
|
101
|
+
throw invalidQenvFileError;
|
|
102
|
+
}
|
|
103
|
+
this.logger.log('warn', invalidQenvFileError.message);
|
|
104
|
+
}
|
|
105
|
+
/** names the type of a value without rendering it: a value in a name's place may be a secret */
|
|
106
|
+
describeValueType(valueArg) {
|
|
107
|
+
if (valueArg === null) {
|
|
108
|
+
return 'null';
|
|
109
|
+
}
|
|
110
|
+
return Array.isArray(valueArg) ? 'array' : typeof valueArg;
|
|
111
|
+
}
|
|
67
112
|
loadAvailableEnvVars() {
|
|
68
113
|
// resolved synchronously: a required env var is always a name, and every source a name can come
|
|
69
114
|
// from - process environment, env file, Docker secrets - is a synchronous read. Resolving it
|
|
@@ -276,4 +321,4 @@ export class Qenv {
|
|
|
276
321
|
: {};
|
|
277
322
|
}
|
|
278
323
|
}
|
|
279
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWVudi5jbGFzc2VzLnFlbnYuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9xZW52LmNsYXNzZXMucWVudi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLG1CQUFtQixDQUFDO0FBQzdDLE9BQU8sRUFBRSwrQkFBK0IsRUFBRSxNQUFNLCtDQUErQyxDQUFDO0FBY2hHLE1BQU0sT0FBTyxJQUFJO0lBa0JmOzs7Ozs7OztPQVFHO0lBQ0gsWUFDRSxzQkFBOEIsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUMzQyxrQkFBMkIsRUFDM0IsZ0JBQXlCLElBQUk7UUE3Qi9CLHFEQUFxRDtRQUM5QyxvQkFBZSxHQUFhLEVBQUUsQ0FBQztRQUV0QyxnREFBZ0Q7UUFDekMscUJBQWdCLEdBQWEsRUFBRSxDQUFDO1FBRXZDLGlEQUFpRDtRQUMxQyxtQkFBYyxHQUFhLEVBQUUsQ0FBQztRQUVyQywwREFBMEQ7UUFDbkQsbUJBQWMsR0FBb0IsRUFBRSxDQUFDO1FBRXJDLFdBQU0sR0FBRyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUM7UUFFM0MseUJBQW9CLEdBQUcsRUFBRSxDQUFDO1FBaUIvQixJQUFJLENBQUMsbUJBQW1CLENBQUMsbUJBQW1CLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztRQUNsRSxJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUMzQixJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztRQUM1QixJQUFJLENBQUMsc0JBQXNCLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDN0MsQ0FBQztJQUVPLG1CQUFtQixDQUFDLG1CQUEyQixFQUFFLGtCQUEyQjtRQUNsRixJQUFJLENBQUMsb0JBQW9CLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQzNDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDLEVBQ3pDLFVBQVUsQ0FDWCxDQUFDO1FBRUYsSUFBSSxrQkFBa0IsRUFBRSxDQUFDO1lBQ3ZCLE1BQU0sZUFBZSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGtCQUFrQixDQUFDLENBQUM7WUFFakUsTUFBTSxlQUFlLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsZUFBZSxFQUFFLFVBQVUsQ0FBQyxDQUFDO1lBQ3ZFLE1BQU0sY0FBYyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRSxTQUFTLENBQUMsQ0FBQztZQUNyRSxNQUFNLGVBQWUsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUUsVUFBVSxDQUFDLENBQUM7WUFFdkUsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLGVBQWUsQ0FBQyxDQUFDO1lBQzNELE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUMsQ0FBQztZQUN6RCxNQUFNLGlCQUFpQixHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsZUFBZSxDQUFDLENBQUM7WUFFM0QsSUFBSSxpQkFBaUIsSUFBSSxDQUFDLGdCQUFnQixJQUFJLGlCQUFpQixDQUFDLEVBQUUsQ0FBQztnQkFDakUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLHVEQUF1RCxDQUFDLENBQUM7Z0JBQ2pGLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxlQUFlLENBQUM7WUFDN0MsQ0FBQztpQkFBTSxJQUFJLGlCQUFpQixFQUFFLENBQUM7Z0JBQzdCLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxlQUFlLENBQUM7WUFDN0MsQ0FBQztpQkFBTSxJQUFJLGdCQUFnQixFQUFFLENBQUM7Z0JBQzVCLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxjQUFjLENBQUM7WUFDNUMsQ0FBQztpQkFBTSxJQUFJLGlCQUFpQixFQUFFLENBQUM7Z0JBQzdCLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxlQUFlLENBQUM7WUFDN0MsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRU8sbUJBQW1CO1FBQ3pCLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxFQUFFLENBQUM7WUFDaEQsT0FBTztRQUNULENBQUM7UUFDRCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLENBQUM7UUFDcEUsTUFBTSxvQkFBb0IsR0FBRyxRQUFRLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDbEQsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsb0JBQW9CLENBQUMsRUFBRSxDQUFDO1lBQ3pDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSwrQ0FBK0MsQ0FBQyxDQUFDO1lBQ3pFLE9BQU87UUFDVCxDQUFDO1FBQ0QsTUFBTSxlQUFlLEdBQWMsb0JBQW9CLENBQUM7UUFDeEQsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQ3ZCLEdBQUcsZUFBZSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEtBQUssRUFBbUIsRUFBRSxDQUFDLE9BQU8sS0FBSyxLQUFLLFFBQVEsQ0FBQyxDQUNqRixDQUFDO0lBQ0osQ0FBQztJQUVPLG9CQUFvQjtRQUMxQixnR0FBZ0c7UUFDaEcsNkZBQTZGO1FBQzdGLDhGQUE4RjtRQUM5Rix1QkFBdUI7UUFDdkIsS0FBSyxNQUFNLE1BQU0sSUFBSSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDMUMsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQzVDLElBQUksS0FBSyxLQUFLLFNBQVMsRUFBRSxDQUFDO2dCQUN4QixJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUNuQyxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxHQUFHLEtBQUssQ0FBQztZQUN0QyxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFTyxzQkFBc0IsQ0FBQyxhQUFzQjtRQUNuRCxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUMvQyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUNwRCxDQUFDO1FBRUYsSUFBSSxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUNyQyxPQUFPO1FBQ1QsQ0FBQztRQUVELElBQUksYUFBYSxFQUFFLENBQUM7WUFDbEIsOEZBQThGO1lBQzlGLE1BQU0sSUFBSSwrQkFBK0IsQ0FBQztnQkFDeEMsY0FBYyxFQUFFLElBQUksQ0FBQyxjQUFjO2dCQUNuQyxlQUFlLEVBQUUsSUFBSSxDQUFDLGVBQWU7Z0JBQ3JDLG9CQUFvQixFQUFFLElBQUksQ0FBQyxvQkFBb0I7YUFDaEQsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUVELElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUNiLE1BQU0sRUFDTixzRUFBc0UsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FDdkcsQ0FBQztJQUNKLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksS0FBSyxDQUFDLGlCQUFpQixDQUM1QixpQkFBNEM7UUFFNUMsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLGlCQUFpQixDQUFDLEVBQUUsQ0FBQztZQUNyQyxLQUFLLE1BQU0sVUFBVSxJQUFJLGlCQUFpQixFQUFFLENBQUM7Z0JBQzNDLE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUMsQ0FBQztnQkFDbEQsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7b0JBQ3hCLE9BQU8sS0FBSyxDQUFDO2dCQUNmLENBQUM7WUFDSCxDQUFDO1lBQ0QsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQzthQUFNLENBQUM7WUFDTixPQUFPLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQ3BELENBQUM7SUFDSCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLHVCQUF1QixDQUNsQyxpQkFBNEM7UUFFNUMsTUFBTSxLQUFLLEdBQUcsTUFBTSxJQUFJLENBQUMsaUJBQWlCLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUM5RCxJQUFJLEtBQUssS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUN4QixNQUFNLElBQUksS0FBSyxDQUFDLFdBQVcsSUFBSSxDQUFDLGtCQUFrQixDQUFDLGlCQUFpQixDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQ3ZGLENBQUM7UUFDRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRDs7OztPQUlHO0lBQ0kscUJBQXFCLENBQUMsaUJBQW9DO1FBQy9ELElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLENBQUM7WUFDckMsS0FBSyxNQUFNLFVBQVUsSUFBSSxpQkFBaUIsRUFBRSxDQUFDO2dCQUMzQyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsVUFBVSxDQUFDLENBQUM7Z0JBQ2hELElBQUksS0FBSyxLQUFLLFNBQVMsRUFBRSxDQUFDO29CQUN4QixPQUFPLEtBQUssQ0FBQztnQkFDZixDQUFDO1lBQ0gsQ0FBQztZQUNELE9BQU8sU0FBUyxDQUFDO1FBQ25CLENBQUM7YUFBTSxDQUFDO1lBQ04sT0FBTyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUNsRCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMseUJBQXlCLENBQ3BDLGlCQUFvQztRQUVwQyxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQ2pFLElBQUksUUFBUSxJQUFJLFFBQVEsQ0FBQyxVQUFVLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQztZQUNyRCxNQUFNLFVBQVUsR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3RELE9BQU8sSUFBSSxDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUN2QyxDQUFDO1FBQ0QsT0FBTyxRQUFRLENBQUM7SUFDbEIsQ0FBQztJQUVPLEtBQUssQ0FBQyxZQUFZLENBQUMsWUFBd0I7UUFDakQsSUFBSSxPQUFPLFlBQVksS0FBSyxVQUFVLEVBQUUsQ0FBQztZQUN2QyxPQUFPLE1BQU0sWUFBWSxFQUFFLENBQUM7UUFDOUIsQ0FBQztRQUVELDRGQUE0RjtRQUM1RixPQUFPLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxZQUFZLENBQUMsQ0FBQztJQUM3QyxDQUFDO0lBRU8sZ0JBQWdCLENBQUMsVUFBa0I7UUFDekMsOEZBQThGO1FBQzlGLDhGQUE4RjtRQUM5RixnREFBZ0Q7UUFDaEQsTUFBTSxPQUFPLEdBQW9DO1lBQy9DLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQywwQkFBMEIsQ0FBQyxVQUFVLENBQUM7WUFDakQsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLHdCQUF3QixDQUFDLFVBQVUsQ0FBQztZQUMvQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsVUFBVSxDQUFDO1lBQzFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxVQUFVLENBQUM7U0FDL0MsQ0FBQztRQUVGLEtBQUssTUFBTSxVQUFVLElBQUksT0FBTyxFQUFFLENBQUM7WUFDakMsTUFBTSxLQUFLLEdBQUcsVUFBVSxFQUFFLENBQUM7WUFDM0IsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7Z0JBQ3hCLE9BQU8sS0FBSyxDQUFDO1lBQ2YsQ0FBQztRQUNILENBQUM7UUFFRCxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBRUQsbUdBQW1HO0lBQzNGLGtCQUFrQixDQUFDLGlCQUE0QztRQUNyRSxNQUFNLFVBQVUsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDOUYsT0FBTyxVQUFVO2FBQ2QsR0FBRyxDQUFDLENBQUMsU0FBUyxFQUFFLEVBQUUsQ0FDakIsT0FBTyxTQUFTLEtBQUssVUFBVTtZQUM3QixDQUFDLENBQUMsR0FBRyxTQUFTLENBQUMsSUFBSSxJQUFJLFdBQVcsSUFBSTtZQUN0QyxDQUFDLENBQUMsU0FBUyxDQUNkO2FBQ0EsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ2hCLENBQUM7SUFFTywwQkFBMEIsQ0FBQyxVQUFrQjtRQUNuRCxPQUFPLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDakMsQ0FBQztJQUVPLHdCQUF3QixDQUFDLFVBQWtCO1FBQ2pELElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxFQUFFLENBQUM7WUFDL0MsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztRQUNELElBQUksQ0FBQztZQUNILE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsQ0FBQztZQUNsRSxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7WUFDbEMsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7Z0JBQ3hCLE9BQU8sU0FBUyxDQUFDO1lBQ25CLENBQUM7WUFDRCxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLEtBQUssSUFBSSxFQUFFLENBQUM7Z0JBQ2hELE9BQU8sZUFBZSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDcEQsQ0FBQztZQUNELE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3ZCLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztJQUNILENBQUM7SUFFRDs7O09BR0c7SUFDTyw2QkFBNkI7UUFDckMsT0FBTyxjQUFjLENBQUM7SUFDeEIsQ0FBQztJQUVPLG1CQUFtQixDQUFDLFVBQWtCO1FBQzVDLE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyw2QkFBNkIsRUFBRSxFQUFFLFVBQVUsQ0FBQyxDQUFDO1FBQ3ZGLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1lBQ2hDLE9BQU8sT0FBTyxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3JELENBQUM7UUFDRCxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBRU8sdUJBQXVCLENBQUMsVUFBa0I7UUFDaEQsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLENBQUMsNkJBQTZCLEVBQUUsQ0FBQztRQUNsRSxJQUFJLElBQUksQ0FBQyxlQUFlLENBQUMsb0JBQW9CLENBQUMsRUFBRSxDQUFDO1lBQy9DLE1BQU0sZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLEVBQUUsQ0FBQyxXQUFXLENBQUMsb0JBQW9CLENBQUMsQ0FBQztZQUN0RSxLQUFLLE1BQU0sTUFBTSxJQUFJLGdCQUFnQixFQUFFLENBQUM7Z0JBQ3RDLElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDO29CQUNuQyxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQzFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLG9CQUFvQixFQUFFLE1BQU0sQ0FBQyxDQUNoRCxDQUFDO29CQUNGLE1BQU0sS0FBSyxHQUFHLFlBQVksQ0FBQyxVQUFVLENBQUMsQ0FBQztvQkFDdkMsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7d0JBQ3hCLFNBQVM7b0JBQ1gsQ0FBQztvQkFDRCxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLEtBQUssSUFBSSxFQUFFLENBQUM7d0JBQ2hELE9BQU8sZUFBZSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUM7b0JBQ3BELENBQUM7b0JBQ0QsT0FBTyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQ3ZCLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUNELE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7SUFFTyxZQUFZLENBQUMsSUFBYTtRQUNoQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3hDLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDcEQsQ0FBQztJQUVPLFlBQVksQ0FBQyxhQUFxQjtRQUN4QyxNQUFNLGFBQWEsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLGFBQWEsRUFBRSxRQUFRLENBQUMsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDN0UsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ25DLENBQUM7SUFFTyxVQUFVLENBQUMsUUFBNEI7UUFDN0MsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ2QsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBQ0QsT0FBTyxPQUFPLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBRU8sZUFBZSxDQUFDLGFBQXFCO1FBQzNDLElBQUksQ0FBQztZQUNILE9BQU8sT0FBTyxDQUFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsYUFBYSxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDMUQsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztJQUNILENBQUM7SUFFTyxrQkFBa0IsQ0FBQyxRQUFnQjtRQUN6QyxNQUFNLFVBQVUsR0FBRyxPQUFPLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDN0QsTUFBTSxZQUFZLEdBQVksUUFBUSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUM7WUFDdEQsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDO1lBQ3hCLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUNuQyxPQUFPLE9BQU8sWUFBWSxLQUFLLFFBQVEsSUFBSSxZQUFZLEtBQUssSUFBSTtZQUM5RCxDQUFDLENBQUUsWUFBa0M7WUFDckMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUNULENBQUM7Q0FDRiJ9
|
|
324
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWVudi5jbGFzc2VzLnFlbnYuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9xZW52LmNsYXNzZXMucWVudi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLG1CQUFtQixDQUFDO0FBQzdDLE9BQU8sRUFDTCx3QkFBd0IsR0FFekIsTUFBTSx3Q0FBd0MsQ0FBQztBQUNoRCxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUNsRixPQUFPLEVBQUUsK0JBQStCLEVBQUUsTUFBTSwrQ0FBK0MsQ0FBQztBQWNoRyxNQUFNLE9BQU8sSUFBSTtJQWtCZjs7Ozs7Ozs7Ozs7T0FXRztJQUNILFlBQ0Usc0JBQThCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFDM0Msa0JBQTJCLEVBQzNCLGdCQUF5QixJQUFJO1FBaEMvQixxREFBcUQ7UUFDOUMsb0JBQWUsR0FBYSxFQUFFLENBQUM7UUFFdEMsZ0RBQWdEO1FBQ3pDLHFCQUFnQixHQUFhLEVBQUUsQ0FBQztRQUV2QyxpREFBaUQ7UUFDMUMsbUJBQWMsR0FBYSxFQUFFLENBQUM7UUFFckMsMERBQTBEO1FBQ25ELG1CQUFjLEdBQW9CLEVBQUUsQ0FBQztRQUVyQyxXQUFNLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBRTNDLHlCQUFvQixHQUFHLEVBQUUsQ0FBQztRQW9CL0IsSUFBSSxDQUFDLG1CQUFtQixDQUFDLG1CQUFtQixFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFDbEUsSUFBSSxDQUFDLG1CQUFtQixDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQ3hDLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO1FBQzVCLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUM3QyxDQUFDO0lBRU8sbUJBQW1CLENBQUMsbUJBQTJCLEVBQUUsa0JBQTJCO1FBQ2xGLElBQUksQ0FBQyxvQkFBb0Is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
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -67,6 +67,24 @@ Every listed name is resolved while the `Qenv` instance is constructed. What is
|
|
|
67
67
|
`availableEnvVars` and, as a string, in `keyValueObject`; what is missing lands in `missingEnvVars`
|
|
68
68
|
and, unless you pass `failOnMissing: false`, makes the constructor throw.
|
|
69
69
|
|
|
70
|
+
With `failOnMissing` left at its default `true`, the `qenv.yml` itself is part of what is enforced:
|
|
71
|
+
if there is none at the given path, the constructor throws `QenvMissingQenvFileError` and names the
|
|
72
|
+
path it looked at. Without the file there is no `required:` list, so every check would pass by
|
|
73
|
+
accident - a container image that forgot to ship its `qenv.yml` would look healthy while enforcing
|
|
74
|
+
nothing. **Ship `qenv.yml` with your deployable**, next to the code that reads it. A `qenv.yml`
|
|
75
|
+
whose `required:` cannot be read as a list of names - a scalar instead of a list, or an entry that
|
|
76
|
+
is not a name - is refused the same way, with `QenvInvalidQenvFileError`.
|
|
77
|
+
|
|
78
|
+
A `qenv.yml` that requires nothing is legitimate and constructs: write `required: []`, or leave the
|
|
79
|
+
`required:` key out altogether. Only the file's absence and an unreadable list are refused.
|
|
80
|
+
|
|
81
|
+
The CLI or library case that reads from wherever it happens to run passes `failOnMissing: false`:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// no qenv.yml expected here - resolve what is there, enforce nothing
|
|
85
|
+
const qenv = new Qenv(process.cwd(), undefined, false);
|
|
86
|
+
```
|
|
87
|
+
|
|
70
88
|
#### 2. Provide Values (`env.yml` or `env.json`)
|
|
71
89
|
|
|
72
90
|
For local development, create an `env.yml` or `env.json` file:
|
|
@@ -363,8 +381,11 @@ new Qenv(
|
|
|
363
381
|
)
|
|
364
382
|
```
|
|
365
383
|
|
|
366
|
-
|
|
367
|
-
`
|
|
384
|
+
With `failOnMissing` true the constructor throws `QenvMissingQenvFileError` when there is no
|
|
385
|
+
`qenv.yml` at `qenvFileBasePathArg`, `QenvInvalidQenvFileError` when its `required:` cannot be read
|
|
386
|
+
as a list of names, and `QenvMissingRequiredEnvVarsError` when a name listed under `required:` is
|
|
387
|
+
not provided by any source. With `failOnMissing` false the constructor never throws: a missing
|
|
388
|
+
`qenv.yml` is accepted and an unreadable `required:` is logged as a warning.
|
|
368
389
|
|
|
369
390
|
#### Methods
|
|
370
391
|
|
|
@@ -397,6 +418,29 @@ Thrown by the constructor when `failOnMissing` is true and a required variable h
|
|
|
397
418
|
| `requiredEnvVars` | `string[]` | Every name listed under `required:` |
|
|
398
419
|
| `qenvFilePathAbsolute` | `string` | The qenv.yml that declared them |
|
|
399
420
|
|
|
421
|
+
### Class: `QenvMissingQenvFileError`
|
|
422
|
+
|
|
423
|
+
Thrown by the constructor when `failOnMissing` is true and there is no `qenv.yml` to read the
|
|
424
|
+
`required:` list from.
|
|
425
|
+
|
|
426
|
+
| Member | Type | Description |
|
|
427
|
+
|--------|------|-------------|
|
|
428
|
+
| `code` | `'QENV_MISSING_QENV_FILE'` | Stable identifier, safe across duplicate installs |
|
|
429
|
+
| `qenvFilePathAbsolute` | `string` | The qenv.yml path that was looked at |
|
|
430
|
+
| `qenvDir` | `string` | The resolved directory that should hold it |
|
|
431
|
+
|
|
432
|
+
### Class: `QenvInvalidQenvFileError`
|
|
433
|
+
|
|
434
|
+
Thrown by the constructor when `failOnMissing` is true and `required:` is not a list of names;
|
|
435
|
+
logged as a warning with the same message when `failOnMissing` is false.
|
|
436
|
+
|
|
437
|
+
| Member | Type | Description |
|
|
438
|
+
|--------|------|-------------|
|
|
439
|
+
| `code` | `'QENV_INVALID_QENV_FILE'` | Stable identifier, safe across duplicate installs |
|
|
440
|
+
| `reason` | `'requiredNotAnArray' \| 'requiredEntryNotAString'` | Which rule the file broke |
|
|
441
|
+
| `invalidValueTypeName` | `string` | The type of the offending value, never the value itself |
|
|
442
|
+
| `qenvFilePathAbsolute` | `string` | The qenv.yml that declared it |
|
|
443
|
+
|
|
400
444
|
## Issue Reporting and Security
|
|
401
445
|
|
|
402
446
|
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.
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/index.ts
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** which rule of the `required:` list a qenv.yml broke */
|
|
2
|
+
export type TQenvInvalidQenvFileReason = 'requiredNotAnArray' | 'requiredEntryNotAString';
|
|
3
|
+
|
|
4
|
+
export interface IQenvInvalidQenvFileErrorOptions {
|
|
5
|
+
qenvFilePathAbsolute: string;
|
|
6
|
+
reason: TQenvInvalidQenvFileReason;
|
|
7
|
+
/** the type name of the offending value, never the value itself */
|
|
8
|
+
invalidValueTypeName: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const reasonDescriptions: Record<TQenvInvalidQenvFileReason, (typeNameArg: string) => string> = {
|
|
12
|
+
requiredNotAnArray: (typeNameArg) =>
|
|
13
|
+
`"required:" is of type ${typeNameArg} instead of a list of environment variable names`,
|
|
14
|
+
requiredEntryNotAString: (typeNameArg) =>
|
|
15
|
+
`"required:" contains an entry of type ${typeNameArg} instead of an environment variable name`,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Thrown when qenv.yml declares a `required:` that qenv cannot read as a list of names. Such a
|
|
20
|
+
* declaration silently enforced nothing, which is the same fail-open as a missing qenv.yml.
|
|
21
|
+
* A file without any `required:` key, and one with an empty list, legitimately require nothing
|
|
22
|
+
* and never reach this error.
|
|
23
|
+
*/
|
|
24
|
+
export class QenvInvalidQenvFileError extends Error {
|
|
25
|
+
public readonly code = 'QENV_INVALID_QENV_FILE';
|
|
26
|
+
public readonly qenvFilePathAbsolute: string;
|
|
27
|
+
public readonly reason: TQenvInvalidQenvFileReason;
|
|
28
|
+
public readonly invalidValueTypeName: string;
|
|
29
|
+
|
|
30
|
+
constructor(optionsArg: IQenvInvalidQenvFileErrorOptions) {
|
|
31
|
+
super(
|
|
32
|
+
`The qenv.yml at ${optionsArg.qenvFilePathAbsolute} declares an unreadable requirement: ` +
|
|
33
|
+
`${reasonDescriptions[optionsArg.reason](optionsArg.invalidValueTypeName)}. ` +
|
|
34
|
+
`Write "required:" as a list of environment variable names, or omit it to require nothing.`
|
|
35
|
+
);
|
|
36
|
+
this.name = 'QenvInvalidQenvFileError';
|
|
37
|
+
this.qenvFilePathAbsolute = optionsArg.qenvFilePathAbsolute;
|
|
38
|
+
this.reason = optionsArg.reason;
|
|
39
|
+
// only the type is carried and rendered: a malformed qenv.yml can hold a value where a name
|
|
40
|
+
// belongs, and a value may be a secret
|
|
41
|
+
this.invalidValueTypeName = optionsArg.invalidValueTypeName;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface IQenvMissingQenvFileErrorOptions {
|
|
2
|
+
qenvFilePathAbsolute: string;
|
|
3
|
+
qenvDir: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Thrown when a Qenv that is set to fail on missing variables finds no qenv.yml at all.
|
|
8
|
+
* Without the file there is no `required:` list to enforce, so every check would pass by
|
|
9
|
+
* accident - a deployable that forgot to ship its qenv.yml would look healthy. `code` identifies
|
|
10
|
+
* the refusal even when two copies of qenv end up in one tree, which makes `instanceof` unreliable.
|
|
11
|
+
*/
|
|
12
|
+
export class QenvMissingQenvFileError extends Error {
|
|
13
|
+
public readonly code = 'QENV_MISSING_QENV_FILE';
|
|
14
|
+
public readonly qenvFilePathAbsolute: string;
|
|
15
|
+
public readonly qenvDir: string;
|
|
16
|
+
|
|
17
|
+
constructor(optionsArg: IQenvMissingQenvFileErrorOptions) {
|
|
18
|
+
super(
|
|
19
|
+
`qenv found no qenv.yml at ${optionsArg.qenvFilePathAbsolute}. It was constructed to fail ` +
|
|
20
|
+
`on missing required environment variables, and without qenv.yml there is no "required:" ` +
|
|
21
|
+
`list to enforce, so nothing would be checked. Ship qenv.yml with your deployable, or ` +
|
|
22
|
+
`construct Qenv with failOnMissing set to false.`
|
|
23
|
+
);
|
|
24
|
+
this.name = 'QenvMissingQenvFileError';
|
|
25
|
+
this.qenvFilePathAbsolute = optionsArg.qenvFilePathAbsolute;
|
|
26
|
+
this.qenvDir = optionsArg.qenvDir;
|
|
27
|
+
}
|
|
28
|
+
}
|
package/ts/qenv.classes.qenv.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import * as plugins from './qenv.plugins.js';
|
|
2
|
+
import {
|
|
3
|
+
QenvInvalidQenvFileError,
|
|
4
|
+
type TQenvInvalidQenvFileReason,
|
|
5
|
+
} from './qenv.classes.invalidqenvfileerror.js';
|
|
6
|
+
import { QenvMissingQenvFileError } from './qenv.classes.missingqenvfileerror.js';
|
|
2
7
|
import { QenvMissingRequiredEnvVarsError } from './qenv.classes.missingrequiredenvvarserror.js';
|
|
3
8
|
|
|
4
9
|
/**
|
|
@@ -36,8 +41,11 @@ export class Qenv {
|
|
|
36
41
|
* @param qenvFileBasePathArg directory that holds qenv.yml
|
|
37
42
|
* @param envFileBasePathArg directory that holds env.json, env.yml or env.yaml
|
|
38
43
|
* @param failOnMissing throws a QenvMissingRequiredEnvVarsError when a variable listed under
|
|
39
|
-
* `required:` in qenv.yml is not provided by any source
|
|
40
|
-
*
|
|
44
|
+
* `required:` in qenv.yml is not provided by any source, a QenvMissingQenvFileError when there
|
|
45
|
+
* is no qenv.yml to read that list from, and a QenvInvalidQenvFileError when the list cannot be
|
|
46
|
+
* read as names. Pass false to only record the names in `missingEnvVars` and continue.
|
|
47
|
+
* @throws QenvMissingQenvFileError
|
|
48
|
+
* @throws QenvInvalidQenvFileError
|
|
41
49
|
* @throws QenvMissingRequiredEnvVarsError
|
|
42
50
|
*/
|
|
43
51
|
constructor(
|
|
@@ -46,7 +54,7 @@ export class Qenv {
|
|
|
46
54
|
failOnMissing: boolean = true
|
|
47
55
|
) {
|
|
48
56
|
this.initializeFilePaths(qenvFileBasePathArg, envFileBasePathArg);
|
|
49
|
-
this.loadRequiredEnvVars();
|
|
57
|
+
this.loadRequiredEnvVars(failOnMissing);
|
|
50
58
|
this.loadAvailableEnvVars();
|
|
51
59
|
this.checkForMissingEnvVars(failOnMissing);
|
|
52
60
|
}
|
|
@@ -81,22 +89,72 @@ export class Qenv {
|
|
|
81
89
|
}
|
|
82
90
|
}
|
|
83
91
|
|
|
84
|
-
private loadRequiredEnvVars() {
|
|
92
|
+
private loadRequiredEnvVars(failOnMissing: boolean) {
|
|
85
93
|
if (!this.fileExists(this.qenvFilePathAbsolute)) {
|
|
94
|
+
if (failOnMissing) {
|
|
95
|
+
// enforcing without the file that declares what to enforce would pass every check by
|
|
96
|
+
// accident: a deployable that forgot to ship qenv.yml must be refused by name, not
|
|
97
|
+
// silently exempted
|
|
98
|
+
throw new QenvMissingQenvFileError({
|
|
99
|
+
qenvFilePathAbsolute: this.qenvFilePathAbsolute,
|
|
100
|
+
qenvDir: plugins.path.dirname(this.qenvFilePathAbsolute),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
86
103
|
return;
|
|
87
104
|
}
|
|
88
105
|
const qenvFile = this.readObjectFromFile(this.qenvFilePathAbsolute);
|
|
89
106
|
const declaredRequirements = qenvFile['required'];
|
|
107
|
+
if (declaredRequirements === undefined || declaredRequirements === null) {
|
|
108
|
+
// a qenv.yml that lists no requirement legitimately requires nothing
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
90
111
|
if (!Array.isArray(declaredRequirements)) {
|
|
91
|
-
this.
|
|
112
|
+
this.refuseInvalidQenvFile(failOnMissing, 'requiredNotAnArray', declaredRequirements);
|
|
92
113
|
return;
|
|
93
114
|
}
|
|
94
115
|
const declaredEntries: unknown[] = declaredRequirements;
|
|
116
|
+
const invalidEntryIndex = declaredEntries.findIndex((entry) => typeof entry !== 'string');
|
|
117
|
+
if (invalidEntryIndex !== -1) {
|
|
118
|
+
this.refuseInvalidQenvFile(
|
|
119
|
+
failOnMissing,
|
|
120
|
+
'requiredEntryNotAString',
|
|
121
|
+
declaredEntries[invalidEntryIndex]
|
|
122
|
+
);
|
|
123
|
+
}
|
|
95
124
|
this.requiredEnvVars.push(
|
|
96
125
|
...declaredEntries.filter((entry): entry is string => typeof entry === 'string')
|
|
97
126
|
);
|
|
98
127
|
}
|
|
99
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Refuses a qenv.yml whose `required:` cannot be read as names: it enforces nothing, which is
|
|
131
|
+
* the fail-open a missing qenv.yml would be. A caller that opted out of failing keeps running,
|
|
132
|
+
* but hears the same named reason instead of losing the requirement silently.
|
|
133
|
+
*/
|
|
134
|
+
private refuseInvalidQenvFile(
|
|
135
|
+
failOnMissing: boolean,
|
|
136
|
+
reason: TQenvInvalidQenvFileReason,
|
|
137
|
+
invalidValue: unknown
|
|
138
|
+
) {
|
|
139
|
+
const invalidQenvFileError = new QenvInvalidQenvFileError({
|
|
140
|
+
qenvFilePathAbsolute: this.qenvFilePathAbsolute,
|
|
141
|
+
reason,
|
|
142
|
+
invalidValueTypeName: this.describeValueType(invalidValue),
|
|
143
|
+
});
|
|
144
|
+
if (failOnMissing) {
|
|
145
|
+
throw invalidQenvFileError;
|
|
146
|
+
}
|
|
147
|
+
this.logger.log('warn', invalidQenvFileError.message);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** names the type of a value without rendering it: a value in a name's place may be a secret */
|
|
151
|
+
private describeValueType(valueArg: unknown): string {
|
|
152
|
+
if (valueArg === null) {
|
|
153
|
+
return 'null';
|
|
154
|
+
}
|
|
155
|
+
return Array.isArray(valueArg) ? 'array' : typeof valueArg;
|
|
156
|
+
}
|
|
157
|
+
|
|
100
158
|
private loadAvailableEnvVars() {
|
|
101
159
|
// resolved synchronously: a required env var is always a name, and every source a name can come
|
|
102
160
|
// from - process environment, env file, Docker secrets - is a synchronous read. Resolving it
|