@dotenc/cli 0.3.4 → 0.4.2

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.
@@ -1,89 +0,0 @@
1
- /**
2
- * Parses a .env file content and returns an object of key-value pairs.
3
- * Supports multiline values when they are properly quoted.
4
- * Supports nested quotes (e.g., "value with 'quotes' inside" or 'value with "quotes" inside')
5
- */
6
- export const parseEnv = (content) => {
7
- const env = {};
8
- let currentKey = "";
9
- let currentValue = "";
10
- let isInSingleQuotes = false;
11
- let isInDoubleQuotes = false;
12
- let isInComment = false;
13
- let isInKey = true;
14
- const commit = (trim) => {
15
- if (currentKey.trim()) {
16
- env[currentKey.trim()] = trim ? currentValue.trim() : currentValue;
17
- }
18
- currentKey = "";
19
- currentValue = "";
20
- isInSingleQuotes = false;
21
- isInDoubleQuotes = false;
22
- isInComment = false;
23
- isInKey = true;
24
- };
25
- for (const char of content) {
26
- // Handle single quoted content
27
- if (isInSingleQuotes) {
28
- if (char === "'") {
29
- commit(false);
30
- }
31
- else {
32
- currentValue += char;
33
- }
34
- continue;
35
- }
36
- // Handle double quoted content
37
- if (isInDoubleQuotes) {
38
- if (char === '"') {
39
- commit(false);
40
- }
41
- else {
42
- currentValue += char;
43
- }
44
- continue;
45
- }
46
- // Handle comments
47
- if (isInComment) {
48
- if (char === "\n") {
49
- isInComment = false;
50
- }
51
- continue;
52
- }
53
- if (char === "#") {
54
- isInComment = true;
55
- continue;
56
- }
57
- // Handle keys
58
- if (isInKey) {
59
- if (char === "=") {
60
- isInKey = false;
61
- }
62
- else {
63
- currentKey += char;
64
- }
65
- continue;
66
- }
67
- // Handle newlines
68
- if (char === "\n") {
69
- commit(true);
70
- }
71
- // Handle single quote opening
72
- if (char === "'") {
73
- currentValue = "";
74
- isInSingleQuotes = true;
75
- continue;
76
- }
77
- // Handle double quote opening
78
- if (char === '"') {
79
- currentValue = "";
80
- isInDoubleQuotes = true;
81
- continue;
82
- }
83
- // Handle value
84
- currentValue += char;
85
- }
86
- // Handle EOF
87
- commit(true);
88
- return env;
89
- };
@@ -1,21 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
4
- import { z } from "zod";
5
- const projectConfigSchema = z.object({
6
- projectId: z.string(),
7
- });
8
- const configPath = path.join(process.cwd(), "dotenc.json");
9
- export const setProjectConfig = async (config) => {
10
- const parsedConfig = projectConfigSchema.parse(config);
11
- await fs.writeFile(configPath, JSON.stringify(parsedConfig, null, 2), {
12
- mode: 0o600,
13
- });
14
- };
15
- export const getProjectConfig = async () => {
16
- if (existsSync(configPath)) {
17
- const config = JSON.parse(await fs.readFile(configPath, "utf-8"));
18
- return projectConfigSchema.parse(config);
19
- }
20
- return {};
21
- };
package/dist/program.js DELETED
@@ -1,62 +0,0 @@
1
- import { Command, Option } from "commander";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { configCommand } from "./commands/config.js";
6
- import { debugCommand } from "./commands/debug.js";
7
- import { editCommand } from "./commands/edit.js";
8
- import { initCommand } from "./commands/init.js";
9
- import { keyExportCommand } from "./commands/key/export.js";
10
- import { keyImportCommand } from "./commands/key/import.js";
11
- import { keyRotateCommand } from "./commands/key/rotate.js";
12
- import { runCommand } from "./commands/run.js";
13
- const __filename = fileURLToPath(import.meta.url);
14
- const __dirname = path.dirname(__filename);
15
- const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "../package.json"), "utf8"));
16
- const program = new Command();
17
- program.name("dotenc").description(pkg.description).version(pkg.version);
18
- if (process.env.NODE_ENV !== "production") {
19
- program.command("debug").description("debug the CLI").action(debugCommand);
20
- }
21
- program
22
- .command("init")
23
- .argument("[environment]", "the environment to initialize")
24
- .description("initialize a new environment")
25
- .action(initCommand);
26
- program
27
- .command("edit")
28
- .argument("[environment]", "the environment to edit")
29
- .description("edit an environment")
30
- .action(editCommand);
31
- program
32
- .command("run")
33
- .argument("<command>", "the command to run")
34
- .argument("[args...]", "the arguments to pass to the command")
35
- .addOption(new Option("-e, --env <env1>[,env2[,...]]", "the environments to run the command in"))
36
- .description("run a command in an environment")
37
- .action(runCommand);
38
- const key = program.command("key").description("manage stored keys");
39
- key
40
- .command("import")
41
- .argument("[environment]", "the environment to import the key to")
42
- .argument("[key]", "the key to import")
43
- .description("import a key for an environment")
44
- .action(keyImportCommand);
45
- key
46
- .command("export")
47
- .argument("[environment]", "the environment to export the key from")
48
- .description("export a key from an environment")
49
- .action(keyExportCommand);
50
- key
51
- .command("rotate")
52
- .argument("[environment]", "the environment to rotate the key for")
53
- .description("rotate a key for an environment")
54
- .action(keyRotateCommand);
55
- program
56
- .command("config")
57
- .argument("<key>", "the key to get or set")
58
- .argument("[value]", "the value to set the key to")
59
- .addOption(new Option("-r, --remove", "remove a configuration key"))
60
- .description("manage global configuration")
61
- .action(configCommand);
62
- program.parse();
@@ -1,18 +0,0 @@
1
- import inquirer from "inquirer";
2
- import fs from "node:fs/promises";
3
- export const chooseEnvironmentPrompt = async (message) => {
4
- const files = await fs.readdir(process.cwd());
5
- const envFiles = files.filter((file) => file.startsWith(".env.") && file.endsWith(".enc"));
6
- if (!envFiles.length) {
7
- console.log('No environment files found. To create a new environment, run "dotenc init"');
8
- }
9
- const result = await inquirer.prompt([
10
- {
11
- type: "list",
12
- name: "environment",
13
- message,
14
- choices: envFiles.map((file) => file.replace(".env.", "").replace(".enc", "")),
15
- },
16
- ]);
17
- return result.environment;
18
- };
@@ -1,12 +0,0 @@
1
- import inquirer from "inquirer";
2
- export const createEnvironmentPrompt = async (message, defaultValue) => {
3
- const result = await inquirer.prompt([
4
- {
5
- type: "input",
6
- name: "environment",
7
- message,
8
- default: defaultValue,
9
- },
10
- ]);
11
- return result.environment;
12
- };
@@ -1,13 +0,0 @@
1
- import inquirer from "inquirer";
2
- export const inputKeyPrompt = async (message, defaultValue) => {
3
- const result = await inquirer.prompt([
4
- {
5
- type: "password",
6
- name: "key",
7
- mask: "*",
8
- message,
9
- default: defaultValue,
10
- },
11
- ]);
12
- return result.key;
13
- };
@@ -1,64 +0,0 @@
1
- import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { afterAll, beforeAll, describe, expect, test, vi } from "vitest";
5
- import { editCommand } from "../commands/edit.js";
6
- import { initCommand } from "../commands/init.js";
7
- import { keyRotateCommand } from "../commands/key/rotate.js";
8
- import { runCommand } from "../commands/run.js";
9
- import { getKey } from "../helpers/key.js";
10
- import { cleanupProjectKeys } from "./helpers/cleanupProjectKeys.js";
11
- import { waitForFile } from "./helpers/waitForFile.js";
12
- const localEnvFilePath = path.join(process.cwd(), ".env");
13
- const encryptedEnvFilePath = path.join(process.cwd(), ".env.test.enc");
14
- const projectFilePath = path.join(process.cwd(), "dotenc.json");
15
- const outputFilePath = path.join(process.cwd(), "e2e.txt");
16
- vi.mock("node:child_process", async (importOriginal) => {
17
- const actual = await importOriginal();
18
- return {
19
- ...actual,
20
- // Mock for the edit command
21
- execSync: () => {
22
- const tempFilePath = path.join(os.tmpdir(), ".env.test");
23
- writeFileSync(tempFilePath, "DOTENC_HELLO=Hello, world!");
24
- },
25
- };
26
- });
27
- describe("e2e", () => {
28
- beforeAll(() => {
29
- vi.spyOn(console, "log").mockImplementation(() => { });
30
- vi.spyOn(process, "exit").mockImplementation(() => ({}));
31
- });
32
- test("should initialize an environment", async () => {
33
- await initCommand("test");
34
- expect(existsSync(localEnvFilePath)).toBe(true);
35
- expect(existsSync(encryptedEnvFilePath)).toBe(true);
36
- expect(existsSync(projectFilePath)).toBe(true);
37
- });
38
- test("should edit an environment", async () => {
39
- const initialContent = readFileSync(encryptedEnvFilePath, "utf-8");
40
- await editCommand("test");
41
- const editedContent = readFileSync(encryptedEnvFilePath, "utf-8");
42
- expect(editedContent).not.toBe(initialContent);
43
- });
44
- test("should rotate a key", async () => {
45
- const currentKey = await getKey("test");
46
- await keyRotateCommand("test");
47
- const rotatedKey = await getKey("test");
48
- expect(rotatedKey).not.toBe(currentKey);
49
- });
50
- test("should run a command in an environment", async () => {
51
- await runCommand("sh", [path.join(__dirname, "helpers", "e2e.sh")], {
52
- env: "test",
53
- });
54
- const output = await waitForFile(outputFilePath);
55
- expect(output).toBe("Hello, world!\n");
56
- });
57
- afterAll(async () => {
58
- await cleanupProjectKeys();
59
- unlinkSync(localEnvFilePath);
60
- unlinkSync(encryptedEnvFilePath);
61
- unlinkSync(projectFilePath);
62
- unlinkSync(outputFilePath);
63
- });
64
- });
@@ -1,13 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { getProjectConfig } from "../../helpers/projectConfig.js";
5
- export const cleanupProjectKeys = async () => {
6
- const { projectId } = await getProjectConfig();
7
- const keysFile = path.join(os.homedir(), ".dotenc", "keys.json");
8
- if (existsSync(keysFile)) {
9
- const keys = JSON.parse(readFileSync(keysFile, "utf-8"));
10
- delete keys[projectId];
11
- writeFileSync(keysFile, JSON.stringify(keys, null, 2));
12
- }
13
- };
@@ -1,15 +0,0 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- export const waitForFile = (filePath, timeout = 5000) => new Promise((resolve, reject) => {
3
- const startTime = Date.now();
4
- const interval = setInterval(() => {
5
- if (existsSync(filePath)) {
6
- clearInterval(interval);
7
- resolve(readFileSync(filePath, "utf-8"));
8
- return;
9
- }
10
- if (Date.now() - startTime > timeout) {
11
- clearInterval(interval);
12
- reject(new Error(`Timeout waiting for file ${filePath}`));
13
- }
14
- }, 100);
15
- });
@@ -1,30 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { parseEnv } from "../helpers/parseEnv.js";
3
- describe("parseEnv", () => {
4
- test("should parse a simple key-value pair", () => {
5
- const env = parseEnv("FOO=bar");
6
- expect(env.FOO).toBe("bar");
7
- });
8
- test("should parse a key-value pair with a space", () => {
9
- const env = parseEnv("FOO = bar");
10
- expect(env.FOO).toBe("bar");
11
- });
12
- test("should parse a complex env file", () => {
13
- const env = parseEnv(`FOO="
14
- bar"
15
- BAR='baz
16
- foo
17
- 'BAZ=123
18
-
19
- # Comment here "!#' foo
20
-
21
- HELLO = WORLD
22
- DOTENC_HELLO = "Hello, world!"
23
- `);
24
- expect(env.FOO).toBe("\nbar");
25
- expect(env.BAR).toBe("baz\nfoo\n");
26
- expect(env.BAZ).toBe("123");
27
- expect(env.HELLO).toBe("WORLD");
28
- expect(env.DOTENC_HELLO).toBe("Hello, world!");
29
- });
30
- });