@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.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/qenv',
6
- version: '8.0.0',
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=
@@ -1,2 +1,4 @@
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';
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMsK0NBQStDLENBQUMifQ==
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. Pass false to only record the names in
27
- * `missingEnvVars` and continue.
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. Pass false to only record the names in
10
- * `missingEnvVars` and continue.
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.logger.log('warn', 'qenv.yml does not contain a "required" Array!');
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWVudi5jbGFzc2VzLnFlbnYuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9xZW52LmNsYXNzZXMucWVudi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLG1CQUFtQixDQUFDO0FBQzdDLE9BQU8sRUFBRSwrQkFBK0IsRUFBRSxNQUFNLCtDQUErQyxDQUFDO0FBY2hHLE1BQU0sT0FBTyxJQUFJO0lBa0JmOzs7Ozs7OztPQVFHO0lBQ0gsWUFDRSxzQkFBOEIsT0FBTyxDQUFDLEdBQUcsRUFBRSxFQUMzQyxrQkFBMkIsRUFDM0IsZ0JBQXlCLElBQUk7UUE3Qi9CLHFEQUFxRDtRQUM5QyxvQkFBZSxHQUFhLEVBQUUsQ0FBQztRQUV0QyxnREFBZ0Q7UUFDekMscUJBQWdCLEdBQWEsRUFBRSxDQUFDO1FBRXZDLGlEQUFpRDtRQUMxQyxtQkFBYyxHQUFhLEVBQUUsQ0FBQztRQUVyQywwREFBMEQ7UUFDbkQsbUJBQWMsR0FBb0IsRUFBRSxDQUFDO1FBRXJDLFdBQU0sR0FBRyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUM7UUFFM0MseUJBQW9CLEdBQUcsRUFBRSxDQUFDO1FBaUIvQixJQUFJLENBQUMsbUJBQW1CLENBQUMsbUJBQW1CLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztRQUNsRSxJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUMzQixJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztRQUM1QixJQUFJLENBQUMsc0JBQXNCLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDN0MsQ0FBQztJQUVPLG1CQUFtQixDQUFDLG1CQUEyQixFQUFFLGtCQUEyQjtRQUNsRixJQUFJLENBQUMsb0JBQW9CLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQzNDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDLEVBQ3pDLFVBQVUsQ0FDWCxDQUFDO1FBRUYs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
324
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicWVudi5jbGFzc2VzLnFlbnYuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9xZW52LmNsYXNzZXMucWVudi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLG1CQUFtQixDQUFDO0FBQzdDLE9BQU8sRUFDTCx3QkFBd0IsR0FFekIsTUFBTSx3Q0FBd0MsQ0FBQztBQUNoRCxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUNsRixPQUFPLEVBQUUsK0JBQStCLEVBQUUsTUFBTSwrQ0FBK0MsQ0FBQztBQWNoRyxNQUFNLE9BQU8sSUFBSTtJQWtCZjs7Ozs7Ozs7Ozs7T0FXRztJQUNILFlBQ0Usc0JBQThCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFDM0Msa0JBQTJCLEVBQzNCLGdCQUF5QixJQUFJO1FBaEMvQixxREFBcUQ7UUFDOUMsb0JBQWUsR0FBYSxFQUFFLENBQUM7UUFFdEMsZ0RBQWdEO1FBQ3pDLHFCQUFnQixHQUFhLEVBQUUsQ0FBQztRQUV2QyxpREFBaUQ7UUFDMUMsbUJBQWMsR0FBYSxFQUFFLENBQUM7UUFFckMsMERBQTBEO1FBQ25ELG1CQUFjLEdBQW9CLEVBQUUsQ0FBQztRQUVyQyxXQUFNLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBRTNDLHlCQUFvQixHQUFHLEVBQUUsQ0FBQztRQW9CL0IsSUFBSSxDQUFDLG1CQUFtQixDQUFDLG1CQUFtQixFQUFFLGtCQUFrQixDQUFDLENBQUM7UUFDbEUsSUFBSSxDQUFDLG1CQUFtQixDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQ3hDLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO1FBQzVCLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUM3QyxDQUFDO0lBRU8sbUJBQW1CLENBQUMsbUJBQTJCLEVBQUUsa0JBQTJCO1FBQ2xGLElBQUksQ0FBQyxvQkFBb0Is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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/qenv",
3
- "version": "8.0.0",
3
+ "version": "8.1.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",
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
- Throws `QenvMissingRequiredEnvVarsError` when `failOnMissing` is true and a name listed under
367
- `required:` is not provided by any source.
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.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/qenv',
6
- version: '8.0.0',
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
  }
package/ts/index.ts CHANGED
@@ -1,2 +1,4 @@
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';
@@ -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
+ }
@@ -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. Pass false to only record the names in
40
- * `missingEnvVars` and continue.
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.logger.log('warn', 'qenv.yml does not contain a "required" Array!');
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