no-shot / env
Showing 9 of 27 files from the diff.

@@ -11,9 +11,9 @@
Loading
11 11
 * @param env - the environment to be loaded
12 12
 * @param dir - the directory where the config is located
13 13
 * @returns a config file as { key: value } pairs to be used with the `config` function
14 -
 * @example load("development")
14 +
 * @example load("development");
15 15
 */
16 -
export default function load(env: string, dir?: string): ConfigOptions {
16 +
export function load(env: string, dir?: string): ConfigOptions {
17 17
  try {
18 18
    const configName = "env.config.json";
19 19
    const configPath = getFilePath(configName, dir);
@@ -52,3 +52,5 @@
Loading
52 52
    return {} as ConfigOptions;
53 53
  }
54 54
}
55 +
56 +
export default load;

@@ -12,15 +12,15 @@
Loading
12 12
 *
13 13
 * @param options - accepts: { `dir`: string, `paths`: string | string[], `encoding`: BufferEncoding, `override`: boolean | string, `debug`: boolean | string }
14 14
 * @returns a single object with `parsed` and `extracted` Envs as { KEY: "value" } pairs
15 -
 * @example config({ dir: "example", paths: ".env" })
15 +
 * @example config({ dir: "example", paths: ".env" });
16 16
 */
17 -
export default function config(options?: ConfigOptions): ConfigOutput {
17 +
export function config(options?: ConfigOptions): ConfigOutput {
18 18
  // default config options
19 19
  let dir: ConfigOptions["dir"] = process.cwd();
20 20
  let paths: ConfigOptions["paths"] = [".env"];
21 21
  let debug: ConfigOptions["debug"];
22 22
  let override: ConfigOptions["override"];
23 -
  let encoding: ConfigOptions["encoding"] = "utf-8";
23 +
  let encoding: ConfigOptions["encoding"] = "utf8";
24 24
25 25
  // override default options with config options arguments
26 26
  if (options) {
@@ -62,3 +62,5 @@
Loading
62 62
    extracted
63 63
  };
64 64
}
65 +
66 +
export default config;

@@ -0,0 +1,31 @@
Loading
1 +
import { createCipheriv, randomBytes } from "crypto";
2 +
import type { CryptOptions, EncryptResult } from "../index";
3 +
4 +
/**
5 +
 * Converts a stringified JSON object into a single encrypted string using {@link https://nodejs.org/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options `crypto.createCipheriv`}.
6 +
 *
7 +
 * Example: Encrypts `"{ "KEY": "VALUE" }"` to `"b8cb1867e4a8248c839db9cb0f1e1d"`
8 +
 *
9 +
 * @param options - accepts an object with the following properties: { `algorithm`: string, `envs`: stringified JSON or Buffer, `encoding`: Encoding, `input`: Encoding, `secret`: string }
10 +
 * @returns an object with the following two properties: `encryptedEvs`: encrypted string and `iv`: a random string
11 +
 * @example encrypt({ algorithm: "aes-256-cbc", envs: JSON.stringify({ "KEY": "VALUE" }), encoding: "utf8", input: "hex", secret: "abcdefghijklmnopqrstuv1234567890" });
12 +
 */
13 +
export function encrypt({
14 +
  algorithm,
15 +
  envs,
16 +
  encoding,
17 +
  input,
18 +
  secret
19 +
}: CryptOptions): EncryptResult {
20 +
  const iv = randomBytes(16).toString("hex").slice(0, 16);
21 +
22 +
  const encrypter = createCipheriv(algorithm, secret, iv);
23 +
24 +
  const encryptedEvs = encrypter
25 +
    .update(envs.toString(encoding), encoding, input)
26 +
    .concat(encrypter.final(input));
27 +
28 +
  return { encryptedEvs, iv };
29 +
}
30 +
31 +
export default encrypt;

@@ -28,9 +28,10 @@
Loading
28 28
 */
29 29
import { readFileSync } from "fs";
30 30
import { execSync } from "child_process";
31 +
import decrypt from "../decrypt";
31 32
import { logWarning } from "../log";
32 33
import fileExists from "../fileExists";
33 -
import type { Option, ParsedEnvs } from "../index";
34 +
import type { DecryptOptions, Option, ParsedEnvs } from "../index";
34 35
35 36
/**
36 37
 * Parses a string or buffer of Envs into an object.
@@ -38,12 +39,10 @@
Loading
38 39
 * @param src - contents to be parsed (string | Buffer)
39 40
 * @param override - allows extracted Envs to be parsed regardless if `process.env` has the properties defined (boolean | string)
40 41
 * @returns a single object of all { key: value } pairs from `src`
41 -
 * @example parse(Buffer.from("JUSTICE=league\n"))
42 +
 * @example parse(Buffer.from("JUSTICE=league\n"));
42 43
 */
43 -
export default function parse(
44 -
  src: string | Buffer,
45 -
  override?: Option
46 -
): ParsedEnvs {
44 +
export function parse(src: string | Buffer, override?: Option): ParsedEnvs {
45 +
  const { assign } = Object;
47 46
  const { env } = process;
48 47
  // initialize extracted Envs object
49 48
  const extracted: ParsedEnvs = {};
@@ -102,26 +101,64 @@
Loading
102 101
103 102
  // loops over key value pairs
104 103
  for (let i = 0; i < keyValues.length; i += 1) {
105 -
    const VAL = keyValues[i];
104 +
    const KEYVAL = keyValues[i];
106 105
    // finds matching "KEY' and 'VAL' in 'KEY=VAL'
107 -
    let keyValueArr = VAL.match(/^\s*([\w.-]+)\s*=\s*(.*)?\s*$/);
106 +
    let keyValueArr = KEYVAL.match(/^\s*([\w.-]+)\s*=\s*(.*)?\s*$/);
108 107
109 108
    // finds matching '# extends: path/to/.env'
110 109
    if (!keyValueArr) {
111 -
      keyValueArr = keyValues[i].match(/(?<=# extends: ).*/g);
110 +
      keyValueArr = KEYVAL.match(/(?<=# extends: ).*/g);
112 111
113 112
      // checks if there's a matching extension
114 113
      if (Array.isArray(keyValueArr)) {
115 114
        let extended = {};
115 +
116 +
        // destructure the path to the env from array
116 117
        const [envPath] = keyValueArr;
118 +
117 119
        // if the file exists, parse it...
118 120
        if (fileExists(envPath))
119 121
          extended = parse(readFileSync(envPath), override);
120 122
121 -
        // and assign it to extracted
122 -
        Object.assign(extracted, extended);
123 +
        // and assign any parsed Envs to extracted
124 +
        assign(extracted, extended);
125 +
126 +
        // remove "extends" line
127 +
        keyValueArr = null;
128 +
      }
129 +
    }
130 +
131 +
    if (!keyValueArr) {
132 +
      // finds matching '# uses: remoteurl, algorithm, input, encoding, secret, iv'
133 +
      keyValueArr = KEYVAL.match(/(?<=# uses: ).*/g);
134 +
135 +
      // checks if there's a match
136 +
      if (Array.isArray(keyValueArr)) {
137 +
        const [envRemotePath] = keyValueArr;
138 +
139 +
        // splits string by space
140 +
        const [remoteurl, algorithm, input, encoding, secret, iv] =
141 +
          envRemotePath.split(" ");
142 +
143 +
        // fetch encrypted string from remoteurl
144 +
        const envs = interpolate(`$(curl -s ${remoteurl})`);
145 +
146 +
        if (envs && typeof envs === "string") {
147 +
          // decrypt the encrypted string and convert stringified JSON to Env "KEY=value" pairs
148 +
          const { decryptedEnvs } = decrypt({
149 +
            algorithm,
150 +
            encoding,
151 +
            envs,
152 +
            input,
153 +
            iv,
154 +
            secret
155 +
          } as DecryptOptions);
156 +
157 +
          // assign Envs to extracted
158 +
          assign(extracted, parse(decryptedEnvs));
159 +
        }
123 160
124 -
        // remove extension value
161 +
        // remove "uses" line
125 162
        keyValueArr = null;
126 163
      }
127 164
    }
@@ -154,3 +191,5 @@
Loading
154 191
155 192
  return extracted;
156 193
}
194 +
195 +
export default parse;

@@ -3,13 +3,15 @@
Loading
3 3
/**
4 4
 * A utility function to check if a file exists.
5 5
 *
6 -
 * @param {string} file - filename
7 -
 * @returns {boolean} whether or not the file exists
6 +
 * @param file - filename
7 +
 * @returns a boolean: whether or not the file exists
8 8
 */
9 -
export default function fileExists(file: string): boolean {
9 +
function fileExists(file: string): boolean {
10 10
  try {
11 11
    return statSync(file).isFile();
12 12
  } catch (e) {
13 13
    return false;
14 14
  }
15 15
}
16 +
17 +
export default fileExists;

@@ -5,6 +5,8 @@
Loading
5 5
 *
6 6
 * @param config - ParsedEnvs
7 7
 */
8 -
export default function assignEnvs(config: ParsedEnvs): ProcessEnv {
8 +
export function assignEnvs(config: ParsedEnvs): ProcessEnv {
9 9
  return Object.assign(process.env, config) as ProcessEnv;
10 10
}
11 +
12 +
export default assignEnvs;

@@ -3,10 +3,12 @@
Loading
3 3
/**
4 4
 * A utility function to join a directory with a filename to a single string.
5 5
 *
6 -
 * @param {string} file - filename
7 -
 * @param {string} dir - directory of the file
8 -
 * @returns {string} a single file path string
6 +
 * @param file - filename
7 +
 * @param dir - directory of the file
8 +
 * @returns a single file path string
9 9
 */
10 -
export default function getFilePath(file: string, dir?: string): string {
10 +
export function getFilePath(file: string, dir?: string): string {
11 11
  return join(dir || process.cwd(), file);
12 12
}
13 +
14 +
export default getFilePath;

@@ -1,7 +1,35 @@
Loading
1 +
import type { CipherKey, Encoding } from "crypto";
2 +
import assign from "./assign";
1 3
import config from "./config";
4 +
import encrypt from "./encrypt";
5 +
import decrypt from "./decrypt";
2 6
import parse from "./parse";
3 7
import load from "./load";
4 -
import assign from "./assign";
8 +
9 +
export type { CipherKey, Encoding };
10 +
11 +
export interface CryptOptions {
12 +
  algorithm: string;
13 +
  envs: string | Buffer;
14 +
  encoding: Encoding;
15 +
  input: Encoding;
16 +
  secret: CipherKey;
17 +
}
18 +
19 +
export type EncryptResult = {
20 +
  encryptedEvs: string;
21 +
  iv: string;
22 +
};
23 +
24 +
export type DecryptResult = {
25 +
  decryptedEnvs: string;
26 +
  decryptedResult: any;
27 +
};
28 +
29 +
export interface DecryptOptions extends CryptOptions {
30 +
  envs: string;
31 +
  iv: string;
32 +
}
5 33
6 34
export interface ParsedEnvs {
7 35
  [name: string]: string;
@@ -44,11 +72,13 @@
Loading
44 72
  }
45 73
})();
46 74
47 -
export { assign, config, load, parse };
75 +
export { assign, config, decrypt, encrypt, load, parse };
48 76
49 77
const env = {
50 78
  assign,
51 79
  config,
80 +
  decrypt,
81 +
  encrypt,
52 82
  load,
53 83
  parse
54 84
};

@@ -0,0 +1,53 @@
Loading
1 +
import { createDecipheriv } from "crypto";
2 +
import type { DecryptOptions, DecryptResult } from "../index";
3 +
4 +
/**
5 +
 * Decrypts and converts a stringified JSON object/Buffer using {@link https://nodejs.org/api/crypto.html#crypto_crypto_createdecipheriv_algorithm_key_iv_options `crypto.createDecipheriv`}.
6 +
 *
7 +
 * Example: Decrypts `"b8cb1867e4a8248c839db9cb0f1e1d"` to `"KEY=value\nKEY2=value2"`
8 +
 *
9 +
 * @param options - accepts an object with the following properties: { `algorithm`: string, `envs`: string(encrypted), `encoding`: BufferEncoding, `input`: Encoding, `iv`: string, `secret`: string }
10 +
 * @returns an object with the following two properties: `decryptedEnvs`: a single string of "KEY=value" pairs and `decryptedResult`: original JSON object/Buffer string
11 +
 * @example decrypt({ algorithm: "aes-256-cbc", envs: "b8cb1867e4a8248c839db9cb0f1e1d", encoding: "utf8", input: "hex", iv: "05c6f2c47de0ecfe", secret: "abcdefghijklmnopqrstuv1234567890" });
12 +
 */
13 +
export function decrypt({
14 +
  algorithm,
15 +
  envs,
16 +
  encoding,
17 +
  input,
18 +
  iv,
19 +
  secret
20 +
}: DecryptOptions): DecryptResult {
21 +
  // decrypt string
22 +
  const decrypter = createDecipheriv(algorithm, secret, iv);
23 +
  const decryptedString = decrypter
24 +
    .update(envs, input, encoding)
25 +
    .concat(decrypter.final(encoding));
26 +
27 +
  const isJSON =
28 +
    decryptedString[0] === "{" &&
29 +
    decryptedString[decryptedString.length - 1] === "}";
30 +
31 +
  const decryptedResult = isJSON
32 +
    ? JSON.parse(decryptedString)
33 +
    : Buffer.from(decryptedString);
34 +
35 +
  const decryptedEnvs = isJSON
36 +
    ? decryptedString
37 +
        // remove first "{" and last "}"
38 +
        .replace(/^\{|\}$/gm, "")
39 +
        // replace commas with new lines
40 +
        .replace(/,/g, "\n")
41 +
        // remove all quotes
42 +
        .replace(/["']/g, "")
43 +
        // replace first occurence of ":" to "= " for each line
44 +
        .replace(/^([^:]*?)\s*:/gm, "$1=")
45 +
    : decryptedResult.toString();
46 +
47 +
  return {
48 +
    decryptedEnvs,
49 +
    decryptedResult
50 +
  };
51 +
}
52 +
53 +
export default decrypt;
Files Coverage
assign/index.ts 100.00%
config/index.ts 100.00%
decrypt/index.ts 100.00%
encrypt/index.ts 100.00%
fileExists/index.ts 100.00%
getFilePath/index.ts 100.00%
index.ts 100.00%
load/index.ts 100.00%
log/index.ts 100.00%
parse/index.ts 100.00%
Project Totals (10 files) 100.00%
Notifications are pending CI completion. Periodically Codecov will check the CI state, when complete notifications will be submitted. Push notifications now.

No yaml found.

Create your codecov.yml to customize your Codecov experience

Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading