@arki/env 0.0.11 → 0.2.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/CHANGELOG.md +196 -0
- package/README.md +231 -62
- package/dist/api.json +597 -0
- package/dist/core/define-env.d.ts +29 -4
- package/dist/core/define-env.d.ts.map +1 -1
- package/dist/core/define-env.js +85 -15
- package/dist/core/define-env.js.map +1 -1
- package/dist/core/describe.d.ts +29 -0
- package/dist/core/describe.d.ts.map +1 -0
- package/dist/core/describe.js +25 -0
- package/dist/core/describe.js.map +1 -0
- package/dist/core/errors.d.ts +10 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +13 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/index.d.ts +7 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +6 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/secret.d.ts +56 -0
- package/dist/core/secret.d.ts.map +1 -0
- package/dist/core/secret.js +85 -0
- package/dist/core/secret.js.map +1 -0
- package/dist/errors.json +128 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/node/codec.d.ts +63 -0
- package/dist/node/codec.d.ts.map +1 -0
- package/dist/node/codec.js +182 -0
- package/dist/node/codec.js.map +1 -0
- package/dist/node/edit.d.ts +112 -0
- package/dist/node/edit.d.ts.map +1 -0
- package/dist/node/edit.js +268 -0
- package/dist/node/edit.js.map +1 -0
- package/dist/node/errors.d.ts +119 -0
- package/dist/node/errors.d.ts.map +1 -0
- package/dist/node/errors.js +144 -0
- package/dist/node/errors.js.map +1 -0
- package/dist/node/files.d.ts +9 -0
- package/dist/node/files.d.ts.map +1 -0
- package/dist/node/files.js +35 -0
- package/dist/node/files.js.map +1 -0
- package/dist/node/index.d.ts +20 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +13 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/key-schemas.d.ts +25 -0
- package/dist/node/key-schemas.d.ts.map +1 -0
- package/dist/node/key-schemas.js +38 -0
- package/dist/node/key-schemas.js.map +1 -0
- package/dist/node/keys.d.ts +54 -0
- package/dist/node/keys.d.ts.map +1 -0
- package/dist/node/keys.js +76 -0
- package/dist/node/keys.js.map +1 -0
- package/dist/node/load.d.ts +103 -0
- package/dist/node/load.d.ts.map +1 -0
- package/dist/node/load.js +306 -0
- package/dist/node/load.js.map +1 -0
- package/dist/node/operation-telemetry.d.ts +3 -0
- package/dist/node/operation-telemetry.d.ts.map +1 -0
- package/dist/node/operation-telemetry.js +53 -0
- package/dist/node/operation-telemetry.js.map +1 -0
- package/dist/node/references.d.ts +16 -0
- package/dist/node/references.d.ts.map +1 -0
- package/dist/node/references.js +72 -0
- package/dist/node/references.js.map +1 -0
- package/dist/node/register.d.ts +2 -0
- package/dist/node/register.d.ts.map +1 -0
- package/dist/node/register.js +19 -0
- package/dist/node/register.js.map +1 -0
- package/dist/{contracts → presets}/common.d.ts +1 -1
- package/dist/presets/common.d.ts.map +1 -0
- package/dist/{contracts → presets}/common.js +4 -8
- package/dist/presets/common.js.map +1 -0
- package/dist/{contracts → presets}/index.d.ts +5 -5
- package/dist/presets/index.d.ts.map +1 -0
- package/dist/presets/index.js +15 -0
- package/dist/presets/index.js.map +1 -0
- package/dist/{contracts → presets}/parsing.d.ts +2 -2
- package/dist/presets/parsing.d.ts.map +1 -0
- package/dist/{contracts → presets}/parsing.js +13 -5
- package/dist/presets/parsing.js.map +1 -0
- package/dist/{contracts → presets}/urls.d.ts +1 -1
- package/dist/presets/urls.d.ts.map +1 -0
- package/dist/{contracts → presets}/urls.js +1 -1
- package/dist/presets/urls.js.map +1 -0
- package/docs/agent-guide.md +121 -0
- package/docs/upgrades/0.0.11-to-0.1.0.md +62 -0
- package/docs/upgrades/0.1.0-to-0.2.0.md +73 -0
- package/docs/upgrades/typed-node-outcomes.md +13 -0
- package/llms.txt +30 -0
- package/package.json +27 -20
- package/src/core/define-env.ts +101 -20
- package/src/core/describe.ts +52 -0
- package/src/core/errors.ts +14 -0
- package/src/core/index.ts +7 -2
- package/src/core/secret.ts +127 -0
- package/src/index.ts +3 -2
- package/src/node/codec.ts +220 -0
- package/src/node/edit.ts +279 -0
- package/src/node/errors.ts +147 -0
- package/src/node/files.ts +41 -0
- package/src/node/index.ts +48 -0
- package/src/node/key-schemas.ts +44 -0
- package/src/node/keys.ts +93 -0
- package/src/node/load.ts +386 -0
- package/src/node/operation-telemetry.ts +56 -0
- package/src/node/references.ts +90 -0
- package/src/node/register.ts +18 -0
- package/src/{contracts → presets}/common.ts +4 -8
- package/src/presets/index.ts +18 -0
- package/src/{contracts → presets}/parsing.ts +12 -5
- package/src/{contracts → presets}/urls.ts +1 -1
- package/dist/contracts/common.d.ts.map +0 -1
- package/dist/contracts/common.js.map +0 -1
- package/dist/contracts/index.d.ts.map +0 -1
- package/dist/contracts/index.js +0 -14
- package/dist/contracts/index.js.map +0 -1
- package/dist/contracts/parsing.d.ts.map +0 -1
- package/dist/contracts/parsing.js.map +0 -1
- package/dist/contracts/urls.d.ts.map +0 -1
- package/dist/contracts/urls.js.map +0 -1
- package/dist/dot.d.ts +0 -67
- package/dist/dot.d.ts.map +0 -1
- package/dist/dot.js +0 -65
- package/dist/dot.js.map +0 -1
- package/src/contracts/index.ts +0 -17
- package/src/dot.ts +0 -94
package/dist/errors.json
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"code": "env.cancelled",
|
|
4
|
+
"package": "@arki/env",
|
|
5
|
+
"kind": "failure",
|
|
6
|
+
"what": "Loading was cancelled.",
|
|
7
|
+
"fix": "Handle cancellation at the calling boundary.",
|
|
8
|
+
"verify": "bun run test",
|
|
9
|
+
"source": "src/node/errors.ts:108"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"code": "env.deadline",
|
|
13
|
+
"package": "@arki/env",
|
|
14
|
+
"kind": "failure",
|
|
15
|
+
"what": "The aggregate load deadline elapsed.",
|
|
16
|
+
"fix": "Increase the budget or restore provider responsiveness.",
|
|
17
|
+
"verify": "bun run test",
|
|
18
|
+
"source": "src/node/errors.ts:117"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"code": "env.decryption_failed",
|
|
22
|
+
"package": "@arki/env",
|
|
23
|
+
"kind": "failure",
|
|
24
|
+
"what": "Ciphertext authentication or decoding failed.",
|
|
25
|
+
"fix": "Restore the correct private key or reset the damaged value.",
|
|
26
|
+
"verify": "bun run test",
|
|
27
|
+
"source": "src/node/errors.ts:27"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"code": "env.encryption_failed",
|
|
31
|
+
"package": "@arki/env",
|
|
32
|
+
"kind": "failure",
|
|
33
|
+
"what": "Cryptographic encryption failed.",
|
|
34
|
+
"fix": "Verify the public key and runtime crypto support.",
|
|
35
|
+
"verify": "bun run test",
|
|
36
|
+
"source": "src/node/errors.ts:18"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"code": "env.file_read_failed",
|
|
40
|
+
"package": "@arki/env",
|
|
41
|
+
"kind": "failure",
|
|
42
|
+
"what": "An env file could not be read.",
|
|
43
|
+
"fix": "Check the path, file type and access permissions.",
|
|
44
|
+
"verify": "bun run test",
|
|
45
|
+
"source": "src/node/errors.ts:45"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"code": "env.file_write_failed",
|
|
49
|
+
"package": "@arki/env",
|
|
50
|
+
"kind": "failure",
|
|
51
|
+
"what": "An env file could not be replaced.",
|
|
52
|
+
"fix": "Check disk space and parent-directory permissions.",
|
|
53
|
+
"verify": "bun run test",
|
|
54
|
+
"source": "src/node/errors.ts:55"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"code": "env.files_failed",
|
|
58
|
+
"package": "@arki/env",
|
|
59
|
+
"kind": "failure",
|
|
60
|
+
"what": "One or more env entries could not be loaded. Public data excludes values and raw causes.",
|
|
61
|
+
"fix": "Address each named problem before starting the app.",
|
|
62
|
+
"verify": "bun run test",
|
|
63
|
+
"source": "src/node/errors.ts:142"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"code": "env.invalid_environment",
|
|
67
|
+
"package": "@arki/env",
|
|
68
|
+
"kind": "failure",
|
|
69
|
+
"what": "The process environment does not match the schema, so the app must not boot.",
|
|
70
|
+
"fix": "Set or correct the variables listed in the report, then start the app again.",
|
|
71
|
+
"verify": "bun run test -- src/core/define-env.test.ts",
|
|
72
|
+
"source": "src/core/errors.ts:9"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"code": "env.key_generation_failed",
|
|
76
|
+
"package": "@arki/env",
|
|
77
|
+
"kind": "failure",
|
|
78
|
+
"what": "The runtime could not generate a keypair.",
|
|
79
|
+
"fix": "Restore runtime crypto support.",
|
|
80
|
+
"verify": "bun run test",
|
|
81
|
+
"source": "src/node/errors.ts:36"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"code": "env.missing_private_key",
|
|
85
|
+
"package": "@arki/env",
|
|
86
|
+
"kind": "failure",
|
|
87
|
+
"what": "No private key is available.",
|
|
88
|
+
"fix": "Restore the environment key from its backup.",
|
|
89
|
+
"verify": "bun run test",
|
|
90
|
+
"source": "src/node/errors.ts:74"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"code": "env.missing_public_key",
|
|
94
|
+
"package": "@arki/env",
|
|
95
|
+
"kind": "failure",
|
|
96
|
+
"what": "The target file has no public key.",
|
|
97
|
+
"fix": "Initialize the file before encrypting values.",
|
|
98
|
+
"verify": "bun run test",
|
|
99
|
+
"source": "src/node/errors.ts:65"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"code": "env.reference_failed",
|
|
103
|
+
"package": "@arki/env",
|
|
104
|
+
"kind": "failure",
|
|
105
|
+
"what": "A secret provider rejected a read.",
|
|
106
|
+
"fix": "Check the provider CLI installation, login and reference.",
|
|
107
|
+
"verify": "bun run test",
|
|
108
|
+
"source": "src/node/errors.ts:98"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"code": "env.rotation_incomplete",
|
|
112
|
+
"package": "@arki/env",
|
|
113
|
+
"kind": "failure",
|
|
114
|
+
"what": "Rotation stopped after its recovery keys were saved.",
|
|
115
|
+
"fix": "Keep the staged key file and retry rotation.",
|
|
116
|
+
"verify": "bun run test",
|
|
117
|
+
"source": "src/node/errors.ts:83"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"code": "env.unrepresentable_value",
|
|
121
|
+
"package": "@arki/env",
|
|
122
|
+
"kind": "failure",
|
|
123
|
+
"what": "A dotenv value has no lossless quoting.",
|
|
124
|
+
"fix": "Use encryption or a value supported by dotenv quoting.",
|
|
125
|
+
"verify": "bun run test",
|
|
126
|
+
"source": "src/node/errors.ts:9"
|
|
127
|
+
}
|
|
128
|
+
]
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAEhF,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
1
|
+
/** The `presets` namespace of ready-made variable shapes, for `defineEnv()`. */
|
|
2
|
+
// Re-export presets for convenience
|
|
3
|
+
export { presets } from './presets/index.js';
|
|
3
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,oCAAoC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { EnvPrivateKey, EnvPublicKey } from './key-schemas.js';
|
|
2
|
+
export { ENCRYPTED_PREFIX } from '../core/define-env.js';
|
|
3
|
+
/** One `KEY=value` assignment: its decoded value and the lines it spans (`end` exclusive). */
|
|
4
|
+
export type EnvEntry = {
|
|
5
|
+
readonly key: string;
|
|
6
|
+
readonly value: string;
|
|
7
|
+
readonly start: number;
|
|
8
|
+
readonly end: number;
|
|
9
|
+
};
|
|
10
|
+
/** A parsed `.env` file: its lines as written, and every assignment in order. */
|
|
11
|
+
export type EnvDocument = {
|
|
12
|
+
readonly lines: readonly string[];
|
|
13
|
+
readonly entries: readonly EnvEntry[];
|
|
14
|
+
};
|
|
15
|
+
/** A keypair as dotenvx writes it: a compressed public key (66 hex chars), a private key (64). */
|
|
16
|
+
export type EnvKeypair = {
|
|
17
|
+
readonly publicKey: EnvPublicKey;
|
|
18
|
+
readonly privateKey: EnvPrivateKey;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Parse `.env` text the way `dotenv` reads it: `export` prefixes, `#` comments, single, double and
|
|
22
|
+
* backtick quotes, multi-line quoted values, and `\n` / `\r` expanded inside double quotes. An
|
|
23
|
+
* unquoted value ends at the first `#`. A key that appears twice keeps both entries; readers take the
|
|
24
|
+
* last one, as dotenv does.
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseEnv(text: string): EnvDocument;
|
|
27
|
+
/** The last value of `key`, or `undefined`. */
|
|
28
|
+
export declare function readEnvValue(document: EnvDocument, key: string): string | undefined;
|
|
29
|
+
/** Every key with its last value, in first-seen order. */
|
|
30
|
+
export declare function envValues(document: EnvDocument): Map<string, EnvEntry>;
|
|
31
|
+
/**
|
|
32
|
+
* The text with `key` set to `value`. An existing assignment (the last one) is rewritten in place;
|
|
33
|
+
* a new key is appended. Every other line is kept as written.
|
|
34
|
+
*/
|
|
35
|
+
export declare function setEnvValue(text: string, key: string, value: string): string | import("@arki/failure").Failure<"env.unrepresentable_value", undefined>;
|
|
36
|
+
/** The text without any assignment of `key`. */
|
|
37
|
+
export declare function removeEnvValue(text: string, key: string): string;
|
|
38
|
+
/** True for a value written by {@link encryptValue} (or by dotenvx). */
|
|
39
|
+
export declare function isEncrypted(value: string): boolean;
|
|
40
|
+
/** Encrypt `value` to a compressed or uncompressed secp256k1 public key (hex). */
|
|
41
|
+
export declare function encryptValue(value: string, publicKey: EnvPublicKey): string | import("@arki/failure").Failure<"env.encryption_failed", undefined>;
|
|
42
|
+
/** Decrypt authenticated UTF-8, or return DecryptionFailed when the ciphertext/key does not match. */
|
|
43
|
+
export declare function decryptValue(value: string, privateKey: EnvPrivateKey): string | import("@arki/failure").Failure<"env.decryption_failed", undefined>;
|
|
44
|
+
/** A new random keypair. */
|
|
45
|
+
export declare function generateKeypair(): import("@arki/failure").Failure<"env.key_generation_failed", undefined> | {
|
|
46
|
+
publicKey: EnvPublicKey;
|
|
47
|
+
privateKey: EnvPrivateKey;
|
|
48
|
+
};
|
|
49
|
+
/** The compressed public key of an already validated private key. */
|
|
50
|
+
export declare function publicKeyOf(privateKey: EnvPrivateKey): EnvPublicKey;
|
|
51
|
+
/**
|
|
52
|
+
* The key names dotenvx uses for a file: `.env` → `DOTENV_PUBLIC_KEY` / `DOTENV_PRIVATE_KEY`,
|
|
53
|
+
* `.env.production` → `..._PRODUCTION`, `.env.ci.local` → `..._CI_LOCAL`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function keyNamesFor(fileName: string): {
|
|
56
|
+
readonly publicKey: string;
|
|
57
|
+
readonly privateKey: string;
|
|
58
|
+
};
|
|
59
|
+
/** True for the `DOTENV_PUBLIC_KEY*` / `DOTENV_PRIVATE_KEY*` names, which are never app values. */
|
|
60
|
+
export declare function isKeyName(key: string): boolean;
|
|
61
|
+
/** The dotenvx header for a file: banner, public key, and a `# <file>` line. */
|
|
62
|
+
export declare function envHeader(fileName: string, publicKey: EnvPublicKey): string;
|
|
63
|
+
//# sourceMappingURL=codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/node/codec.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAKpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAQzD,8FAA8F;AAC9F,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,iFAAiF;AACjF,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;CACvC,CAAC;AAEF,kGAAkG;AAClG,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAA;CAAE,CAAC;AAElG;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAmBlD;AAED,+CAA+C;AAC/C,wBAAgB,YAAY,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEnF;AAED,0DAA0D;AAC1D,wBAAgB,SAAS,CAAC,QAAQ,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAItE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,oFAYnE;AAED,gDAAgD;AAChD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAQhE;AAED,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,gFAQlE;AAED,sGAAsG;AACtG,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,gFASpE;AAED,4BAA4B;AAC5B,wBAAgB,eAAe;;;EAW9B;AAED,qEAAqE;AACrE,wBAAgB,WAAW,CAAC,UAAU,EAAE,aAAa,GAAG,YAAY,CAEnE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CASzG;AAED,mGAAmG;AACnG,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,GAAG,MAAM,CAU3E"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dotenvx 2.x file format: parse and edit `.env` text without losing a byte of the lines you
|
|
3
|
+
* did not touch, and encrypt or decrypt one value.
|
|
4
|
+
*
|
|
5
|
+
* A value is encrypted as `encrypted:` + base64 of ECIES over secp256k1 (HKDF-SHA256, AES-256-GCM,
|
|
6
|
+
* an ephemeral key per value), exactly what `eciesjs` produces with its defaults. The same bytes are
|
|
7
|
+
* what dotenvx writes, so files move freely between `dot env` and the `dotenvx` CLI.
|
|
8
|
+
*/
|
|
9
|
+
import { Buffer } from 'node:buffer';
|
|
10
|
+
import { decrypt, encrypt, PrivateKey } from 'eciesjs';
|
|
11
|
+
import { Config } from 'eciesjs/config';
|
|
12
|
+
import { attempt, isFailure } from '@arki/failure';
|
|
13
|
+
import { ensure } from '@arki/schema';
|
|
14
|
+
import { ENCRYPTED_PREFIX } from '../core/define-env.js';
|
|
15
|
+
import { DecryptionFailed, EncryptionFailed, KeyGenerationFailed, UnrepresentableValue } from './errors.js';
|
|
16
|
+
import { envPrivateKeySchema, envPublicKeySchema } from './key-schemas.js';
|
|
17
|
+
export { ENCRYPTED_PREFIX } from '../core/define-env.js';
|
|
18
|
+
/** Pinned so a process that changes the global `ECIES_CONFIG` cannot change our bytes. */
|
|
19
|
+
const DOTENVX_ECIES = new Config();
|
|
20
|
+
const KEY_PATTERN = /^[ \t]*(?:export[ \t]+)?([\w.-]+)[ \t]*=(.*)$/;
|
|
21
|
+
const QUOTES = ['"', "'", '`'];
|
|
22
|
+
/**
|
|
23
|
+
* Parse `.env` text the way `dotenv` reads it: `export` prefixes, `#` comments, single, double and
|
|
24
|
+
* backtick quotes, multi-line quoted values, and `\n` / `\r` expanded inside double quotes. An
|
|
25
|
+
* unquoted value ends at the first `#`. A key that appears twice keeps both entries; readers take the
|
|
26
|
+
* last one, as dotenv does.
|
|
27
|
+
*/
|
|
28
|
+
export function parseEnv(text) {
|
|
29
|
+
const lines = text.replaceAll('\r\n', '\n').split('\n');
|
|
30
|
+
const entries = [];
|
|
31
|
+
for (let index = 0; index < lines.length; index++) {
|
|
32
|
+
const match = KEY_PATTERN.exec(lines[index] ?? '');
|
|
33
|
+
if (match === null)
|
|
34
|
+
continue;
|
|
35
|
+
const key = match[1] ?? '';
|
|
36
|
+
let raw = (match[2] ?? '').trimStart();
|
|
37
|
+
const start = index;
|
|
38
|
+
const quote = QUOTES.find(candidate => raw.startsWith(candidate));
|
|
39
|
+
if (quote !== undefined) {
|
|
40
|
+
while (closingQuote(raw, quote) === -1 && index + 1 < lines.length) {
|
|
41
|
+
index++;
|
|
42
|
+
raw += `\n${lines[index] ?? ''}`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
entries.push({ key, value: decodeValue(raw, quote), start, end: index + 1 });
|
|
46
|
+
}
|
|
47
|
+
return { lines, entries };
|
|
48
|
+
}
|
|
49
|
+
/** The last value of `key`, or `undefined`. */
|
|
50
|
+
export function readEnvValue(document, key) {
|
|
51
|
+
return document.entries.findLast(entry => entry.key === key)?.value;
|
|
52
|
+
}
|
|
53
|
+
/** Every key with its last value, in first-seen order. */
|
|
54
|
+
export function envValues(document) {
|
|
55
|
+
const values = new Map();
|
|
56
|
+
for (const entry of document.entries)
|
|
57
|
+
values.set(entry.key, entry);
|
|
58
|
+
return values;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The text with `key` set to `value`. An existing assignment (the last one) is rewritten in place;
|
|
62
|
+
* a new key is appended. Every other line is kept as written.
|
|
63
|
+
*/
|
|
64
|
+
export function setEnvValue(text, key, value) {
|
|
65
|
+
const document = parseEnv(text);
|
|
66
|
+
const encoded = encodeValue(value);
|
|
67
|
+
if (isFailure(encoded))
|
|
68
|
+
return encoded;
|
|
69
|
+
const line = `${key}=${encoded}`;
|
|
70
|
+
const existing = document.entries.findLast(entry => entry.key === key);
|
|
71
|
+
if (existing === undefined) {
|
|
72
|
+
const body = document.lines.join('\n');
|
|
73
|
+
const separator = body === '' || body.endsWith('\n') ? '' : '\n';
|
|
74
|
+
return `${body}${separator}${line}\n`;
|
|
75
|
+
}
|
|
76
|
+
return [...document.lines.slice(0, existing.start), line, ...document.lines.slice(existing.end)].join('\n');
|
|
77
|
+
}
|
|
78
|
+
/** The text without any assignment of `key`. */
|
|
79
|
+
export function removeEnvValue(text, key) {
|
|
80
|
+
const document = parseEnv(text);
|
|
81
|
+
const dropped = new Set();
|
|
82
|
+
for (const entry of document.entries) {
|
|
83
|
+
if (entry.key !== key)
|
|
84
|
+
continue;
|
|
85
|
+
for (let line = entry.start; line < entry.end; line++)
|
|
86
|
+
dropped.add(line);
|
|
87
|
+
}
|
|
88
|
+
return document.lines.filter((_, index) => !dropped.has(index)).join('\n');
|
|
89
|
+
}
|
|
90
|
+
/** True for a value written by {@link encryptValue} (or by dotenvx). */
|
|
91
|
+
export function isEncrypted(value) {
|
|
92
|
+
return value.startsWith(ENCRYPTED_PREFIX);
|
|
93
|
+
}
|
|
94
|
+
/** Encrypt `value` to a compressed or uncompressed secp256k1 public key (hex). */
|
|
95
|
+
export function encryptValue(value, publicKey) {
|
|
96
|
+
return attempt(() => {
|
|
97
|
+
const sealed = encrypt(publicKey, new TextEncoder().encode(value), DOTENVX_ECIES);
|
|
98
|
+
return `${ENCRYPTED_PREFIX}${Buffer.from(sealed).toString('base64')}`;
|
|
99
|
+
}, cause => EncryptionFailed({ cause }));
|
|
100
|
+
}
|
|
101
|
+
/** Decrypt authenticated UTF-8, or return DecryptionFailed when the ciphertext/key does not match. */
|
|
102
|
+
export function decryptValue(value, privateKey) {
|
|
103
|
+
if (!isEncrypted(value))
|
|
104
|
+
return DecryptionFailed();
|
|
105
|
+
return attempt(() => {
|
|
106
|
+
const sealed = Buffer.from(value.slice(ENCRYPTED_PREFIX.length), 'base64');
|
|
107
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(decrypt(privateKey, sealed, DOTENVX_ECIES));
|
|
108
|
+
}, cause => DecryptionFailed({ cause }));
|
|
109
|
+
}
|
|
110
|
+
/** A new random keypair. */
|
|
111
|
+
export function generateKeypair() {
|
|
112
|
+
return attempt(() => {
|
|
113
|
+
const secret = new PrivateKey(undefined, DOTENVX_ECIES.ellipticCurve);
|
|
114
|
+
return {
|
|
115
|
+
publicKey: ensure(secret.publicKey.toHex(true), envPublicKeySchema),
|
|
116
|
+
privateKey: ensure(secret.toHex(), envPrivateKeySchema),
|
|
117
|
+
};
|
|
118
|
+
}, cause => KeyGenerationFailed({ cause }));
|
|
119
|
+
}
|
|
120
|
+
/** The compressed public key of an already validated private key. */
|
|
121
|
+
export function publicKeyOf(privateKey) {
|
|
122
|
+
return ensure(PrivateKey.fromHex(privateKey, DOTENVX_ECIES.ellipticCurve).publicKey.toHex(true), envPublicKeySchema);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The key names dotenvx uses for a file: `.env` → `DOTENV_PUBLIC_KEY` / `DOTENV_PRIVATE_KEY`,
|
|
126
|
+
* `.env.production` → `..._PRODUCTION`, `.env.ci.local` → `..._CI_LOCAL`.
|
|
127
|
+
*/
|
|
128
|
+
export function keyNamesFor(fileName) {
|
|
129
|
+
const base = fileName.toLowerCase().replace(/\.txt$/, '');
|
|
130
|
+
const suffix = base.startsWith('.env.')
|
|
131
|
+
? `_${base
|
|
132
|
+
.slice('.env.'.length)
|
|
133
|
+
.replaceAll(/[^a-z0-9]+/g, '_')
|
|
134
|
+
.toUpperCase()}`
|
|
135
|
+
: '';
|
|
136
|
+
return { publicKey: `DOTENV_PUBLIC_KEY${suffix}`, privateKey: `DOTENV_PRIVATE_KEY${suffix}` };
|
|
137
|
+
}
|
|
138
|
+
/** True for the `DOTENV_PUBLIC_KEY*` / `DOTENV_PRIVATE_KEY*` names, which are never app values. */
|
|
139
|
+
export function isKeyName(key) {
|
|
140
|
+
return key.startsWith('DOTENV_PUBLIC_KEY') || key.startsWith('DOTENV_PRIVATE_KEY');
|
|
141
|
+
}
|
|
142
|
+
/** The dotenvx header for a file: banner, public key, and a `# <file>` line. */
|
|
143
|
+
export function envHeader(fileName, publicKey) {
|
|
144
|
+
return [
|
|
145
|
+
'#/-------------------[DOTENV_PUBLIC_KEY]--------------------/',
|
|
146
|
+
'#/ public-key encryption for .env files /',
|
|
147
|
+
'#/ [how it works](https://dotenvx.com/encryption) /',
|
|
148
|
+
'#/----------------------------------------------------------/',
|
|
149
|
+
`${keyNamesFor(fileName).publicKey}="${publicKey}"`,
|
|
150
|
+
'',
|
|
151
|
+
`# ${fileName}`,
|
|
152
|
+
].join('\n');
|
|
153
|
+
}
|
|
154
|
+
function closingQuote(raw, quote) {
|
|
155
|
+
for (let index = 1; index < raw.length; index++) {
|
|
156
|
+
if (raw[index] === quote && raw[index - 1] !== '\\')
|
|
157
|
+
return index;
|
|
158
|
+
}
|
|
159
|
+
return -1;
|
|
160
|
+
}
|
|
161
|
+
function decodeValue(raw, quote) {
|
|
162
|
+
if (quote === undefined)
|
|
163
|
+
return raw.split('#')[0]?.trim() ?? '';
|
|
164
|
+
const close = closingQuote(raw, quote);
|
|
165
|
+
const inner = close === -1 ? raw.slice(1) : raw.slice(1, close);
|
|
166
|
+
return quote === '"' ? inner.replaceAll('\\n', '\n').replaceAll('\\r', '\r') : inner;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* The first quoting that reads back as exactly `value`, in one entry: double quotes (newlines as
|
|
170
|
+
* `\n`), then single, then backticks. A value no quoting can carry (it holds every quote character
|
|
171
|
+
* and a line break, say) is refused rather than written in a form that could inject other keys.
|
|
172
|
+
*/
|
|
173
|
+
function encodeValue(value) {
|
|
174
|
+
const candidates = [`"${value.replaceAll('\r', '\\r').replaceAll('\n', '\\n')}"`, `'${value}'`, `\`${value}\``];
|
|
175
|
+
for (const candidate of candidates) {
|
|
176
|
+
const document = parseEnv(`K=${candidate}`);
|
|
177
|
+
if (document.entries.length === 1 && document.entries[0]?.value === value)
|
|
178
|
+
return candidate;
|
|
179
|
+
}
|
|
180
|
+
return UnrepresentableValue();
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.js","sourceRoot":"","sources":["../../src/node/codec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { EnvPrivateKey, EnvPublicKey } from './key-schemas.js';
|
|
2
|
+
/** The public key in a file's header, or `undefined` when it has none. */
|
|
3
|
+
export declare function publicKeyIn(file: string): import("@arki/failure").Failure<"env.file_read_failed", {
|
|
4
|
+
file: string;
|
|
5
|
+
}> | import("@arki/failure").Failure<"schema.async_required", undefined> | import("@arki/failure").Failure<"schema.invalid_input", {
|
|
6
|
+
issues: readonly import("@standard-schema/spec").StandardSchemaV1.Issue[];
|
|
7
|
+
}> | import("@arki/failure").Failure<"schema.validator_defect", undefined> | EnvPublicKey | undefined;
|
|
8
|
+
/** What `initEnvFile` did. */
|
|
9
|
+
export type InitResult = {
|
|
10
|
+
readonly file: string;
|
|
11
|
+
readonly publicKey: EnvPublicKey;
|
|
12
|
+
/** `false` when the file already had a header. */
|
|
13
|
+
readonly headerAdded: boolean;
|
|
14
|
+
/** Where a new private key was written; `undefined` when an existing key was reused. */
|
|
15
|
+
readonly keysWritten?: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Give `file` a dotenvx header, creating the file if needed. Reuses the private key of the same
|
|
19
|
+
* environment when one is already reachable (so every `.env.development` in a repo shares one key);
|
|
20
|
+
* otherwise generates a keypair and stores the private key in `<root>/.env.keys`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function initEnvFile(file: string, options: {
|
|
23
|
+
readonly root: string;
|
|
24
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
25
|
+
}): import("@arki/failure").Failure<"env.file_read_failed", {
|
|
26
|
+
file: string;
|
|
27
|
+
}> | import("@arki/failure").Failure<"env.file_write_failed", {
|
|
28
|
+
file: string;
|
|
29
|
+
}> | import("@arki/failure").Failure<"env.key_generation_failed", undefined> | import("@arki/failure").Failure<"env.unrepresentable_value", undefined> | import("@arki/failure").Failure<"schema.async_required", undefined> | import("@arki/failure").Failure<"schema.invalid_input", {
|
|
30
|
+
issues: readonly import("@standard-schema/spec").StandardSchemaV1.Issue[];
|
|
31
|
+
}> | import("@arki/failure").Failure<"schema.validator_defect", undefined> | {
|
|
32
|
+
file: string;
|
|
33
|
+
publicKey: EnvPublicKey;
|
|
34
|
+
headerAdded: boolean;
|
|
35
|
+
keysWritten?: undefined;
|
|
36
|
+
} | {
|
|
37
|
+
file: string;
|
|
38
|
+
publicKey: EnvPublicKey;
|
|
39
|
+
headerAdded: boolean;
|
|
40
|
+
keysWritten: string;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Set `key` in `file`, encrypted to the file's public key unless `plain`. Returns `false` when the
|
|
44
|
+
* file already held exactly this plaintext value (an encrypted value is always rewritten, because
|
|
45
|
+
* the same plaintext never encrypts to the same bytes).
|
|
46
|
+
*/
|
|
47
|
+
export declare function setEnvFileValue(file: string, key: string, value: string, options: {
|
|
48
|
+
readonly plain: boolean;
|
|
49
|
+
}): boolean | import("@arki/failure").Failure<"env.encryption_failed", undefined> | import("@arki/failure").Failure<"env.file_read_failed", {
|
|
50
|
+
file: string;
|
|
51
|
+
}> | import("@arki/failure").Failure<"env.file_write_failed", {
|
|
52
|
+
file: string;
|
|
53
|
+
}> | import("@arki/failure").Failure<"env.missing_public_key", {
|
|
54
|
+
file: string;
|
|
55
|
+
}> | import("@arki/failure").Failure<"env.unrepresentable_value", undefined> | import("@arki/failure").Failure<"schema.async_required", undefined> | import("@arki/failure").Failure<"schema.invalid_input", {
|
|
56
|
+
issues: readonly import("@standard-schema/spec").StandardSchemaV1.Issue[];
|
|
57
|
+
}> | import("@arki/failure").Failure<"schema.validator_defect", undefined>;
|
|
58
|
+
/** Encrypt plaintext values in place: the listed keys, or every value. Returns the keys encrypted. */
|
|
59
|
+
export declare function encryptEnvFile(file: string, keys?: readonly string[]): string[] | import("@arki/failure").Failure<"env.encryption_failed", undefined> | import("@arki/failure").Failure<"env.file_read_failed", {
|
|
60
|
+
file: string;
|
|
61
|
+
}> | import("@arki/failure").Failure<"env.file_write_failed", {
|
|
62
|
+
file: string;
|
|
63
|
+
}> | import("@arki/failure").Failure<"env.missing_public_key", {
|
|
64
|
+
file: string;
|
|
65
|
+
}> | import("@arki/failure").Failure<"env.unrepresentable_value", undefined> | import("@arki/failure").Failure<"schema.async_required", undefined> | import("@arki/failure").Failure<"schema.invalid_input", {
|
|
66
|
+
issues: readonly import("@standard-schema/spec").StandardSchemaV1.Issue[];
|
|
67
|
+
}> | import("@arki/failure").Failure<"schema.validator_defect", undefined>;
|
|
68
|
+
/** Decrypt values in place with `privateKeys`: the listed keys, or every value. Returns the keys decrypted. */
|
|
69
|
+
export declare function decryptEnvFile(file: string, privateKeys: readonly EnvPrivateKey[], keys?: readonly string[]): string[] | import("@arki/failure").Failure<"env.decryption_failed", undefined> | import("@arki/failure").Failure<"env.file_read_failed", {
|
|
70
|
+
file: string;
|
|
71
|
+
}> | import("@arki/failure").Failure<"env.file_write_failed", {
|
|
72
|
+
file: string;
|
|
73
|
+
}> | import("@arki/failure").Failure<"env.unrepresentable_value", undefined>;
|
|
74
|
+
/** What `rotateEnvFiles` did. */
|
|
75
|
+
export type RotateResult = {
|
|
76
|
+
readonly privateKeyName: string;
|
|
77
|
+
readonly files: readonly {
|
|
78
|
+
readonly file: string;
|
|
79
|
+
readonly values: number;
|
|
80
|
+
}[];
|
|
81
|
+
/** Where the new private key was written. */
|
|
82
|
+
readonly keysFile: string;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Re-encrypt every file of one environment to a new keypair, in one step: decrypt everything first
|
|
86
|
+
* (so a wrong key changes nothing), then write each file, then the new private key. All `files` must
|
|
87
|
+
* share a file name (`.env.production`), because they share a key name.
|
|
88
|
+
*/
|
|
89
|
+
export declare function rotateEnvFiles(files: readonly string[], options: {
|
|
90
|
+
readonly root: string;
|
|
91
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
92
|
+
}): import("@arki/failure").Failure<"env.decryption_failed", undefined> | import("@arki/failure").Failure<"env.encryption_failed", undefined> | import("@arki/failure").Failure<"env.file_read_failed", {
|
|
93
|
+
file: string;
|
|
94
|
+
}> | import("@arki/failure").Failure<"env.file_write_failed", {
|
|
95
|
+
file: string;
|
|
96
|
+
}> | import("@arki/failure").Failure<"env.key_generation_failed", undefined> | import("@arki/failure").Failure<"env.missing_private_key", {
|
|
97
|
+
file: string;
|
|
98
|
+
}> | import("@arki/failure").Failure<"env.rotation_incomplete", {
|
|
99
|
+
keysFile: string;
|
|
100
|
+
completedFiles: number;
|
|
101
|
+
totalFiles: number;
|
|
102
|
+
}> | import("@arki/failure").Failure<"env.unrepresentable_value", undefined> | import("@arki/failure").Failure<"schema.async_required", undefined> | import("@arki/failure").Failure<"schema.invalid_input", {
|
|
103
|
+
issues: readonly import("@standard-schema/spec").StandardSchemaV1.Issue[];
|
|
104
|
+
}> | import("@arki/failure").Failure<"schema.validator_defect", undefined> | {
|
|
105
|
+
privateKeyName: string;
|
|
106
|
+
files: {
|
|
107
|
+
file: string;
|
|
108
|
+
values: number;
|
|
109
|
+
}[];
|
|
110
|
+
keysFile: string;
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=edit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../src/node/edit.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA2BpE,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM;;;;sGAOvC;AAED,8BAA8B;AAC9B,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,kDAAkD;IAClD,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,wFAAwF;IACxF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;CAAE;;;;;;;;;;;;;;;;EA8BhG;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE;;;;;;;;2EAoB7G;AAED,sGAAsG;AACtG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE;;;;;;;;2EAyBpE;AAED,+GAA+G;AAC/G,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,aAAa,EAAE,EAAE,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE;;;;6EAqB3G;AAED,iCAAiC;AACjC,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9E,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,OAAO,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;CAAE;;;;;;;;;;;;;;;;;;;EAiFhG"}
|